Environment: VC++ 6.0, Win2K, NT 4.0 SP6 and Win98
I just started using the WTL a couple of days a go and I was quite impressed. Don't get me wrong, I'm not about to chuck MFC out the window just yet... it did cross my mind though :).
Anyway I needed a toolbar with drop down arrows so that I could place some pop-up menus on them. Initially I tried handling the notification messages from within the parent window. This became a huge mess and was just plain ugly looking. So I decided instead to create my own custom toolbar control. The processes is quite simple so I'll get right to the code:
1. We needed to retrieve the notification messages from the parent window. Now, we don't just want to go and reflect all of the notification messages since we don't want to have to handle all of them. So we create a contained window, with which we can selectively chose which notification messages were going to handle.
CContainedWindow m_wndParent; // Subclass parent window CWindow wndParent = GetParent(); CWindow wndTopLevelParent = wndParent.GetTopLevelParent(); m_wndParent.SubclassWindow(wndTopLevelParent);
2 We've now got a copy of the parent window and are receveing all of its messages. Next we need to define in the message map which messages were going to process.
BEGIN_MSG_MAP(CToolBarCtrlExImpl) //... ALT_MSG_MAP(1) // Parent window messages NOTIFY_CODE_HANDLER(TBN_DROPDOWN, OnParentDropDown) NOTIFY_CODE_HANDLER(TBN_HOTITEMCHANGE, OnParentHotItemChange) END_MSG_MAP()
Notice that were using ALT_MSG_MAP(1). ATL suports multiple message maps, so all messages that belong to the parent window are now been pumped through here. By the way, the parent window still recieves all these messages, we're just taking a peek at them and using the ones we need.
The folowing code is the current message handlers in the toolbar class:
LRESULT OnParentDropDown(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { // Check if this is from us if(pnmh->hwndFrom != m_hWnd) { bHandled = FALSE; return 1; } LPNMTOOLBAR lpnmtb = (LPNMTOOLBAR)pnmh; _DropDownButton* pb = FindDropDownButton(lpnmtb->iItem); if (pb && pb->uIdMenu) { CMenu menu; CMenuHandle popupMenu; menu.LoadMenu(pb->uIdMenu); popupMenu = menu.GetSubMenu(0); RECT rc; GetRect(lpnmtb->iItem, &rc); ClientToScreen(&rc); popupMenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL, rc.left, rc.bottom, m_hWnd, &rc); return TBDDRET_DEFAULT; } return TBDDRET_NODEFAULT; } // Do not hot track if application in background LRESULT OnParentHotItemChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { // Check if this is from us if(pnmh->hwndFrom != m_hWnd) { bHandled = FALSE; return 0; } LPNMTBHOTITEM lpnmhi = (LPNMTBHOTITEM)pnmh; DWORD dwProcessID; ::GetWindowThreadProcessId(::GetActiveWindow(), &dwProcessID); if((!m_wndParent.IsWindowEnabled() || ::GetCurrentProcessId() != dwProcessID)) return 1; else { bHandled = FALSE; return 0; } }
Thanks to Paul DiLascia for the code to add drop-down menus to toolbar buttons. (1997 MSJ)
That's it!, a future enhancement will be to have bitmaps on the drop-down menus