Login


Advanced Message Map Techniques

By Jonathan Wood on 11/30/2010 (Updated on 12/4/2010)
Language: C++
Technology: MFC
Platform: Windows
License: CPOL
Views: 13,064
Frameworks & Libraries » MFC » Message Maps » Advanced Message Map Techniques

Introduction

Experienced MFC developers will already be familiar with message maps. A message map uses macros to create a table of message or command IDs and the methods that will handle those messages in a particular class. When a command or message is routed to an instance of that class, MFC scans the table for an entry that corresponds to the current message. If the entry is found, the corresponding method is called.

Listing 1. A Message Map

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
    ON_COMMAND(ID_MYCMD, OnMyCommand)
    // Additional entries can go here
END_MESSAGE_MAP( )

When you add a C++ event handler in Visual Studio, the Wizards automatically add entries to the message map. So, normally, you don't need to think much about them. The two types of entries that the Wizards will add automatically are regular message and command handlers, and update handlers, which are used to update the state of a command's button or menu.

However, there are some advanced message map techniques that are not directly supported by the Wizards. These techniques require you to edit the message map manually.

Command Range Handlers

The first technique I'll discuss is using command range handlers. Command range handlers allow you to specify a single method that will handle a range of command IDs. For example, you may have a number of commands for setting a color used by your application. In this case, it may be easier to write one method that processes all of these commands rather than writing a separate handler for each color. The response to each command will be very similar.

To accomplish this, you can use the ON_COMMAND_RANGE macro. The arguments to this macro are the starting command ID, the ending command ID, and the method handler. The method will be called for each command in the specified range.

Listing 2. Command Range Handler

// In class header
afx_msg void OnColorCommand(UINT nID);

// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
  ON_COMMAND_RANGE(ID_FIRST, ID_LAST, OnColorCommand)
END_MESSAGE_MAP()

// In class implementation
void CMyDoc::OnColorCommand(UINT nID)
{
    switch (nID)
    {
    case ID_FIRST:
        // Handle this command
        break;
    case ID_LAST:
        // Handle this command
        break;
    // Handle any additional commands here
    }
}

In addition, you can use the ON_UPDATE_COMMAND_UI_RANGE macro to specify a range update handler. This allows you to control the command state from a single update handler. This macro takes the same arguments as ON_COMMAND_RANGE and the handler method takes an additional argument, which is a pointer to a CCmdUI object. You can use this argument to control the state of the corresponding menu/button command.

User Message Handlers

In some cases, it may be necessary to define your own message and then add a handler for that message. This can be accomplished using the ON_MESSAGE macro.

Listing 3. User Message Handler

// In class header
#define WM_CUSTOMMESSAGE (WM_USER + 100)
afx_msg LRESULT OnCustomMessage(WPARAM wParam, LPARAM lParam);

// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
    ON_MESSAGE(WM_CUSTOMMESSAGE, OnCustomMessage)
END_MESSAGE_MAP()

// In class implementation
LRESULT CMyDoc::OnCustomMessage(WPARAM wParam, LPARAM lParam)
{
    return 0;
}

Registered Message Handlers

Sometimes you need to handle a user message, but you don't know the value of that message until run time. For example, you may be using RegisterWindowMessage() to obtain the message value to ensure different instances of your application will be using the same value.

This can be accomplished using the ON_REGISTERED_MESSAGE macro. Instead of taking an explicit command ID, this macro references an integer variable that will hold the ID value at run time.

Listing 4. Registered Message Handler

// In class header
UINT m_nRegMsg;
afx_msg LRESULT OnRegisteredMessage(WPARAM wParam, LPARAM lParam);

// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
    ON_REGISTERED_MESSAGE(m_nRegMsg, OnRegisteredMessage)
END_MESSAGE_MAP()

// In class implementation
UINT CMyDoc::m_nRegMsg = RegisterWindowMessage(_T("MyRegMsg"));
LRESULT CMyDoc::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
    return 0;
}

Conclusion

MFC defines a few additional message map macros that I have not covered here. However, you probably won't run into many cases where you need them. As I mentioned before, most of the time the Wizards will add these entries automatically. But understanding the techniques I've described above will add some helpful techniques to your bag of tricks.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.