Some Mix-in classes that give your ATL/WTL program a better appearance
2.69/5 (5 votes)
Apr 2, 2004
6 min read
54841
1042
This article discuss some Mix-In classes for WTL. These Mix-In classes provide some features that WTL/ATL framework does not support, such as owner-draw menu, skin dialog's button and static text control.

Contents
Introduction
I have been a MFC programmer for a long time, it is a very nice framework. But MFC also has some disadvantages especially on the plan and development for little memory-resident programs. For that kind of programs, we need the program has less size, occupies less resource and is without resource leak. Obviously, that's not the advantage of MFC, so I began to study WTL. Not like MFC, WTL has no built-in support for Owner-Drawn Menu. I had wrote the CMenuHelp class for the plain WTL window to skin the window's popup menu. CWzButtonImpl is a simple owner-drawn Button class, it co-operates with CButtonHelp which uses CWzButtonImpl to subclass all buttons in a dialog. CButtonHelp can also cooperate with other Owner-Drawn Button classes. CCtrlColor processes WM_CTLCOLORDLG and WM_CTLCOLORSTATIC messages of dialog window, uses the template parameter to change the background color of dialog and controls' text color.
Since I'm not an expert on WTL/ATL framework, I can't ensure the code can work for everyone. Any advice are welcomed. You may notice that this article has many syntax errors or is even total wrong sometimes, that's because I am not an English speaker. I hope everyone can understand this article.
CMenuHelp
As we know that when a menu pops up, Windows system sends the WM_INITMENU and WM_ENTERMENULOOP messages to the owner window of this menu, and when it disappears, Windows system sends the WM_EXITMENULOOP message to the owner window. If the menu items have MFT_OWNERDRAW attribute, Windows will also send WM_MEASUREITEM to measure the menu items and send WM_DRAWITEM message to let the owner window draw the menu items.
CMenuHelp is a template class, it is also a MixIn class. It handles the 5 messages of the owner window. In WM_INITMENU message handle, it gets the handle of the active menu (the menu is not shown out yet) and calls AttachActiveMenu() to add MFT_OWNERDRAW attribute to all menu items in this menu. When the menu disappears, DetachActiveMenu() will be invoked, this method removes the MFT_OWNERDRAW attribute that is added by AttachActiveMenu().
Properties of CMenuHelp
HMENU CMenuHelp::m_hActiveMenuStores the handle of the menu that pops-up,
OnMeasureItemandOnDrawItemuses this handle to take some operation on the pop-up menu.COLORREF CMenuHelp::m_crTextColor be used to draw the normal menu item text.
COLORREF CMenuHelp::m_crHiTextColor be used to draw the selected menu item text.
COLORREF CMenuHelp::m_crBkGndColor be used to draw the background of menu item.
COLORREF CMenuHelp::m_crHiBkGndColor be used to draw the background of selected menu item.
Methods of CMenuHelp
HMENU CMenuHelp::AttachActiveMenu(HMENU hActiveMenu)Attach the menu handle which will pop-up to
m_hActiveMenu.hActiveMenuis the handle of the new menu.AttachActiveMenu()addsMFT_OWNERDRAWattribute to all menu items in this menu, so the system will sendWM_MEASUREITEMandWM_DRAWITEMmessages to our window.CMenuHelphandles the two messages to draw the menu.AttachActiveMenu()returns the last popped up menu's handle.HMENU CMenuHelp::DetachActiveMenu()Remove
MFT_OWNERDRAWattribute from all menu items ofm_hActiveMenuand setm_hActiveMenutoNULL. It returns the handle of the current pop-up menu.
Use CMenuHelp in your code
It's easy to use this class in your code. Include MenuHelp.h to your project, add CMenuHelp to the derivation chain of your window, like this:
class CMainDlg : public CDialogImpl<CMainDlg>,public CMenuHelp<CMainDlg>
and lastly, add declaration information to message chain:
BEGIN_MSG_MAP(CMainDlg)
...
CHAIN_MSG_MAP(CMenuHelp<CMainDlg>)
...
END_MSG_MAP()
When it works, all popup menu of this window will be drawn by our CMenuHelp instead of Windows normal menu appearance.
CButtonHelp
CButtonHelp is a helper class to change the appearance of button controls in a dialog. It modifies and adds BS_OWNERDRAW property to all pushbuttons when a dialog initializes itself, link these button controls to CWzButtonImpl class. CWzButtonImpl is the real Owner-Draw button class. CWzButtonImpl uses SubclassWindow to take over the measure and draw behaviors of standard Windows buttons, like most other MFC's CButton derived class.
CWzButtonImpl
CWzButtonImpl is a Owner-Draw button class derived from CButton (ATL class). It is easy to use like most MFC's Owner-Draw button classes. Declare a CWzButtonImpl object first and then call SubclassWindow method to attach it to a pushbutton control. You can modify the drawing code in this class to change the appearance of the button control if you don't like the current style. You can also replace this class by other Owner-Draw button classes. You can find many just in CodeProject. You can also specify several different Owner-Draw button classes (style) in your apps, but can only use one class in a dialog.
Properties of CButtonHelp
CSimpleArray<t_ButtonClass *> CButtonHelp::m_arButtonsStores all
t_ButtonClassobjects in the dialog, and everyt_ButtonClassobject attaches to a pushbutton.t_ButtonClassis a template parameter, it should beCWzButtonImplin our example code.
Methods of CButtonHelp
BOOL CButtonHelp::SubclassAllButtons()Attach pushbuttons in dialog to
t_ButtonClassclass objects. It is usually called in the initial function of a window, such asCDialog::OnInitDialog.BOOL CButtonHelp::SubclassButton(HWND hBtnWnd)Creates a
t_ButtonClassobject and attaches it to a normal pushbutton control.hBtnWndis theHWNDof the button control.BOOL CButtonHelp::UnSubclassButton(HWND hBtnWnd)Detach a
t_ButtonClassobject from pushbutton control.hBtnWndis theHWNDof the button control. Thet_ButtonClassobject will be deleted fromCMenuHelp::m_arButtons.int CButtonHelp::FindButtons(HWND hBtnWnd)To make sure whether a pushbutton control had been sub-classed already.
Use CButtonHelp in your code
Include ButtonHelp.h to your project first, and then add CButtonHelp class to the derivation chain of the dialog, like this:
class CMainDlg : public CDialogImpl<CMainDlg>, public CButtonHelp<CMainDlg,CWzButtonImpl>
Finally, don't forget to call SubclassAllButtons() in CDialog::OnInitDialog.
CCtrlColor
CCtrlColor class handles some of WM_CTLCOLOR* messages (currently supports only WM_CTLCOLORDLG and WM_CTLCOLORSTATIC) and draws the controls in dialog using special text color and background color (specified by template parameter). It is a quite simple class, just 34 lines C++ code.
Properties of CCtrlColor
HBRUSH CCtrlColor::m_brBkgndHBRUSHGDI object:CCtrlColorclass uses this brush to paint the background of dialog and controls.CCtrlColorcreates this object in construct function, uses color specified by template parametert_crBkgnd, and deletes this object in destruct function.
Use CCtrlColor in your code
CCtrlColor has three template parameters: T, t_crTextColor and t_crBkgnd. The last two are optional, they have default values as RGB(64,64,255) and RGB(222,222,222). So you can add CCtrlColor to the derivation chain of your dialog in two methods:
class CMainDlg : public CDialogImpl<CMainDlg>, public CCtrlColor<CMainDlg>
or
class CAboutDlg : public CDialogImpl<CAboutDlg>, public CCtrlColor<CAboutDlg,RGB(64,128,64)>
The same as CMenuHelp, adding declaration information to message chain:
BEGIN_MSG_MAP(CMainDlg)
...
CHAIN_MSG_MAP(CCtrlColor<CMainDlg>)
...
END_MSG_MAP()
About the demo code
All source code are organized in a VC6 project, it is not tested with VC7 compiler. Since this article is talking about ATL/WTL, you should also install WTL for compiling the source code. There is no limit on using this code, it can be used for any purpose.
