The Window Classes--CWnd and Its Derived Classes

You will normally override the InitInstance member function of the CWinApp class to create your application's main window, an object of a class derived from CWnd. This window class, together with all the other window classes, have member functions for receiving and sending messages.

There are different types of Windows messages. Each type is handled somewhat differently by the Microsoft Foundation classes.

Notification Messages and the Message Map

A “notification message” is a message sent to a window by Windows itself in response to a keystroke, mouse click, window move, control window activity, or other event. If necessary, your application can force Windows to send a notification message. The Microsoft Foundation Class Library has a special mechanism, called a message map,” that links Windows notification messages with the member functions you have written.

A message map is a table that you include with your window class code. It contains an entry for each Windows notification message that you intend to process with a custom-written member function. The result is a message-processing system that provides all the advantages of virtual functions without the storage overhead.

Many of the member functions that process notification messages are predefined. For example, if your class needs to process the Windows WM_CREATE message, you must put the following entry in the class's message map:

ON_WM_CREATE()

and you must declare and implement this exact member function:

afx_msg int OnCreate( LPCREATESTRUCT lpcs );

Note:

The afx_msg keyword denotes a notification message declaration or implementation. It is defined as a no-operation in AFXWIN.H and thus documents the fact that the function behaves like a CWnd virtual function. It must be emphasized that message maps depend solely on standard preprocessor macros and not on any extensions to the C++ language.

A table that shows all permitted message-map entries and the corresponding member function prototypes is presented in Chapter 6, “Message Map Cross-Reference.” For a complete example of message-map usage, see Chapter 6 of the Class Libraries User's Guide.

Other notification messages allow you to define your own functions. For example, if you need to call a function in response to a mouse click on a button whose ID number is IDD_BUTN1, your message-map entry is:

ON_BN_CLICKED( IDD_BUTN1, OnTopButton )

and your member function declaration looks like this:

afx_msg void OnTopButton();

When you define a message map, you specify the base class in addition to the messages. This allows the base class to handle messages not handled in the derived classes.

Windows Control Messages

A Windows control, such as an edit window or list box, is represented by a window object (of a class derived from CWnd), but the processing is controlled by Windows rather than by the Microsoft Foundation classes. If you need to update a control, your application must send a “control message” to Windows. A frame window object, for example, can send an LB_ADDSTRING message to a list-box window that is its child. The Microsoft Foundation classes wrap this message in the CListBox member function AddString. The call looks like this:

CListBox* listBox1; // Object initialized elsewhere

listBox1->AddString( "list-box line item" );

In this case the phrase list-box line item is sent directly to Windows for display. The CListBox member functions cannot access the list-box line items unless they retrieve them directly from Windows.

Other Windows Messages

Other Windows messages do not relate to controls. If, for example, you want to select a font for future text drawing, you must send the WM_SETFONT message to the appropriate window. The Microsoft Foundation classes wrap messages like this with CWnd member functions. In this example, you call the SetFont member function.

C++ Window Objects and the Windows They Represent

A C++ window object is distinct from its corresponding Windows window, but the two are tightly linked. A good understanding of this relationship is crucial for effective Microsoft Foundation class programming.

The window object is an instance of the C++ CWnd class (or a derived class). It comes and goes in response to your program's constructor and destructor calls. The Windows window, on the other hand, is an internal Windows data structure that corresponds to a visible (or invisible) window. A Windows window is identified by a “window handle” (HWND) and is created when the CWnd object is created, but the window may be destroyed by either a program call or by a user's action.

All the window classes provided by the Microsoft Foundation Class Library employ “two-phase construction”. The C++ constructor makes an object but does not create a corresponding Windows window. The Create member function makes the Windows window (usually by calling the native Windows CreateWindow function) and stores its HWND value in the C++ object's public data member m_hWnd.

If you derive a window class that will, in turn, be used for derivation, you must use two-phase construction. Suppose, instead, that your higher-level constructor called Create. In that case, the lower-level constructor creates the Windows window before the object (and its message-map linkage) is completely constructed. The ensuing WM_CREATE and WM_PAINT messages are then handled by the higher-level class's message-handling functions rather than by those of the lower-level class.

The CWnd virtual destructor destroys the Windows window. If you need to circumvent the object-HWND relationship, the Microsoft Foundation Class Library provides several options. The function DestroyWindow, one of the few public virtual member functions in CWnd, destroys the Windows window without destroying the object. Another CWnd member function, Detach, prevents the destructor from destroying the Windows window.

You must be careful, particularly with child windows, to destroy the C++ window object when the user closes a Windows window. If you do not destroy these objects, you will not recover their memory. Main window object destruction is not so critical because the program generally terminates immediately after the main window is closed.

Some messages are not wrapped by individual member functions. Suppose you derive a class from CWnd and you need a member function that activates the window's nonclient area. The following call accomplishes the task:

SendMessage( WM_NCACTIVATE, TRUE, 0 );

SendMessage is a CWnd member function, and thus it communicates directly with your C++ window object that underlies the Windows window. The SendMessage function sends its message immediately. Another function, PostMessage, posts the message to the Windows message queue for delayed processing.

Direct Calls to Windows

In many cases your program interacts with Windows through a direct call rather than through a sent message. Many of these direct calls are mapped to CWnd member functions. If you need to set a window's caption, for example, you simply call CWnd::SetWindowText.

Control Window Classes

The Microsoft Foundation Class Library includes classes for standard Windows controls, which are actually special-purpose windows. These controls include:

Buttons

Combo boxes

Edit windows

List boxes

Scroll bars

Static controls

You will seldom need to derive from these classes because most of their functionality is determined by Windows itself. If you need a button, for example, you construct an object and then specify one of several predefined styles to the Create member function.

Buttons, like other controls, are designed to be child windows. When the user clicks a button, for example, the button object sends a BN_CLICKED message to its parent window object. The parent window class must define a message map and have an appropriate member function to handle the message from the button.

Note:

Do not confuse the scroll-bar control window with the frame window's built-in scroll bars. Any frame window can have horizontal and vertical scroll bars if it is created with the proper parameters. The scroll bars created in a frame window this way are not actually separate child windows. A true scroll-bar control is a separate child window that can be sized and placed as required.

Dialog Boxes

A “dialog box” is a special kind of frame window that contains a number of child window controls, such as buttons and edit fields. It is generally used to collect data from the user. A “modal” dialog forces the user to complete the requested action prior to returning to the application's main window. The “modeless” dialog allows the user to continue work in the parent window.

Dialog boxes are frequently defined, along with the constituent child windows, in “resource files” where the child windows have assigned ID numbers. Your program must construct an object of class CDialog or CModalDialog (or a derived class) in order to use a resource-based dialog box. If your dialog box requires only routine operations, such as detecting button hits and reading input strings from edit controls, then you do not have to construct child control window objects; instead, you call member functions of the dialog base class that use child window IDs as arguments.

If, for example, you need to read the string from an edit window identified as IDM_DATA, then use the CWnd member function GetDlgItemText as follows:

GetDlgItemText( IDM_DATA, string, 128 );

where string is the address of a character buffer and 128 is the maximum buffer size. You do not need to reference an edit window object.

If you do need to access resource-based dialog child windows as C++ objects, the Microsoft Foundation classes provide a way. The GetDlgItem dialog class member function returns a CWnd pointer that corresponds to a dialog child window ID number that is defined by the resource. This CWnd pointer refers to an internally allocated window object that is stored in a temporary table. It allows you to use the appropriate window class member functions. The IsKindOf and GetRuntimeClass member functions of CObject can help identify the specific window class of the object.

If, for example, you need the line count from the edit control introduced previously, then use CWnd::GetDlgItemText as follows:

CEdit* pEdit = (CEdit*)GetDlgItem( IDM_DATA );

int count = pEdit->GetLineCount();