Foreword
This article shows how to easily and effectively get a call to a handler function, when a specific popup menu is opened.
The Problem
Working with MFC, from time to time, you want to get a callback from the framework when a popup menu is about to be opened. The framework indeed sends this kind of callbacks, and you can easily get it by:
ON_WM_INITMENUPOPUP()
which leads to:
OnInitMenuPopup()
But note that this handler is called for every popup menu in your current window. This is good if you want to do something generic for all menus, but what-if you need some special treatment for a specific popup-menu? Then, you need to know which is the popup menu-item that on clicking has opened this menu. The OnInitMenuPopup()
callback does not tell you who the "parent" of the opened-popup menu is; it only tells you which the popup menu that is about to be opened is. So what are we going to do?
Solution
The Design
Well, I think that the best solution for this problem is to "mark" the popup-menu item somehow, and then, when the callback is called, we will search for this mark. If we find it - we have found our popup-menu item. Otherwise - oh, well. Do nothing.
The Implementation
Let's create the handler function that will be called when our popup menu is about to be opened. Soon we will see how we make this happen; but for a start, let's define it, first. It can be a global function, or it can be a method in some class. The only thing we have to take into account is that the caller (that generates the call to our handler, CMainFrame
, for example) will have to know (by include) this class.
void OnMarkedPopupMenu(CMenu* pPopupMenu);
Wherever you initialize your GUI settings (or may I say, your controls... for example: CMainFrame::OnCreate()
), add the "mark" to the specific popup menu. In this specific example, I add a new popup menu item (in runtime) to an improvised location (this is not the main point of this article...), and give it an improvised name, "Nikon D70s" (my camera...). Then, mark this menu (we mark the popup menu itself, not the menu-item that opens it!!), using the MENUINFO
struct. This struct has the dwMenuData
member, that we can use for this purpose. This member is a DWORD
, so you can use a constant as a marker, or allocate a string and pass the pointer to it. Here I use the simple constant c_nCode
(that is declared somewhere else in my code).
CMenu* pPopupMenu = AddPopupMenu(this, 2, 4, "Nikon D70s");
MENUINFO MenuInfo;
MenuInfo.cbSize = sizeof(MENUINFO);
MenuInfo.fMask = MIM_MENUDATA;
MenuInfo.dwMenuData = c_nCode;
pPopupMenu->SetMenuInfo( &MenuInfo );
Note that "Nikon D70s" is the item, clicking on it opens the popup menu. We mark the popup menu, not the item, since in OnInitMenuPopup()
we get a pointer to the menu, not to the item.
Now, we need to implement the OnInitMenuPopup()
callback. This callback is called every time a popup menu is about to be opened. So we need to check now if this popup menu was the one that we have marked up in the initialization. If it is - call the handler function.
void CMainFrame::OnInitMenuPopup( CMenu* pPopupMenu,
UINT nIndex, BOOL bSysMenu )
{
MENUINFO MenuInfo;
MenuInfo.cbSize = sizeof(MENUINFO);
MenuInfo.fMask = MIM_MENUDATA;
MenuInfo.dwMenuData = 0;
VERIFY( pPopupMenu->GetMenuInfo( &MenuInfo ) );
if(MenuInfo.dwMenuData == c_nCode)
{
OnMarkedPopupMenu(pPopupMenu);
}
}
That's it!
Note that we can mark different popup menus with different "codes" (or strings) and then, in CMainFrame::OnInitMenuPopup
, we will have to check this and delegate to different handlers.
Last Remark - MENUINFO
One last thing before we go: you might encounter a nice obstacle, when the compiler will shout on you the error:
error C2065: 'MENUINFO' : undeclared identifier
Well, the problem is not "include
", since including windows.h is enough, and you probably has done so in your stdafx.h. So what is the problem? Well, MFC versions are the problem this time. Just put:
#define WINVER 0x500
in the top of your stdafx.h file. This will solve this problem.