Environment:Visual C++ 6, Windows Template Library (WTL)
DevStudio’s Tools|Customize menu displays a dialog box that contains a button control
with an arrow on it. When pressed, the button will display a drop-down menu. I needed this
feature in my ATL applications, but didn’t want the overhead of MFC or the headache of Win32.
So I borrowed from Norm Almond’s excellent example of
Cool Push Menu Button
and ported it to WTL, simplifying it’s functionality along the way. I’m not implying anything was
wrong with what Norm developed…the control just did more than I needed it to. I will eventually
port his entire control to WTL, when time allows.
Caveats
CButtonMenu
is derived from CButtonMenuImpl
. To draw the arrow on
the button, CButtonMenuImpl
needs to have the BS_OWNERDRAW style set. However,
this will send the WM_DRAWITEM msg to the parent of the CButtonMenuImpl
, and not the
CButtonMenuImpl
itself. By subclassing the parent HWND, the CButtonMenuImpl
can hijack the msg and handle it alone. To do this, the CButtonMenuImpl
has a private member
CContainedWindow
variable that subclasses the parent. Then, using
the ALT_MSG_MAP() macro, we can catch the msg:
class CButtonMenuImpl : ... { CContainedWindow m_Parent; public: CButtonMenuImpl(): m_Parent(this,1){} //... protected: typedef CWindowImpl<T, TBase, TWinTraits> baseClass; BEGIN_MSG_MAP(CButtonMenuImpl) //other msgs ALT_MSG_MAP(1) MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) END_MSG_MAP() //{other code} };
Finally, to make simple use of this control, CButtonMenu
provides an operator overload:
class CButtonMenu : public CButtonMenuImpl<CButtonMenu> { public: DECLARE_WND_SUPERCLASS(_T("WTL_CButtonMenu"), GetWndClassName()) //ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ CButtonMenu& operator=(HWND hWnd) { if ( m_hWnd ) DestroyWindow(); if ( ::IsWindow(hWnd) ) SubclassWindow(hWnd); return *this; } };
Here’s an example of how to initialize a CButtonMenu
with a button already on a dialog:
// Member variable of dialog CButtonMenu btnMenu; // Somewhere in WM_INITDIALOG handler btnMenu = GetDlgItem(IDC_BTN_ABOUT); btnMenu.AddMenuItem(IDC_MNU_ONE,"Windows"); btnMenu.AddMenuItem(IDC_MNU_TWO,"Template"); btnMenu.AddMenuItem(IDC_MNU_THREE,"",MF_SEPARATOR); btnMenu.AddMenuItem(IDC_MNU_FOUR,"Library");
When the user selects a menu item, a msg will be sent to the dialog.
Deficiencies
Downloads
Download demo project – 16 Kb
Download source – 3 Kb
Version History
- August 16, 2000 – Fixed the OnDraw method to use the DRAWITEMSTRUCT’s HWND instead of the Button’s HWND when calling GetWindowText().
This fixed a bug that resulted in two different CButtonMenu’s on the same dialog always painting the same caption. - July 27, 2000 – Original version posted