CS-360
Fall, 2000
Class 5
R. Eckert

CHILD WINDOW CONTROLS

Child window controls are child windows with some of the following 
characteristics:

  -An application uses them in conjunction with another window.
  -Normally they are used for simple I/O tasks.
  -Let the user choose commands, view status, view/edit text, etc.
  -Properties, appearance, and behavior are determined by predefined 
   class definitions.
  -But their behavior can be customized.
  -Allow an application to easily set up child windows for common objects:
      (buttons, scroll bars, etc.).
  -Allow the user to display and select information in standard ways.
  -The Windows Environment does most of work in:
      -painting/updating a Control's screen area.
      -determining what the user is doing.
  -So Controls can do the "dirty work" for the main window.
  -Often used as input devices for the parent window.
  -They constitute the "working components" of dialog boxes.
  -The Windows environment contains each control's WinProc() ==>
      -Messages to child windows are processed in a predefined way.
      -The programmer doesn't have to worry about it.
  -Main window communicates with controls by sending/receiving messages.
  -There are six standard control types:
      -Static Text
      -Button
      -Edit Control
      -List Box
      -Combo Box
      -Scroll Bar
  -All are windows.
  -The Windows Environment automatically repaints Control upon exposure.
  -An Example: WordPad ("File"|"Open") -- has most types of controls
  -There are 17 other predefined "Common Controls" (See Petzold, Ch. 12)

Child Window Controls are Created with CreateWindow(); The control type
is specified as a "Predefined Class Name" in one of the parameters.

Predefined Control Class names--

STATIC--Used primarily to display text, but can also display icon images
and rectangles. The advantage over painting text on the window's client area
is that the static control is automatically redrawn if covered and uncovered
by another window. Thus, we don't have to worry about processing
WM_PAINT messages to keep the client area up to date. Static controls often
act as labels for other controls.

BUTTON--Can be clicked by the user to indicate actions to be taken or
choices made. There are lots of different styles (e.g., check, radio, group).
Typically they notify the parent window when the user chooses the control.

LISTBOX--Contains lists of items that can be selected. The entire list is 
shown.

EDIT--A single or multiline control that allows the user to enter, view and 
edit text. Lots of word processing capability is built in.

COMBO BOX--Consists of either a static text box or an edit box combined
with a list box. The list box can be displayed at all times or pulled
down by the user. If the combo box contains a static text box, the text
box always displays the selection (if any) in the list box portion of the
combo box. If it uses an edit box, the user can type in the desired
selection. The list box highlights the first item (if any) that matches
what the user has entered in the edit box. The user can then select the
item highlighted in the list box to complete the choice.

SCROLLBAR--Lets the user choose the direction and distance to scroll (move)
information in a related window. There are two types:

  1. A control attached to edge of a parent window. Allows the user to
     "scroll" the information in the parent window's client area.
  2. A stand-alone child window control that allows the user to enter or 
     change an integer value by moving the scroll bar "thumb".

Creating Child Window Controls--

CreateWindow()--one of the most complicated functions in Windows since it
can create such a wide range of objects. It returns a handle (HWND) to the
window it created. It can be used to create any kind of window, including
a child window control.

Recall the parameters:

LPSTR lpClassName: Name of window class that the window being created is
based on. Can be a new class defined with RegisterClass() or one of the
predefined control classes: "STATIC", "BUTTON", "EDIT", "LISTBOX",
"COMBOBOX", "SCROLLBAR".

LPSTR lpWindowName: Name of the window--a pointer to a character string.
For windows with caption bars, this will be the caption. For BUTTON, EDIT,
STATIC classes, it is the text in the center of the control. For COMBOBOX,
LISTBOX, SCROLLBAR, it is ignored (use "").

DWORD dwStyle: Window style; a series of bit values that tell Windows the
properties needed for the window being created. Several styles can be
combined with the bitwise or operator (|).

int X: The X position of the upper left corner of the window. For a main 
window, the position is relative to the upper-left corner of the screen 
(screen coordinates); for child windows and window controls, the position is 
relative to the upper left corner of parent window's client area. It is 
measured in pixels. CW_USEDEFAULT can be used to let Windows decide where to 
put the window.

int Y: The Y position. This is used just like X.

int nWidth: The Width of window in pixels. CS_USEDEFAULT==>Windows chooses.

int nHeight: The height of window in pixels. This is used just like nWidth.

HWND hWndParent: Handle to the parent window. For the application's main 
window this is NULL; for Child windows and window controls: you must specify 
the handle of the parent window.

HMENU hMenu: For windows that have menus, this is the handle of the menu to 
be used. NULL means to use the menu defined in the class definition. In the 
case of controls, which don't have menus,  hMenu is used to hold the integer ID 
value for the control. This is very important since the ID value will be passed 
with the WM_COMMAND message that is generated when the user interacts with the 
control. This ID allows the program to identify which control was activated.

HANDLE hInstance: A handle to the instance of the program creating the
window. GetWindowLong() can be used to get this value.

LPSTR lpParam: Normally NULL, this can be used to pass data with the WM_CREATE
message sent by Windows when window is created.


Window Styles--

There are a whole bunch. They usually begin with WS_. <Check the on-line help on 
WS_*** and/or CreateWindow (end of listing)>.


STATIC CONTROLS--

  -Used to display text, icons, or rectangles.
  -We don't need to worry about repainting in response to WM_PAINT messages.
  -Often act as labels for other controls.
  -Text can be changed by sending the control a message.

Static Control Styles--There are lots, see on-line help (CreateWindow, end of
listing).


The STATIC Example Program: displays 3 static controls (text,icon,rectangle)--

When the main program's window is created, Windows sends WM_CREATE message to
the WndProc(). The program responds to this message by creating the controls 
with CreateWindow().

Creation of the static control--All use the WS_CHILD style, so the control is a
child window, attached to parent and only visible if parent is. It can only
exist inside parent's client area.

  Individual styles:

    SS_CENTER for text==>text is centered; text wraps to next line as necessary.

    SS_ICON for icon:
       Uses the same icon as the program icon (but it could be anything).
       The .ico file name is declared in the resource (.rc) file as follows:
          MYICON ICON static.ico
       So we must use "MYICON" as the 2nd parameter in CreateWindow().
       The height and width parameters are set to 0,
          ==>icon size is determined by the icon resource data.

    SS_GRAYRECT for a rectangle whose color is gray

Getting the instance handle (hInstance): Three ways--

   1. GetWindowLong(hWnd, nOffset)--retrieves a word at a specified offset
   in some extra memory that Windows maintains for the window whose handle
   is hWnd. GWL_HINSTANCE is the offset of the word containing the handle
   of the module that owns the window. So a call using this value will
   return the hInstance of the program creating the window.

   2. Use a global variable and set it equal to hInstance after creating
   the main window in WinMain().

   3. Use the fact that during a WM_CREATE message, the lParam is a pointer 
   to a structure of type CREATESTRUCT that has an hInstance member. So 
   just cast the lParam into a long pointer to that type of structure:

       CreateWindow(..., ((LPCREATESTRUCT)lParam -> hInstance,...);


Sending a Message to a Control--

In spite of the "static" name, we can change what's displayed in a STATIC
control.

This is done in two ways in STATIC.CPP using the menu items: "Change Title" 
and "Send Message".

The user selects a menu item ==> Windows sends a WM_COMMAND message with 
LOWORD(wParam) equal to menu the item ID. In the .rc file the menu items are:  
                   "&Change Title" -- ID = IDM_CHANGE
                   "&Send Message" -- ID = IDM_SEND
                   "&Quit" -- ID = IDM_QUIT
The values of these IDs are given in the .h file.

So our program can test the low word of the wParam for the menu item selected:

        case WM_COMMAND:
            switch (LOWORD(wParam))         /* menu items */
            {
                case IDM_CHANGE:    /* change static text */
                    SetWindowText (hStaticText,
                                   "New Text Via SetWindowText().") ;
                    break ;
                case IDM_SEND:      /* change static text */
                    SendMessage (hStaticText, WM_SETTEXT, 0,
                                 (LPARAM) "New Text Via SendMessage().") ;
                    break ;
                case IDM_QUIT:
                    DestroyWindow (hWnd) ;  /* destroy window, */
                    break ;     /* terminating application */
            }
            break ;

Note the use of the functions:

 SetWindowText()-- changes the test displayed in the specified window or
 control. (Remember that a static control is a window even if it doesn't
 look like one.) For a main window, it sets the window's title to the 
 specified  text. For a static control containing text, the text is changed.
 The parameters are:
               A handle to the window in which the text is to be displayed;
               A pointer to the string to be displayed.

 SendMessage()--sends a message directly to the WinProc() of a window.
                This doesn't return until the message has been processed.
 The parameters are: 
             the handle of destination window;
             the ID of the message to send;
             the wParam and lParam values containing message data, if any.

Here the message is WM_SETTEXT, sent to the static control. When this is 
received, the static control's WndProc() code causes it to change its text 
string. For this message,:
       -the wParam must be 0;
       -the lParam is a LPSTR pointing to the text string to be displayed.
But SendMessage() requires an LPARAM type--hence the cast:
                                    (LPARAM) "New text......"

We could use wsprintf() here to generate a formatted text string:
  wsprintf(cBuf, format string, values);
For example, see STATIC1.CPP which outputs a new test number each time
the "Send Message" menu item is selected:

            static char cBuf[50];
            static int nn = 0;
            ...
            wsprintf(cBuf, "This is test # %d ", nn);
            nn++;
            SendMessage(hStaticText, WM_SETTEXT, 0, (LPARAM) cBuf);

This will cause the value of nn to be displayed as part of the string. Note
that since nn is declared as static, the value is "remembered" each time the
user clicks on the menu item that causes the message to be sent to the static 
control. Therefore the value displayed increments by one each time.

A related message-sending function: PostMessage()--places (posts) a message in
the window's message queue and returns immediately. This could have been
used here.

Messages can be retrieved with GetMessage() or PeekMessage(), as we've seen.


BUTTON CONTROLS--

Button controls are little (usually) windows that can be clicked by the
user to indicate actions to be taken or choices made. There are lots of
different styles (e.g., check, radio, group) available. Typically buttons
notify the parent window when the user chooses the control.

When creating buttons with CreateWindow(), the style parameter can have any BS_
values (Check the on-line help at end of CreateWindow listing).

When a control is selected with the mouse, Windows sends a WM_COMMAND
message to control's parent window; the control's ID value is placed in the
low order word of the wParam of the message, so we  can do a switch/case on
LOWORD(wParam) to find out which button it was.

Recall that WM_COMMAND messages are also sent for menu items; the wParam is
the menu item's ID value. It's important to have distinct numbers for menu
items and controls so the WinProc() can determine where the WM_COMMAND
message came from (menu or control).

WM_COMMAND message details for buttons (and other child window controls)--

User interacts with the button (or other control) ==>

  WM_COMMAND message is sent to the parent window
     LOWORD(wParam) = buttonID (hMenu value when button was created)
     lParam = handle to the button's window
     HIWORD(wParam) = button notification code (BN_***)
                      Indicates what the user interaction was
                      (See on-line help on BN_ ).

  <The details here have changed from Win16>

Example program: BUTTON.CPP illustrates many button styles--

Two radio buttons inside a group box; a check box; two pushbuttons.

Pushbuttons (command buttons)--initiate some action when selected.

Others allow the user to make selections.

Radio buttons should not be selected simultaneously. So selecting Radio 1
should automatically deselect Radio 2 and vice-versa.

Check box--We want it to be a three-state button: on (checked), off (open),
disabled (grayed). It cycles between the three states on each click, so the 
style should be BS_AUTO3STATE.

All buttons are created in response to the WM_CREATE message with 
CreateWindow(), as in STATIC.CPP. All have the WS_CHILD style. The radio 
buttons are positioned inside a Group Box. The buttons are displayed
with ShowWindow().

The Control IDs are constants defined in BUTTON.H:

The control ID is used in each call to CreateWindow() as the hMenu
parameter. For main program windows, hMenu is used to pass a handle to the
menu definition. For window controls, hMenu used to pass the control's ID
(there's no room for a menu on a control). It must be cast to (HMENU). This
is how Windows determines the ID value of a selected control button. (The
group box can't be selected, so its ID is set to NULL.)

Processing Button Control Message in BUTTON.CPP's WndProc()--

The Boolean variables bIsChecked and bTopRadioOn keep track of the status
of the check box and the top radio button.

If "Radio1" or "Radio2" is selected, the program sends a BM_SETCHECK
message to the radio button with SendMessage(). The lParam is the check
state (TRUE or FALSE). The program logic makes the buttons mutually
exclusive. This must be done in the code if the button doesn't have an
AUTO style. Note that the check box code doesn't need to have this logic
since its style is BS_AUTO3STATE. Thus no processing of the WM_COMMAND is
necessary for this button.

"Show status" button: Windows sends program a WM_COMMAND message with
LOWORD(wParam) set to button's ID value (STATUS_BUTTON). The WinProc() 
responds by using SendMessage() to send BM_GETCHECK messages to the check 
box and RADIO1 button. The value returned indicates the status of the button
(0=unchecked/open, 1=checked/filled, 2=grayed).

The program doesn't check the other radio button, since its logic makes
sure they're in the opposite state always.

Status values are finally displayed in a small popup window using
MessageBox(). The parameters are:
        the handle of parent window of the message box,
        a LPSZ pointing to the text to be displayed in the message box,
        a LPSZ pointing to the text to appear in the message box's title,
        the style of the message box--MB_OK ==> it contains an OK push button.

The message box remains on the screen until the user presses the OK button.

wsprintf()--Windows equivalent of the sprintf() C library function. It copies
a formatted string to a character buffer. sprintf() could be used, but the
standard C code would have to be added to your Windows program by the
compiler/linker. wsprintf() is a part of Windows, so the program is smaller
using it.

We want the "Test Settings" menu item to do the same thing as the "Show
Status" button. To avoid using a GO TO, the program uses SendMessage() to
simulate the WM_COMMAND message that Windows sends when the "Show Status"
button is pressed. In other words, the program sends a message to itself to
imitate what Windows does, so the window handle is that of the main program
window. The wParam message data is STATUS_BUTTON, so when the WinProc()
receives it, it executes the same STATUS_BUTTON code that was executed when
the "Show Status" button is pressed.


LIST BOX--

A list box contains lists of items that can be selected. The entire list is 
shown. (Many styles provide scroll bars as needed.)

It is created with CreateWindow() using the "LISTBOX" window class. There
are many possible values of the dwStyle parameter (see on-line help on LBS_).

Include the LBS_NOTIFY or LBS_STANDARD style if you want the list box to send
messages to the parent window when it's activated by the user.

Using a LISTBOX is similar to using button controls. The program communicates
with the LISTBOX by sending it messages; the LISTBOX notifies the program
if the user makes a selection by sending a WM_COMMAND message.

The LISTBOX.CPP Example Program--

Creates an empty LISTBOX. Selecting the "Fill Listbox" menu item causes the 
list box to be filled with some strings. Selecting any string in the List box 
brings up a message box to display the chosen string.

WM_CREATE--
Creates an empty List box of style WS_CHILD|LBS_STANDARD, so there will be a
child window List box with standard attributes: entries sorted in ASCII
order and with a vertical scroll bar on the right side if all entries can't
be seen. The ID value (LISTBOX_ID defined in LISTBOX.H) is stored in the
hMenu variable as in BUTTON.CPP.

Filling the box--The user selects menu item "Fill Listbox" (ID value IDM_FILL
as specified in the .rc and .h files) ---> WM_COMMAND message with wParam
set to IDM_FILL.

The WndProc() responds by sending messages to the List box to empty itself 
(LB_RESETCONTENT) and to add several strings to the list using LB_ADDSTRING
message ID. Again, note the type casting of the string as in STATIC.CPP.

If the user clicks in any part of the list box, Windows sends the program a
WM_COMMAND message with the List box's ID number in LOWORD(wParam) and the
list box window handle in the lParam. If the click took place over one of
the selections in the list box, a list box notification code of LBN_SELCHANGE
is placed in HIWORD(wParam). (See on-line help; search on LBN_ for the
various list box notification codes.)

We can then send the list box an LB_GETCURSEL message to obtain the item
number (0, 1, 2,...) of the selection. Then we send a LB_GETTEXT msg to
the list box with the item number in the wParam. Windows will copy the
string into the character buffer pointed to by the lParam. This has to be
cast to an LPARAM. Finally we can format the string with wsprintf() and
display it in a message box as in the BUTTON.CPP program.


COMBO BOX--

A combo box is just like a list box, but it has an edit header control that
shows the current selection. The combo box can be set up to show only the
header, so it takes up less space. 

Styles--(see on-line help on CBS_)

The logic of the COMBO.CPP program is very similar to LISTBOX.CPP
(LB --> CB, LISTBOX --> COMBOBOX)

Note that to make the combo box's list box initially visible, as in
COMBO.CPP, use the style CBS_SIMPLE in the call to CreateWindow(); to make 
it initially hidden, use the style CBS_DROPDOWN.


SCROLLBAR--

There are two types of scroll bars:

  1. A control attached to the edge of a parent window. It allows the user 
  to "scroll" the information in the parent window's client area.

  2. A stand-alone child window control that can allow the user to
  enter an integer value by moving the scroll bar "thumb".

Both types send messages when the user interacts with the scroll bar.
The messages are: WM_HSCROLL or WM_VSCROLL for horizontal/vertical scroll 
bars. For stand-alone scrollbars, the lParam contains the window handle of 
the scroll bar control; for an attached scroll bar it is 0. The low word of
the wParam is a notification code number that describes what action the
user has taken.

Scroll bar notification codes [LOWORD(wParam)] values: SB_THUMBTRACK 
(pressed), SB_THUMBPOSITION (released), SB_LINEUP, SB_PAGEUP, SB_LINEDOWN, 
SB_PAGEDOWN.

If the notification code is SB_THUMBTRACK or SB_THUMBPOSITION, the high
word of the wParam is the current position of the scrollbar's thumb.

For a horizontal scroll bar, "up" means left and "down" means right.

Scrollbar styles when creating the scrollbar--See online help on SBS_.

The default alignment for an attached scroll bar attaches it to the right 
side and bottom of a window.

A program can also move the scroll bar's thumb, change the range of values
represented by the scroll bar, and determine the current thumb position by
using the functions:

GetScrollPos()   -Retrieve current position of thumb
GetScrollRange() -Retrieve minimum and maximum value range
SetScollPos()    -Set position of thumb
SetScrollRange   -Set minimum and maximum value range
ShowScrollBar()  -Display scroll bar, optionally attaching to window border

The SCROLL1.CPP Example (a Stand-alone Scrollbar)--

Allows the user to enter an integer value between 0 and 50 by using a stand-
alone scroll bar. The current value is continually displayed in a static
control. An informational message box shows the current value when the user
chooses the menu item "Get Value".

WM_CREATE message:

1. Use CreateWindow() to create the scroll bar.

2. Set the Upper/lower number range values--

  Use SetScrollRange(hScroll,SB_CTL,0,MAXSCROLL,FALSE);

The SB_CTL parameter is a flag indicating that the first parameter is a 
handle to a stand-alone scroll bar (not to a window). [For windows with 
h/v scroll bars, SB_HORZ and SB_VERT specify which scroll bar we want.]
MAXSCROLL is set to 50 in the .h file, and FALSE means we don't want to 
redraw the scroll bar to reflect the change.

3. We use the static int nScrollPos variable for the program to keep track of
the current position of the thumb. (This must be static so that the former 
value is remembered.) To set the initial position of the thumb to 1/2 range 
we use SetScrollPos().

4. Use ShowScrollBar() to display the scroll bar.

5. Use CreateWindow() to create a static control to hold the thumb position.

6. Use wsprintf(), SetWindowText(), and ShowWindow() to display the current
scroll position in the static control.

WM_HSCROLL message:
  This is sent if the user clicks any part of the scroll bar.
  A notification code is passed in LOWORD(wParam).
  This specifies what part of scroll bar was clicked--
    Thumb dragged==>SB_THUMPOSITION [position value in HIWORD(wParam)]
    Bottom (right) arrow clicked==>SB_LINEDOWN, so increment position by 1
    Top (left) arrow clicked==> SB_LINEUP, so decrement position by 1
    Bottom (right) area clicked==>SB_PAGEDOWN, so increment position by 10
    Top (left) area clicked==> SB_PAGEUP, so decrement position by 10
    (In each case nothing is done if the new position is outside of the range)

  For the last four cases, the program must use SetScrollPos() to
  reposition the thumb.

  Finally, the value of nScrollPos is output to the static control with
  SetWindowText().

If the user clicks on "Get Value" menu item, WM_COMMAND with wParam==IDM_GET
is sent to the program. We use GetWindowText() to get the contents of the
static control, wsprintf() to format the number, and MessageBox() to output
the result in a message box window. Here the last parameter indicates a
style of message box that has an OK button and an informational "i" icon
along side the message (MB_OK | MB_ICONINFORMATION).


Scroll Bars Attached to a Window (The SCROLL2.CPP Example)--

This creates a window with a vertical scroll bar and puts 3 lines of text in the
client area; the user can scroll through the client area using the scroll bar.

To add scroll bars to the edge of a main window, use WS_HSCROLL or WS_VSCROLL
as the style when creating the window. e.g.,

    hWnd = CreateWindow ("MyClass", "Scroll Control Example 2",
        WS_OVERLAPPEDWINDOW | WS_VSCROLL,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL) ;

The program creates this type of main window, and keeps track of the
position with nScrollPos variable as in SCROLL1.CPP. The value of nScrollPos
is used to place lines of text in the window when it's exposed. The higher
the value, the lower the position in the window the text will be painted.

When the scroll bar is moved, the client area of the window must be
repainted so the text will appear in the correct position. So somehow we need 
to convince Windows to send a WM_PAINT message. We use InvalidateRect(), which
tells Windows that the client area needs to be repainted. Windows responds
by sending a WM_PAINT msg.

InvalidateRect(HWND hwnd, const RECT FAR* lprc, BOOL fErase);
 The parameters are:
  -hwnd: window with client area that needs to be repainted.
  -lprc: address of a rectangle structure containing the coordinates of the 
   rectangle to be updated (NULL ==> entire client area).
  -fErase indicates if the background within the update area is to be erased 
   when BeginPaint() is called.

When the program gets the WM_PAINT msg, it paints the text lines at their new
position. This gives the illusion that the lines moved down. [This is the
opposite from the way scrolling normally works!]


EDIT CONTROLS (for viewing and editing text)--

These can range from a small rectangle for entering a single word or number
up to a window that occupies the parent window's whole client area. Key word
processing features are built into edit controls:

 -The current location is kept track of with a "carat"--a small vertical line
 -Backspace, Delete, and arrow keys are recognized and give expected responses
 -Blocks of text can be marked for deletion/cutting/pasting, using the clipboard
 -IF WS_HSCROLL, WS_VSCROLL styles are used, the user can scroll through the
  text; the program doesn't have to worry about repainting when scrolling occurs.

Edit Controls do NOT have the ability to format text with different fonts,
character styles, etc.

Styles of EDIT windows -- See the online help on edit styles.

The EDIT1.CPP Example -- 

This program displays multiline edit control box inside the client area. The 
user can enter/delete text in the edit control. Scrolling is built in.  Clicking
on the menu item "Get Text" causes the contents of the edit control to be 
displayed in the main window's client area.

An EDIT class child window is created with CreateWindow() in response to
the WM_CREATE message. The style is:

  WS_CHILD | ES_MULTILINE | WS_VISIBLE | ES_AUTOSCROLL | WS_BORDER
    (so it has visible border)

Note it has a null ("") title string, since we don't want it to contain any
text initially.

The program communicates with the control by sending/receiving messages.

The text in an edit control is stored as one long character string. Each
carriage return <CR> is stored as ASCII code (0x0D,0x0A). If a line wraps
because the next word won't fit on the same line, a <CR> is inserted. A NULL
character is inserted only at the end of the last line of text.

Getting the contents of an edit control--We must extract it line by line.
To do this:

1. Send the control an EM_GETLINECOUNT message to determine the number of 
   lines stored.
2. For each line, send the following messages:
    EM_GETLINE to copy the line into a buffer;
    EM_LINEINDEX to determine character position of the start of the next line;
    EM_LINELENGTH to get the length of the line;

All this is done in response to a WM_COMMAND message when the user selects
the menu item "Get Text" (IDM_GET). The extracted text is then written line
by line to the main window using TextOut() and the line number to position
it.

Note that if we resize or cover/uncover part of the text extracted and
displayed in the main window, it does not reappear, since we're not
handling WM_PAINT messages for the main window. The text in the edit window
does reappear because that handling is built into the edit control.