This article is going to show another aspect of
BEGIN_MESSAGE_MAP() provided in VC++ (MFC). A brief introduction about this: in MFC, whenever there is a need to make a button control dynamic, we often get hung up on the point of defining a map to link with the corresponding method for that button such that when that button is clicked, our method will get called. This article is going to show you a sample of code where I have created a dynamic button, which is mapped with a function as per the job selected by the radio buttons, rather than using a pre-defined map.
As an MFC developer, you may already know that whenever you have to create an application in which buttons or controls are going to be created dynamically, you need to presume the constant (
Resource_ID) for controls. You also have to specify the method corresponding to the map, like
ON_COMMAND(RESOURCE_ID, methodName) in
BEGIN_MESSAGE_MAP(). As a result, whenever the button is clicked, the corresponding method would get invoked.
However, for a more dynamic environment, we may need to find an alternative way in which we can redefine the click event when it is already specified.
As a .NET developer, you may also be aware of delegates, pointers to functions that play a significant role in defining a method for any event of a control. This article talks about a similar functionality for a non-.NET environment. For the time being, this functionality is defined for the button-click event only. This is achieved using the
ON_COMMAND() aspect of MFC with a pre-defined message map. The message map is defined and assigned values based on the requirements of the control. In future, I'll try to cover other events of MFC so that it may become more flexible for all events.
Using the code
We use a trick here: just like
BEGIN_MESSAGE_MAP(), information about ID and its corresponding method is collected and stored in a pre-defined map. The following code is used for the creation of the map containing the ID and the function pointers:
typedef void (CDynButtonDlg::*fn)(int i);
typedef std::map< UINT, fn > EventMessageMap;
Using this map type, we create a new variable. Here is the actual instance:
Whenever we need to create a button dynamically, we'll make a corresponding entry in this map:
msgMap[DYNAMIC_BUTTON_ID] = &CDynButtonDlg::Job1;
Now, our work of changing functions is simple. Whenever we wish to change the functionality of a function, we just need to do this:
msgMap[DYNAMIC_BUTTON_ID] = &CDynButtonDlg::Job2;
The only remaining question to be answered is, "How will this function be called when the button is clicked?" The answer is:
BOOL CDynButtonDlg::OnCmdMsg(UINT nID, int nCode,
void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
EventMessageMap::iterator itTrg = msgMap.find( nID );
if(itTrg != msgMap.end())
fn btnM = msgMap[nID];
return CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
I am not claiming that this is the only way to do this. Another way could be by putting conditions and call functions accordingly. I just found a new way to accomplish this task, so I am sharing it with all of you here.
I wish to make MFC events more flexible as delegates are in .NET. I'll wait for your valuable comments. I'll update this article if I could find more free time/issues.
Happy coding! :)
- 21 June 2007 -- Original version posted.
- 17 February 2010 -- Updated description.