Common Control Fundamentals
MFC provides classes to wrap the common controls just as it provides classes to wrap the core control types implemented in User.exe. The following table shows the 20 types of common controls, the WNDCLASSes on which they're based, and the corresponding MFC classes . It also shows aliases for those WNDCLASSes defined in the header file commctrl.h. Image lists and property sheets do not have WNDCLASSes because they're not controls in the strict sense of the word, but they're nearly always counted among the .. common controls because their code resides in Comctl32.dll You'll sometimes see drag list boxes shown with the common controls I did not include them here because drag list boxes are not stand-alone controls; they're conventional list boxes that are converted into "drag" list boxes by a function in Comctl32.dll. MFC provides a convenient implementation of drag list boxes in CDragListBox, so for more information, see the documentation for CDragListBox.
THE Common Controls
Control TypeWNDCLASSWNDCLASS AliasMFC ClassAnimation "SysAnimate32" ANIMATE_CLASSCAnimateCtrlComboBoxEx * "ComboBoxEx32" WC_COMBOBOXEXCComboBoxExDate-Time * "SysDateTimePick32" DATETIMEPICK_CLASSCDateTimeCtrlHeader "SysHeader32" WC_HEADERCHeaderCtrlHotkey "msctls_hotkey32" HOTKEY_CLASSCHotKeyCtrlImage listN / AN / ACImageListIP address ** "SysIPAddress32" WC_IPADDRESSCIPAddressCtrlList view "SysListView32" WC_LISTVIEWCListCtrlMonth calendar * "SysMonthCal32" MONTHCAL_CLASSCMonthCalCtrlProgress "msctls_progress32" PROGRESS_CLASSCProgressCtrlProperty sheetN / AN / ACPropertySheetRebar * "ReBarWindow32" REBARCLASSNAMECReBarCtrlRich edit "RichEdit20A" (ANSI) or "RichEdit20W" (Unicode) RICHEDIT_CLASSCRichEditCtrlSlider "msctls_trackbar32" TRACKBAR_CLASSCSliderCtrlSpin button "msctls_updown32" UPDOWN_CLASSCSpinButtonCtrlStatus bar "msctls_statusbar32" STATUSCLASSNAMECStatusBarCtrlTab "SysTabControl32" WC_TABCONTROLCTabCtrlToolbar "ToolbarWindow32 "ToolbarclassNamectoolbarctrltooltip" Tooltips_Class32 "Tooltips_ClassctooltipCtrLtree View "SystreeView32 WC_TreeViewCTreeCtrl * Requires Internet Explorer 3.0 or Later.
** Requires Internet Explorer 4.0 or Later.
As you can see from the table, some of the common controls are only supported on systems that have a particular version of Internet Explorer installed. That's because when you install Internet Explorer, the setup program silently upgrades Comctl32.dll, too. Many times in this chapter I'll say something like "This style is only supported on systems equipped with Internet Explorer 3.0 or later" or "This feature requires Internet Explorer 4.0." In truth, it's not Internet Explorer that's required but the version of Comctl32.dll that comes with that version of Internet Explorer. Because installing a more recent version of Internet Explorer is presently the only legal way to get the latest version of Comctl32.dll, Internet Explorer is a reasonable basis for documenting version dependencies.Given the common controls' MYRIAD Dependencies on The Version of Comctl32.dll That's Installed and The Fact That Some Systems Don n't have, you might wonder how to determi ne at run time whether a given feature is supported provided that you know what version of Comctl32.dll it requires. Here's a simple routine that returns Comctl32.dll's major and minor version numbers. It returns 4.0 if the Comctl32.dll installed on the host System is One That Predates Internet Explorer 3.0, And 0.0 if ComctL32.dll Isn't Installed At All:
Void getcomctLversion (DWORD & DWMAJOR, DWORD & DWMINOR)
{
DWMAJOR = dwminor = 0;
Hinstance Hlib = :: loadLibrary (_t ("comctl32.dll");
IF (HLIB! = null) {
DllgetVersionProc PDLGETVERSION =
(DllgetVersionProc) :: getProcaddress (Hlib, _T ("DllgetVersion")));
IF (pdllgetversion) {// ie 3.0 or higherdllversioninfo DVI;
:: ZeromeMory (& DVI, SIZEOF (DVI));
Dvi.cbsize = sizeof (dvi);
HRESULT HR = (* pdllgetversion) (& DVI);
IF (succeededed (hr)) {
DWMAJOR = DVI.dwmajorversion;
DWMINOR = DVI.dwminorVersion;
}
}
Else {// pre-IE 3.0
DWMAJOR = 4;
dwminor = 0;
}
:: Freelibrary (HLIB);
}
}
You Also Need A Way To Translate Internet Explorer Version Numbers INTO ComctL32.dll Version Numbers. Here's a Table That Will Help:
Internet Explorer VersioncomctL32.dll Version3.04.704.04.714.014.72
Now if i say a certin feature requirements Internet Explorer 3.0 or later and you want to determine at run time WHENER That Feature Is Supported, you can do this:
DWORD DWMAJOR, DWMINOR;
GetcomctLversion (dwmajor, dwminor);
IF ((dwmajor == 4 && dwminor> = 70) || dwmajor> 4) {
// The feature is supported.
}
Else {
// The feature is not supported.
}
Yes, It's Ugly. But it's the only option Currently Available.
CREANG A Common Control
There are two ways to create a common control without resorting to API functions The first method is to instantiate the corresponding MFC control class and call the resulting object's Create function, as demonstrated here.:
#include
CPROGRESSCTRL WNDPROGRESS;
WndProgress.create (WS_CHILD | WS_VISIBLE | WS_BORDER,
CRECT (X1, Y1, X2, Y2), this, IDC_Progress;
The header file Afxcmn.h contains the declarations for CProgressCtrl and other common control classes. The second method is to add a CONTROL statement to a dialog template. When the dialog box is created, the control is created, too. The following CONTROL statement creates A Progress Control In A Dialog Box: Control "", IDC_Progress, Progress_Class, WS_Border, 32, 32, 80, 16
When you create a common control this way, you can specify either the literal WNDCLASS name or its alias, whichever you prefer. The Visual C dialog editor writes CONTROL statements for you when you use it to add common controls to a dialog box.
Most of the common controls support their own window styles, which you can combine with WS_CHILD, WS_VISIBLE, and other standard window styles. The table below shows the "generic" common control styles that, at least in theory, are not specific to any particular control type. As an MFC programmer, you'll rarely have occasion to manipulate these styles directly because many of them apply only to toolbars and status bars, and if you use CToolBar and CStatusBar instead of the more primitive CToolBarCtrl and CStatusBarCtrl classes to implement toolbars and status bars, the appropriate CCS styles are built in. These are by no means all the styles you can use with common controls. I'll point out control-specific styles when we examine individual control types.
Common Control Styles
StyleDescriptionCCS_TOPPositions the control at the top of its parent's client area and matches the control's width to the width of its parent. Toolbars have this style by default.CCS_BOTTOMPositions the control at the bottom of its parent's client area and matches the control's width to the width of its parent. Status bars have this style by default.CCS_LEFT * Positions the control at the left end of its parent's client area.CCS_RIGHT * Positions the control at the right end of its parent's client area.CCS_VERT * Orients the control vertically rather than horizontally .CCS_NOMOVEX * Causes the control to resize and move itself vertically but not horizontally when its parent is resized.CCS_NOMOVEYCauses the control to resize and move itself horizontally but not vertically when its parent is resized. Header controls have this style by default.CCS_NORESIZEPrevents the control From resizing itself when the size of it is specified, the control assumes the width and he ight specified in the control rectangle.CCS_NOPARENTALIGNPrevents the control from sticking to the top or bottom of its parent's client area. A control with this style retains its position relative to the upper left corner of its parent's client area. If this style is combined with CCS_TOP or CCS_BOTTOM, the control assumes a default height but its width and position do not change when its parent is resized.CCS_NODIVIDEREliminates the divider drawn at the top of a toolbar control.CCS_ADJUSTABLEEnables a toolbar control's built-in customization features. Double-clicking a Toolbar of this Type Displays a Customize Toolbar Dialog Box. * Requires Internet Explorer 3.0 or Later
Once you've created a common control, you manipulate it using member functions of the corresponding control class. For controls created from dialog templates, you can use any of the techniques described in Chapter 8 to manufacture type-specific references for accessing a control's function . and data members For example, the following statement links a CProgressCtrl member variable named m_wndProgress to the progress control whose ID is IDC_PROGRESS: DDX_Control (pDX, IDC_PROGRESS, m_wndProgress);
This statement must appear in a dialog class's DoDataExchange function. Rather than add the statement manually, you can use ClassWizard if you'd like. See Chapter 8 for a description of how to use ClassWizard to bind a member variable in a dialog class to a Control in the dialog box.
When you use the common controls in an SDK-style application, you must call either :: InitCommonControls or the newer :: InitCommonControlsEx to load Comctl32.dll and register the controls' WNDCLASSes before creating the first control. In an MFC application, MFC calls these functions for you. It first tries to call :: InitCommonControlsEx. If the attempt fails because Internet Explorer 3.0 or later is not installed (Internet Explorer adds :: InitCommonControlsEx to the Win32 API), MFC falls back and calls :: InitCommonControls, Which is supported on any system running windows 95 or higher.
MFC calls :: InitCommonControls (Ex) whenever a dialog box is created or a common control class's Create function is called. If for some reason you decide to create a common control or a dialog box that contains a common control using the Windows API instead of MFC, or if you create a common control with CreateEx instead of Create, you should call :: InitCommonControls or :: InitCommonControlsEx yourself. A good place to do that is in the main window's OnCreate handler or InitInstance, although you can defer the call until just before the control or dialog box is created if you'd prefer It's not harmful to call :: InitCommonControls (Ex) multiple times during an application's lifetime.Processing Notifications:. The WM_NOTIFY Message
Unlike the classic controls, which send notifications to their parents using WM_COMMAND messages, most common controls package their notifications in WM_NOTIFY messages. A WM_NOTIFY message's wParam holds the child window ID of the control that sent the message, and lParam holds a pointer to either an NMHDR Structure OR A Structure That's A SuperSet of NmHDR. NmHDR Is Defined As Follows:
Typedef struct tagnmhdr {
HWND HWNDFROM;
Uint IDFrom;
Uint code;
NmHDR;
hwndFrom holds the control's window handle, idFrom holds the control ID (the same value passed that's in wParam), and code specifies the notification code. The following notifications are transmitted by virtually all of the common controls.
NotificationSent WhenNM_CLICKThe control is clicked with the left mouse button.NM_DBLCLKThe control is double-clicked with the left mouse button.NM_RCLICKThe control is clicked with the right mouse button.NM_RDBLCLKThe control is double-clicked with the right mouse button.NM_RETURNThe Enter key is pressed while the control has the input focus.NM_KILLFOCUSThe control loses the input focus.NM_SETFOCUSThe control gains the input focus.NM_OUTOFMEMORYAn operation on the control has failed because of insufficient memory.Systems on which Internet Explorer 3.0 or later is installed support a richer assortment of NM notifications. For example, certain control types, including some of the original common controls that are not unique to Internet Explorer but that are enhanced when Internet Explorer is installed, send NM_CUSTOMDRAW notifications so that their owners can customize their appearance. Others send NM_SETCURSOR notifications That Their Owners CAN Use to Apply Custom Cursors. The Document ATION for Individual Controls Notes The "Special" nm Notifications, if any, That the controls send.
Most common controls define additional notification codes to signify control-specific events. For example, a tree view control notifies its parent when a subtree is expanded by sending it a WM_NOTIFY message with code equal to TVN_ITEMEXPANDED. LParam points to an NM_TREEVIEW structure, which contains The Following Data MEMBERS:
Typedef struct _nm_treeview {
NMHDR HDR;
Uint Action;
TV_Item item ItemOLD;
TV_Item itemnew;
Point PTDRAG;
NM_TREEVIEW;
Notice that the structure's first member is an NMHDR structure, making NM_TREEVIEW a functional superset of NMHDR. The type of structure lParam points to depends on the type of control the notification came from. It sometimes even depends on the notification code. For instance, the LPARAM Accompanying a TV A Tree View Control Points to a TV_DISPINFO STRUCTS To A Tv_dispinfo Structure, Which Is Defined Differently Than NM_TreeView Is: Typedef Struct_TV_Dispinfo {
NMHDR HDR;
TV_Item item;
} TV_Dispinfo;
How do you know what kind of pointer to cast lParam to You start by casting to an NMHDR pointer and examining the notification code Then, if necessary, you can recast to a more specific pointer type, as demonstrated here?.:
NMHDR * PNMH = (nmHDR *) LPARAM;
Switch (pnmh-> code) {
Case TVN_ItemExpanded:
NM_TREEVIEW * PNMTV = (nm_treeview *) PNMH;
// process the notification.
Break;
Case TVn_getdispinfo:
Nm_dispinfo * pnmdi = (nm_dispinfo *) PNMH;
// process the notification.
Break;
}
IF The window That Processes these Notifications Contains Two or More Tree View Controls, IT CAN Examine The Hwndfrom Or idfrom FIELD OF THE NMHDR STRUCTURE To DETERMINE Which Control Sent The Notification.
switch statements like the one above are usually unnecessary in MFC applications, because notifications encapsulated in WM_NOTIFY messages are mapped to class member functions with ON_NOTIFY and ON_NOTIFY_RANGE macros. In addition, WM_NOTIFY notifications can be reflected to derived control classes using ON_NOTIFY_REFLECT. (MFC also supports . extended forms of these macros named ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE, and ON_NOTIFY_REFLECT_EX) The following message-map entries map TVN_ITEMEXPANDED and TVN_GETDISPINFO notifications from a tree view control whose ID is IDC_TREEVIEW to handling functions named OnItemExpanded and OnGetDispInfo: ON_NOTIFY (TVN_ITEMEXPANDED, IDC_TREEVIEW, OnItemExpanded)
ON_NOTIFY (TVN_GetDispinfo, IDC_TreeView, ONGETDISPINFO)
Casting to Specific Pointer Types Is Performed Inside The Notification Handlers:
Void CMYWINDOW :: OnItemxpanded (NmHDR * PNMH, LRESULT * PRESULT)
{
NM_TREEVIEW * PNMTV = (nm_treeview *) PNMH;
// process the notification.
}
Void CMYWINDOW :: ONGETDISPINFO (NMHDR * PNMH, LRESULT * PRESULT)
{
Nm_dispinfo * pnmdi = (nm_dispinfo *) PNMH;
// process the notification.
}
The pnmh parameter passed to an ON_NOTIFY handler is identical to the WM_NOTIFY message's lParam. The pResult parameter points to a 32-bit LRESULT variable that receives the handler's return value. Many notifications attach no meaning to the return value, in which case the handler can safely ignore pResult. But sometimes what happens after the handler returns depends on the value of * pResult. For example, you can prevent branches of a tree view control from being expanded by processing TVN_ITEMEXPANDING notifications and setting * pResult to a nonzero value. A 0 Return Value, on The Other Hand, Allows The Expansion To Occur: // in The Message Map
ON_NOTIFY (TVN_ItemXpanding, IDC_TreeView, OnItemXPanding)
Void OnItemExpanding (NmHDR * PNMH, LRESULT * PRESULT)
{
NM_TREEVIEW * PNMTV = (nm_treeview *) PNMH;
IF (...) {
* PRESULT = true; // Under Certain conditions, prevent
Return; // The Expansion from Taking Place.
}
* PRESULT = 0; // allow the expansion to proced.
}
A TVN_ITEMEXPANDING notification differs from a TVN_ITEMEXPANDED notification in that it is sent before an item in a tree view control is expanded, not after. As with the standard control types, you can ignore notifications you're not interested in and process only those that are Meaningful to your application. Windows Provides appropriate default responses for unhandled notifications.