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 program. For that kind of program, we prefer that the
program has less size, occupy less resources and is without resource leaks.
Obviously, that's not the advantage of MFC, so I begin 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
cooperates with CButtonHelp
which uses CWzButtonImpl
to subclass all buttons in a dialog. CButtonHelp
can also cooperate
with other Owner-Drawn Button class. CCtrlColor
processes
WM_CTLCOLORDLG
and WM_CTLCOLORSTATIC
messages of
dialog window, uses the template parameters to change the background color of
dialog and controls' text color.
Since I'm not the 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 even totally wrong words sometimes, that's because I am
not an English speaker. I hope everyone can understand this article.
CMenuHelp
As we known that when a menu will be popup, windows system send the
WM_INITMENU
and WM_ENTERMENULOOP
message 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 has
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 that 5 messages of the owner window; in WM_INITMENU
message
handle, it gets the handle of active menu (the menu is not show 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 was added by
AttachActiveMenu()
.
Properties of CMenuHelp
HMENU CMenuHelp::m_hActiveMenu
Store the handle of the menu that will pop-up, OnMeasureItem
and
OnDrawItem
uses this handle to take some operation on the pop-up
menu.
COLORREF CMenuHelp::m_crText
Color to be used to draw the normal menu item text.
COLORREF CMenuHelp::m_crHiText
Color to be used to draw the selected menu item text.
COLORREF CMenuHelp::m_crBkGnd
Color to be used to draw the background of menu item.
COLORREF CMenuHelp::m_crHiBkGnd
Color to 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
.
hActiveMenu
is the handle of new menu.
AttachActiveMenu()
will add MFT_OWNERDRAW
attribute to
all menu items in this menu, so the system will send WM_MEASUREITEM
and WM_DRAWITEM
messages to our window. CMenuHelp
handles the two messages to draw the menu. AttachActiveMenu()
returns the last popup menu handle.
HMENU CMenuHelp::DetachActiveMenu()
Remove MFT_OWNERDRAW
attribute from all menu items of
m_hActiveMenu
and set m_hActiveMenu
to
NULL
. It returns the handle of current pop-up menu.
Use CMenuHelp in your code
It's easy to use this
class in your code. Inlude "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 last, add declaration information to message chain:
BEGIN_MSG_MAP(CMainDlg)
...
CHAIN_MSG_MAP(CMenuHelp<CMainDlg>)
...
END_MSG_MAP()
it works, all popup menu of this window will be draw 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 initialize 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 button, like most other MFC's CButton
deriving
classes.
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 class. 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 button control
if you don't like the current style. You can also replace this class by other
Owner-Draw button class, You can find many just in CodeProject. You can also
specify several different Owner-Draw button class (style) in your apps, but can
only one class in a dialog.
Property of CButtonHelp
CSimpleArray<t_ButtonClass *> CButtonHelp::m_arButtons
Store all t_ButtonClass
objects in the dialog, and every
t_ButtonClass
object attaches to a pushbutton.
t_ButtonClass
is a template parameter, it should be
CWzButtonImpl
in our example code.
Method of CButtonHelp
BOOL CButtonHelp::SubclassAllButtons()
Attach pushbuttons in dialog to t_ButtonClass
class objects. It
usually will be a call in initial function of a window, such as
CDialog::OnInitDialog
.
BOOL CButtonHelp::SubclassButton(HWND hBtnWnd)
Create a t_ButtonClass
object and attach it to a normal
pushbutton control. hBtnWnd
is the HWND
of the button
control.
BOOL CButtonHelp::UnSubclassButton(HWND hBtnWnd)
Detach a t_ButtonClass
object from pushbutton control.
hBtnWnd
is the HWND
of the button contol. The
t_ButtonClass
object will be delete from
CMenuHelp::m_arButtons
.
int CButtonHelp::FindButtons(HWND hBtnWnd)
To make sure whether a pushbutton control had been subclassed already.
Include
ButtonHelp.h to your project firstly, and then add
CButtonHelp
class to the derivation chain of dialog,like this:
class CMainDlg : public CDialogImpl<CMainDlg>,
public CButtonHelp<CMainDlg,CWzButtonImpl>
finally, don't forget call
SubclassAllButtons()
in
CDialog::OnInitDialog
.
CCtrlColor
CCtrlColor
class handles some of WM_CTLCOLOR
*
messages (current support only for WM_CTLCOLORDLG
and
WM_CTLCOLORSTATIC
), draw 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.
HBRUSH CCtrlColor::m_brBkgnd
HBRUSH GDI object
CCtrlColor
class uses this brush to paint the background of
dialog and controls. CCtrlColor
creates this object in constructor
function; use color specified by template parameter t_crBkgnd
, and
delete 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 value 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 codes 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 use in any purposes.