CS-360
Fall, 2000
Class 8
R. Eckert

Child and Popup Windows

To date we've used a main window program and predefined window controls
(buttons, static text, etc.). This is OK if the controls have exactly the
features needed. But sometimes we need to create custom windows.

The most common type of custom window is called a child window, which is
always attached to its parent. It is only visible if its parent is visible.
It is always on top of its parent. If the parent is destroyed, the child
window also is destroyed.

Child windows are used to deal with a specific task, e.g., getting input
from the user. They can also be used as a programming tool to break up a
large screen area into smaller portions, each with its own message-
processing function.

Popup window--a type of child window that is not physically attached to
its parent; it can be positioned anywhere on screen. (It does vanish when
its parent is destroyed.) Popup windows are handy if the user needs to
move things around on the screen.

Creating a child window--

  1. Register a new window class for the child (RegisterClass()).
  2. Create the child window using CreateWindow() with WS_CHILD style.
  3. Write a separate message-processing function for the child window.

Sending messages to a child window--

We can use SendMessage() to send messages to a child window (just as we did
with Windows controls). We need to specify the child window's handle
(obtained when the child window was created) and the message ID with its
parameters. Frequently the message ID that is used is WM_USER, which is
defined in Windows.h as a number which is greater than any of the
predefined Windows messages. So you can use WM_USER to mean anything you
like. USER messages are used to communicate between windows with separate
message-processing functions. We can use WM_USER and above to define
messages for any type of activity. For example, we could have a header file
containing the following:

  #define WM_MYKILLCHILD   WM_USER    /* tell child window to vanish */
  #define WM_MYMAXCHILD    WM_USER+1  /* tell child window to maximize */
  #define WM_MYMINCHILD    WM_USER+2  /* tell child window to minimize */

Then these constants could be used in the switch (wMessage)  statement in
the child window's message-processing function.

In addition, a child window can send messages to its parent and to other
child windows.


THE CHILD EXAMPLE PROGRAM--

Clicking on the main window's "Create" menu item produces a child window
with a "Destroy Me" button and some text.  Clicking the "Send Message" main
menu item causes the caption of the child window to change. Clicking the
"Destroy Me" button in the child window causes the child window to
disappear.

Registering the child window class--

The message processing function is specified as ChildProc() -- It will
receive messages from any windows based on this class.

Class Icon -- LoadIcon() will be used to load the standard IDI_APPLICATION
icon, which means that when this child window is minimized, that icon will
appear on the bar inside the parent window.

Cursor shape -- The standard IDC_CROSS cursor is loaded with LoadCursor().

Background -- The LTGRAY_BRUSH background brush is used.

Menu -- None will be used in child windows based on this class.


Creating the child window--

Whenever the user clicks on the "Create" main menu item, the resulting
WM_COMMAND message [LOWORD(wParam)=IDM_CREATE] is processed as follows:

if(!hChild)
    hChild = CreateWindow ("ChildClass", "Child Window",
                       WS_CHILD | WS_THICKFRAME | WS_MINIMIZEBOX |
                       WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU,
                       10, 30, 200, 150, hWnd,
                       NULL, hInstance, NULL) ;

In other words, if the child window handle, hChild, is NULL (i.e., the
child window doesn't already exist), the child window is created. This
logic effectively prohibits us from having more than one of these child
windows around simultaneously.

Window styles--The window is a child window (WS_CHILD class style), is
sizeable (WS_THICKFRAME class style), is iconifiable (WS_MINIMIZEBOX),
is maximizeable (WS_MAXIMIZEBOX), has a caption and is thus moveable
(WS_CAPTION), and has a system menu button (WS_SYSMENU).

Position and size--Client area coordinates (pixels) relative to the upper
lefthand corner of the parent window are used.

hWnd--The parent window's handle; Windows stores this linkage between child
and parent==>if the parent is destroyed, Windows will destroy the child.

The child window's handle, hChild, returned by the call to CreateWindow(),
is stored in a static variable. This handle is used later to send the child
window a message.

Sending a message to the child window--

When the user clicks the "Send Message" main menu item: a WM_COMMAND
message with ID IDM_SEND is sent to the program's main window procedure
(WndProc()). Its response is:

   if (hChild)
       SendMessage (hChild, WM_USER, 0,0);

So that if the child window exists, SendMessage() is used to send a
WM_USER message to the child window. When we registered the child window's
class ("ChildClass"), ChildProc() was identified as its window procedure.
So the message ends up being transferred to ChildProc(). The response to
this WM_USER message is to call SetWindowText() in order to write the
string "Got message from parent" on the child window's caption bar.

The program's WndProc() also responds to WM_PAINT messages by displaying a
string.

It also responds to a WM_USER+1 message from the child window (child window
is being destroyed) by setting hChild to NULL. This indicates that the
child window is dead, so the user can again use the "Create" menu item to
create another child window. 

The Child Window Procedure--

ChildProc()--Since this is a window procedure, it uses the same 4 parameters
as the main window's WndProc(). ChildProc() receives all messages from the
child window. In addition to handling the WM_USER message, this ChildProc()
explicitly handles WM_CREATE, WM_COMMAND, and WM_PAINT messages. All other
messages are sent to the DefWindowProc(), which takes care of all the basic
window functionality such as allowing us to move the child window, etc.

The WM_CREATE message is sent when the child window is first created. The
response is to create a button control (style = WS_CHILD | BS_PUSHBUTTON |
WS_VISIBLE, with the text "Destroy Me". So we have a situation here of 3-
deep nesting of windows:

    Parent (main window)
         Child window
             Button Control

When the button is clicked, a WM_COMMAND is sent to the button's parent
(the child window that contains the button). The child window doesn't have
a menu or any other controls, so the button is the only source of
WM_COMMAND messages to the child window's ChildProc(). Its response to this
message is to call DestroyWindow(hChild), which kills the child window.
Prior to doing that, however, GetParent() is used to retrieve the handle to
the child window's parent so that it can then send a WM_USER+1 message to
the parent window. Note that in the main window procedure, this message is
handled by setting the child window handle to NULL. This means that the
user can once again use the "Create" menu item on the main window's menu
bar to create another child window.

Th WM_USER message is sent by the parent window's WndProc(). As indicated
above, ChildProc() responds by changing the text in its caption bar.

The processing of WM_PAINT results in getting a DC with BeginPaint() and
outputting a line of text with TextOut(). Note that this allows us to cover
the child window and expose it, without the loss of the line of text.

Notice that both parent and child process WM_PAINT messages. This means
that text is redrawn after expose events for either window.

Notice also that the message processing function for the parent and the
child are completely independent. Messages for the parent window are
processed by WndProc(), and messages for the child by ChildProc(). This
segmentation of the logic for each window into a separate function helps
make complex Windows programs manageable. Each window ends up being its own
little world. Also, this provides a certain "independence" among windows--
often you can design utility windows and their message processing functions
that can be used in many different applications without change.


Popup Windows--

Popup windows are similar to child windows, but they are not restricted to
the parent window's client area. They can appear anywhere on screen, can
cover parts of parent window and other program windows. Like child windows,
however, popup windows can never be covered by their parent window (i.e.,
the parent window is always under its popups). If the parent is minimized,
the popup automatically disappears, and it reappears when parent is
restored.

Popup windows can be handy for small utility programs, such as a window
that shows the current cursor position in a painting program. They are also
ideal for applications that have multiple independent sections, such as a
communications program which supports a number of simultaneous terminal
sessions in different popup windows.

Popup windows are created with CreateWindow(). The WS_POPUP style (which is
mutually exclusive with WS_CHILD) is used. Coordinates are interpreted
as screen coordinates, not as client-area coordinates of the parent window
(as for WS_CHILD style windows).


The Big Picture--

Child and popup window behavior with respect to the parent window mimics a
program window's behavior with respect to the Windows desktop (background
screen). This is on purpose. The windows desktop is just another window
(lacking a title bar and border). All running main program windows behave
like children of the desktop window. When a main program window is
minimized, it shrinks to an icon on the desktop just like the child window
in the CHILD example shrinks to an icon in the corner of its parent
window's client area.

When writing a program with several movable child windows, we're creating
a tiny world in our program's client area; i.e., our program's client
area behaves just like the full Windows desktop.

Using the same code for both main program windows and for child windows
keeps the Windows system small and gives the environment a consistent
behavior.