Developer Blog
Articles about Using Microsoft Developer Tools

Advanced Message Map Techniques

Tuesday, November 25, 2008 9:37 AM by jonwood

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.

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

A 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 since 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.

// 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
  // ...
  }
}

Command Range Handler

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.

// 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;
}

User Message Handler

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.

// 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;
}

Registered Message Handler

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.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   C++/MFC
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

MFC Ribbon Bar Doesn't Implement ID_CONTEXT_HELP

Tuesday, November 25, 2008 2:48 AM by jonwood

Overview

One of the predefined command IDs historically supported by MFC is ID_CONTEXT_HELP. When this command is selected, the mouse pointer changes to an arrow with a question mark and, when you click on a control or window, MFC displays help associated with the item clicked.

However, as of Visual Studio 2008 SP1, this command does not work for the ribbon bar. The command is still implemented but it does not obtain the correct value and so help fails to load.

Details

CMFCToolBar derives from CBasePane, which derives from CWnd. The handler for ID_CONTEXT_HELP ends up calling CBasePane::OnHelpHitTest() to obtain the ID of the element that was clicked. To accomplish this, CBasePane::OnHelpHitTest() calls CWnd::OnToolHitTest(), which is virtual.

Classes like CMFCToolBar override OnToolHitTest() to return the ID of the button at the specified location. But CMFCRibbonBar does not override OnToolHitTest() and so CWnd::OnToolHitTest() tries to find the child window at the specified point. Since ribbon elements are not actual windows, CWnd::OnToolHitTest() finds no child windows and returns -1. As a result, CBasePane::OnHelpHitTest() simply returns the ID of the ribbon window itself, which is useless here.

Resolution

Someone from Microsoft confirmed with me this issue and said it was not supported because it is not supported in the Microsoft Office 2007 applications.

In all fairness, the ribbon bar has a fairly sophisticated tooltip system than can be used to display brief help for any element by simply hovering the mouse over that element. This makes the ID_CONTEXT_HELP command far less important than it may have been in the past. Still, I ended up wasting a bit of time trying to determine why I couldn't get ID_CONTEXT_HELP to work with the ribbon bar. The best fix may simply be to avoid its use in MFC applications that have a ribbon bar.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   ,
Categories:   C++/MFC
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Welcome

Monday, November 24, 2008 6:25 PM by jonwood

Welcome to my new blog. This blog will be dedicated to software and Web development.

I've been a developer since 1987, focusing mostly on Microsoft platforms and developer tools. I've done a lot of writing that includes magazine columns, magazine articles, contributions to a couple of books, and various on-line and MSDN content. So this blog will serve as an outlet for much of the same kinds of topics and issues.

I suspect that this blog will be driven, in large part, by issues I have to solve in the process of my own projects. But we'll just have to see.

I'm excited to start this blog and hope it serves some value to the community. Enjoy!

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:  
Categories:   General
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed