Child window controls

PGUI: Child window controls

Below is a short introduction to the use of child window controls in Windows application programming. The details of individual control types are not covered.

Window classes

Child window controls are implemented in predefined window classes. The window classes (window procedures) are included in the system DLLs. The simple child window classes are

Class name Description MFC class
BUTTON Designates a small rectangular child window that represents a button the user can click to turn it on or off. Button controls can be used alone or in groups, and they can either be labeled or appear without text. Button controls typically change appearance when the user clicks them. This class also implements the "group box". CButton
STATIC Designates a simple text field, box, or rectangle used to label, box, or separate other controls. Static controls take no input and provide no output. CStatic
EDIT Designates a rectangular child window into which the user can type text from the keyboard. The user selects the control and gives it the keyboard focus by clicking it or moving to it by pressing the TAB key. The user can type text when the edit control displays a flashing caret; use the mouse to move the cursor, select characters to be replaced, or position the cursor for inserting characters; or use the BACKSPACE key to delete characters. CEdit
LISTBOX Designates a list of character strings. Specify this control whenever an application must present a list of names, such as file names, from which the user can choose. The user can select a string by clicking it. A selected string is highlighted, and a notification message is passed to the parent window. CListBox
COMBOBOX Designates a control consisting of a list box and a selection field similar to an edit control. When using this style, an application should either display the list box at all times or enable a drop-down list box. If the list box is visible, typing characters into the selection field highlights the first list box entry that matches the characters typed. Conversely, selecting an item in the list box displays the selected text in the selection field. CComboBox
SCROLLBAR Designates a rectangle that contains a scroll box and has direction arrows at both ends. The scroll bar sends a notification message to its parent window whenever the user clicks the control. The parent window is responsible for updating the position of the scroll box, if necessary. CScrollBar

There is also a set of more complex child window controls called the "common controls".

Creating child window controls

Child window controls are created in the similar way as top level windows in Windows API. We can use functions CreateWindow or CreateWindowEx. In creation, we need to specify window style WS_CHILD to identify the window as a child window. Also, we need to define the parent window. For example, to create a label and a list box, we write

HWND hLeftLabel, hLeftList;
...
hLeftLabel = CreateWindowEx(0, "STATIC", _T("Left elements:"),
  WS_CHILD | WS_VISIBLE | SS_LEFT,
  0, 0, 0, 0, hWnd, (HMENU) -1, hInst, NULL);
hLeftList = CreateWindowEx (WS_EX_CLIENTEDGE, _T("LISTBOX"), NULL,
  WS_CHILD | WS_VISIBLE | LBS_SORT | LBS_EXTENDEDSEL | LBS_NOTIFY | WS_VSCROLL,
  0, 0, 0, 0, hWnd,
  (HMENU) LEFT_LIST_ID, hInst, NULL);

If yout need to refer to the child windows later on, you need to store the returned handle in a variable of type HWND.

MFC makes it somewhat easier to create child window controls with the predefined MFC classes. For example, to create a list ox, we write

CListBox m_fontlist;
...
m_fontlist.CreateEx(WS_EX_CLIENTEDGE, _T("LISTBOX"), _T(""), (WS_CHILD | WS_VISIBLE | LBS_STANDARD) & ~WS_BORDER, r, this, IDC_FONTLIST);

The last paramater in both cases is the child id of the control. The control can be identified with the help of this id in message processing.

The child window controls are usually created while processing the WM_CREATE -message of the parent window.

Layout

The simplest way to lay out the components is to specify the coordinates in CreateWindow (see the four zeros above). This does not scale with the parent window. Better way is to lay out the controls while processing WM_SIZE -message of the parent. To make the layout device-independent we need to get some information of the character dimensions:

case WM_SIZE:
  // calculate average character dimensions
  HDC hdc = GetDC(hWnd);
  TEXTMETRIC tm;
  GetTextMetrics(hdc, &tm);
  cxChar = tm.tmAveCharWidth;
  cyChar = tm.tmHeight + tm.tmExternalLeading;
  ReleaseDC(hWnd, hdc);
  // get the new dimensions of the window
  width = LOWORD(lParam);
  height = HIWORD(lParam);
  // position every chils window control
  MoveWindow(hLeftLabel, cxChar, cyChar/2, width/2 - 7*cxChar, cyChar, TRUE);
  MoveWindow(hLeftList, cxChar, 2*cyChar, width/2 - 7*cxChar, height - 2*cyChar, TRUE);
  ...
  break;

In MFC this can be done in the same way, the function call differs little:

m_fontlist.MoveWindow(m_cxchar*2, m_cychar, m_cxchar*30, cy - m_cychar*2, true);

Control messages

Window messages can be used for getting and/or setting the control attributes. We can use function SendMessage for sending messages to a window. For example, the code

// get indices of selected items
int *selb = new int[n];
SendMessage(hSrc, LB_GETSELITEMS, n, (LPARAM) selb);

gets the indices of the selected items in a list referred by hSrc.

MFC provides convenience member functions that hide the message sending operation.

Child window controls notify their parent about changes and user actionsby sending messages to the parent. Usually they send WM_COMMAND -messages with a special notification code:

case WM_COMMAND:
  wmId  = LOWORD(wParam); 
  wmEvent = HIWORD(wParam); 
  // Parse the menu selections:
  if (lParam == NULL) {
    ...
  }
  // Parse control messages:
  else {
    switch (wmId) {
      case LEFT_LIST_ID:
	    if (wmEvent == LBN_SELCHANGE) {
		    long c = SendMessage(hLeftList, LB_GETSELCOUNT, 0, 0);
		    EnableWindow(hL2RBtn, c != 0);
        }
        break;
	  ...

MFC uses message maps for connecting messages to functions:

BEGIN_MESSAGE_MAP(CFontViewerPanel,CWnd )
  ...
  ON_LBN_SELCHANGE(IDC_FONTLIST, FontChanged)
  ON_CBN_SELCHANGE(IDC_SIZECOMBO, FontChanged)
END_MESSAGE_MAP()

Example programs

ComponentDemo with Win API demonstrates the use of lists and buttons with scaling layout.

FontViewer with MFC is another example of the use of child window controls.

See also:
ComponentDemo example
FontViewer example