Introduction
In some applications we can find menus which allow multiple selection. One example is the pop-up menu shown by Microsoft Visual Studio for adding or removing toolbar buttons.
We’ll try to do something similar in our own MFC application.
Finding a solution
By default, a native Windows pop-up menu is dismissed when the user selects an item. I could not find in the documentation any flag, message or notification that allows changing this behavior. So, one solution may be using a hook. On the first site, may be tempted to use a CBT hook, as shown in a previous article “AfxMessageBox with Auto-close” (see the link, below). That’s because a pop-up menu is a window itself (of pre-defined class “#32768”).
However, easier and better for our purpose is using a message filter hook. This type of hook can be installed by passing WH_MSGFILTER value in the first parameter of SetWindowsHookEx function.
Using an application-wide WH_MSGFILTER hook
Next example sets a WH_MSGFILTER hook in application’s main thread class. The hook procedure processes and filters the messages occurred as a result of menu input.
// Demo.h : main header file for the Demo application //... struct MSGFILTER_HOOK_INFO { HHOOK hHook; // hook handle HMENU hMenu; // selected menu handle WORD wMenuItemID; // selected menu item identifier }; // ... class CDemoApp : public CWinApp { // ... // Overrides public: virtual BOOL InitInstance(); virtual int ExitInstance(); // Implementation private: static MSGFILTER_HOOK_INFO m_hookInfo; static LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam); static void _HandleMenuMessage(MSG* pMSG); };
// Demo.cpp : Defines the class behaviors for the application. //... MSGFILTER_HOOK_INFO CDemoApp::m_hookInfo; // ... BOOL CDemoApp::InitInstance() { // Installs WH_MSGFILTER hook m_hookInfo.hHook = SetWindowsHookEx(WH_MSGFILTER, // hook type &CDemoApp::MessageProc, // hook procedure NULL, // module handle GetCurrentThreadId()); // current thread ID ASSERT(NULL != m_hookInfo.hHook); // ... return TRUE; } // WH_MSGFILTER hook procedure LRESULT CALLBACK CDemoApp::MessageProc(int nCode, WPARAM wParam, LPARAM lParam) { switch(nCode) { case MSGF_MENU: // message is for a menu { MSG* pMSG = (MSG*)lParam; _HandleMenuMessage(pMSG); } break; } return ::CallNextHookEx(m_hookInfo.hHook, nCode, wParam, lParam); } // handles hooked menu messages void CDemoApp::_HandleMenuMessage(MSG* pMSG) { switch(pMSG->message) { case WM_MENUSELECT: // keep in mind selected menu handle and selected item identifier m_hookInfo.wMenuItemID = LOWORD(pMSG->wParam); m_hookInfo.hMenu = (HMENU)pMSG->lParam; break; case WM_LBUTTONUP: { switch(m_hookInfo.wMenuItemID) { case ID_VIEW_TOOLBAR: case ID_VIEW_STATUS_BAR: // cases for other menu command IDs... { // toggle check item MENUITEMINFO menuItemInfo = {0}; menuItemInfo.cbSize = sizeof(MENUITEMINFO); menuItemInfo.fMask = MIIM_STATE; ::GetMenuItemInfo(m_hookInfo.hMenu, m_hookInfo.wMenuItemID, FALSE, &menuItemInfo); if(MFS_CHECKED & menuItemInfo.fState) menuItemInfo.fState &= ~MFS_CHECKED; else menuItemInfo.fState |= MFS_CHECKED; ::SetMenuItemInfo(m_hookInfo.hMenu, m_hookInfo.wMenuItemID, FALSE, &menuItemInfo); // send command message to main window CWnd* pWnd = AfxGetMainWnd(); ASSERT_VALID(pWnd); pWnd->SendMessage(WM_COMMAND, MAKEWPARAM( m_hookInfo.wMenuItemID, 0), 0); // change to WM_NULL to prevent closing menu pMSG->message = WM_NULL; } } } break; case WM_LBUTTONDBLCLK: // just set WM_NULL to get rid of all default processing pMSG->message = WM_NULL; break; } } int CDemoApp::ExitInstance() { // Uninstalls WH_MSGFILTER hook if(NULL != m_hookInfo.hHook) { ::UnhookWindowsHookEx(m_hookInfo.hHook); } // ... return CWinApp::ExitInstance(); }
Demo project
The demo project attached here is a simple SDI MFC application having the “View” menu with multiple selection.
What’s next?
This article just demonstrates how to use a WH_MSGFILTER hook in order to make a menu with multiple selection. We may notice that using an application-wide hook and some hard-coded menu item IDs is not the best choice. What about if we have tens of menus with tens of menu items?
Well, in the next article we’ll implement an MFC-extension class that can serve our initial purpose in a much more flexible and object-oriented manner.
Resources
See also
- Codexpert blog: Multiple Selection Menu – Part 2
- Codexpert blog: AfxMessageBox with Auto-close
- Codexpert forum: check-uirea mai multor butoane intr-un menu