This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

MSDN Magazine

C++ Q&A

Enabling Menus in MFC Apps, Changing the Behavior of Enter with DLGKEYS Sample App

Paul DiLascia

Code for this article: CQA0700.exe (48KB)

Q I'm trying to enable and disable the menus in my MFC app. I call CMenu::EnableMenuItem with the main menu and proper flags (MF_ENABLED), but nothing seems to happen. My menu item is always enabled. How do I disable the menu items?

Many readers

A There must be something contagious in the air because all of a sudden I keep getting questions about how to enable menu items. Perhaps a slew of new MFC programmers has recently entered the programming community.
      It would certainly seem reasonable that if there's a function called EnableMenuItem, that would be the function to call to enable or disable your menu items. And indeed it isâ€"but not in MFC apps. In an MFC app, you enable or disable menu items by using ON_ UPDATE_COMMAND_UI. Let me explain why MFC differs from the normal C and Windows® API implementation and why it's way cool.
      In general, the state of a user interface itemâ€"button, status pane, menu item, whateverâ€"is derived from your program's state. For example, if your program is in read-only mode, then commands that edit should be disabled, and perhaps there's a little indicator somewhere to remind the user. Likewise, if there's nothing in the clipboard (state), then the Paste command should be disabled (menu). So, in general, UI state is derived from program state, and changes in program state should be reflected in your menus. This is how you should think of it:

   program state � UI state 
  

If you don't think of it this way, start now. Are you thinking in terms of state? Good.
      The next problem is: how do you implement the right-pointing arrow in the previous line? That is, how do you get your user interface to reflect your program's state at all times? There are two approaches: the obsessive-neurotic way and the relaxed way.
      The neurotic way says, "Whenever something happens to change the state, don't forget to update the UI, too." If the user invokes the Read-only Mode command, the command handler disables all the edit commands as one of its final orders of business. Likewise, if the user invokes Cut or Copy, the handler immediately enables the Paste command. Anywhere in your program you do anything to change the program state, you must also remember to update the UI accordingly.
      The relaxed approach says, "Hey man, don't worry trying to maintain all that state stuff. Just update the UI when you need it." For menus, don't worry about keeping the menu state up-to-date; wait until a menu is about to be displayed. If the neurotic approach is supply-push, the relaxed approach is demand-pull:

   UI state � program state 
  

      This approach is easier and more foolproof. Most importantly, it separates data from user interface. Each object stores only its own stateâ€"for example, the document knows when it's in read-only mode. The UI can interpret the state however it likes, when it's time. You don't want your lowlevel objects calling UI functions like EnableMenu!
      MFC provides a UI- update mechanism to implement the relaxed approach. The detailed description is too long to fit here (see my article "Meandering Through the Maze of MFC Message and Command Routing" in the July 1995 issue of Microsoft Systems Journal), but the basic idea is this: when the user invokes a menu, Windows sends a WM_INITMENUPOPUP message. MFC handles it by creating a little CCmdUI object, initializing it successively for each menu item, and passing it to objects in your app. That is, MFC calls your ON_UPDATE_COMMAND_UI handler for that menu item, if there is one. So if you have a handler

   ON_UPDATE_COMMAND_UI(ID_FOO, OnUpdateFoo) 
  

then MFC will call your OnUpdateFoo function whenever the user invokes the menu containing the Foo item. You don't have to worry about all the places you might have to call ::EnableMenuItem (the neurotic approach); all you have to do is calculate the menu state from the program state. A typical handler looks like this:

  void CMainFrame::OnUpdateFoo(CCmdUI* pCmdUI) 
  
{
pCmdUI->Enable(pObj->GetFoo());
}

      Here GetFoo is assumed to be a function that gets the foo state of some objectâ€"for example, m_pDocument->GetReadOnly(). There might be 20 functions that modify the foo state (through a method SetFoo, naturally), but only one place that updates the menu item. And, of course, the condition might be more complex, such as:

  pCmdUI->Enable(m_bFoo &&
  
(GetStatusX(...) || GetStatusY(...)));

      In the case of Paste, you'd have to query the clipboard to check whether there's anything to paste andâ€"if you want to do a really good jobâ€"whether the contents are of a type your program can useâ€"for example, text as opposed to graphics. But however complex the condition is, the point is that by calculating the menu state when it's needed, menu updating code is isolated to one functionâ€"away from the underlying objectsâ€"instead of sprinkling EnableMenuItem calls throughout those objects.
      MFC uses CCmdUI and ON_UPDATE_COMMAND_UI to adjust the state of buttons and status bar panes as well as menu items, and you can extend it yourself for other kinds of UI items. For example, you might adjust the contents of a combo box or listbox based on program state when the user clicks the drop-down arrow. CCmdUI::Enable is a virtual function that, in the case of menu items, translates into ::EnableMenuItem.
      In the previous example, I've shown the UI handler implemented in CMainFrame, but you can put your handler in the frame, view, document, application (CWinApp-derived), or any other class to which commands are routed through CCmdTarget::OnCmdMsg. If MFC can't find an ON_UPDATE_COMMAND_UI for a particular menu item, it automatically enables or disables the item based on the following rule: if there's a handler (ON_COMMAND) for the command, MFC enables the menu item; if there's no handler, MFC will disable it. You can override this behavior by setting CFrameWnd::m_bAutoMenuEnable to FALSE, in which case all items will be enabledâ€"even ones that have no handlers.
      The upshot is, in an MFC app, don't call EnableMenuItem to enable or disable your menu items; you should instead implement an ON_UPDATE_COMMAND_UI handler for the item.

Q I have a simple question. In a dialog-based program, every time I hit the Enter key, my program exits. I tried unchecking BS_DEFPUSHBUTTON for the OK button, and adding an override for OnOK, but my program still quits when I hit Enter. How can I make Enter not exit the program?

TS Toh

A Well, darn that pesky dialogâ€"why won't it stay put? Alas, it can indeed be difficult and confusing to figure out what the whoozy goes on when the user presses the Enter key in your dialog. That's because this area touches on some of the oldest code in Windows. And the older the code is, the kludgier it is because the creators wrote all kinds of hardwired stuff into Windowsâ€"partly to make the code fit, and partly because they never imagined they were building a system that would last as long as it has. In any case, I'll show you how to disable the Enter key. I'll go even one better and show you how to make the Enter key do whatever you like.
      If all you want to do is disable Enter, it's really quite simple, though never in a zillion years would you guess the magic incantation. (Well, maybe in a zillion years, but not in 15 minutes.) Overriding OnOK is not a bad idea, but if you override OnOK to do nothing, then that's precisely what will happen when the user presses the OK button with the mouse: nothing. You could change the ID of the OK button to something else like ID_MY_OK, and write a handler that calls EndDialog. In fact, this would work, but it's kind of ugly.
      The next reasonable idea would be to somehow disable the "defaultness" of the OK button. This is what you tried, and it was a good idea. The problem is, unchecking BS_DEFPUSHBUTTON isn't enough. Carefully controlled and subtle experiments in Spy++ reveal that even when you uncheck the default option, pressing Enter still does an OK, sending your dialog bye-bye. Sigh.
      The problem is, you have to distinguish between OK the button and OK the Enter key. You could write an OnOK handler that calls GetCurrentMessage to get the last sent message, which should be WM_COMMAND, and check the low-order word of WPARAM to see where the command came fromâ€"but that, too, isn't pretty.
      The only way to get to the bottom of this and solve the problem correctly is to use Spy++ to figure out what's going on. When the user presses Enter, Windows sends a special DM_GETDEFID message to get the default command ID, which Windows then sends as a WM_COMMAND when the user presses Enter. So all you have to do is override WM_GETDEFID. The less-than-stellar Windows documentation describes the return value from DM_ GETDEFID as follows: "If a default push button exists, the high-order word of the return value contains the value DC_HASDEFID and the low-order word contains the control identifier. Otherwise, the return value is zero."
      Upon reading this, I assumed that if there's no default push button, then the return value should be zero. Silly me. If you want to disable the default ID, you still have to return DC_HASDEFID in the high word.

  BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
  
ON_MESSAGE(DM_GETDEFID, OnGetDefID)
���
END_MESSAGE_MAP()
LRESULT CMyDlg::OnGetDefID(WPARAM wp, LPARAM lp)
{
return MAKELONG(0,DC_HASDEFID);
}

Note that since there's no MFC macro for DM_GETDEFID, you have to use the generic ON_MESSAGE macro. Now the user can press Enter as long as she likes and nothing will happen. The problem is now solved.
      But this naturally raises the question: what if you want the Enter key to do something else? Frequently readers ask me how they can map Enter into Tab, so pressing the Enter key has the same effect as pressing Tabâ€"that is, focus moves to the next control in the dialog. This requires a bit more work, but the simplest way is to use an accelerator. Many programmers attempt to use OnChar. No, no, no! OnChar is a low-level mungy-grungy sort of thing you want to avoid any time you can. Even worse are WM_KEYDOWN, WM_ KEYUP, and the like. Who can deal with that stuff? OnChar is OK if you want to restrict the characters allowed in an edit control to, say, numeric digitsâ€"but any time you want to map a key to a command, accelerators are the way to go.
      In this instance, all you have to do is create an accelerator for VK_RETURN that maps to a command, ID_MY_ENTER, and write a command handler that does what you want.

  BEGIN_MESSAGE_MAP(CMyDlg, CDialog) 
  
ON_COMMAND(ID_MY_ENTER, OnMyEnter)
���
END_MESSAGE_MAP()
void CMyDlg::OnMyEnter()
{
NextInTabOrder();
}

Figure 1 DLGKEYS in Action

Figure 1 DLGKEYS in Action

      Figure 1 and Figure 2show a sample program, DLGKEYS, that implements this. NextInTabOrder is the function that does the actual work. It uses GetNextDlgTabItem to get the next control in the tab order (which, by the way, is the same as the z-order, in case you ever want to change it dynamically).
      If you're thinking this is totally trivial, there's one little problem I forgot to tell you about. MFC dialogs don't do accelerators. It's true. Accelerators are not an automatic thing for dialogs; you have to write some code to make them work. To understand why, let's flash back quickly to the ancient days when C++ was just a figment of Bjarne Stroustrup's imagination, when all we had was C and the raw Windows API. In those quiet days, every Windows-based program had a central loop called the message pump that looked something like this:

  while (GetMessage(...)) { 
  
TranslateMessage(...);
DispatchMessage(...);
}

      The details are not important. What's important is that messages don't just arrive at your program, you have to request them. This is an artifact of nonpreemptive multitasking, where the multi part is faked by having every app cooperate nicely. As the Redmondtonians added more and more features, someone came up with the idea of an accelerator table. This is just a table that maps keystrokes to command IDs. To make it work, they invented a function called TranslateAccelerator. Now, the message pump looks like this:

  while (GetMessage(...)) { 
  
if (TranslateAccelerator(hAccel...)) {
// handled, continue looping
} else {
TranslateMessage(...);
DispatchMessage(...);
}
}

      hAccel is a handle to the accelerator table. Again, the details are not important. The main thing is, there's something called an accelerator table and a special function that translates keystroke messages into WM_COMMAND messages using this table. TranslateAccelerator looks for a WM_KEYDOWN, WM_CHAR, WM_ KEYUP sequence where the character matches one of the keys in the table. If it finds one, it inserts a WM_COMMAND into the message queue, where the command ID is whatever the accelerator table says it's supposed to be. The net result is you don't have to worry about keystrokes or WM_CHAR; all you have to do is set up the accelerator table (as a resource) and remember to call the magic function TranslateAccelerator.
      Flash forward to the year 2000, when C++ and MFC are old hat and more programmers search HotBot for "pre-IPO" than the word "sex." Now the whole message loop thing is hidden within MFC, in the bowels of CWinThread. But not entirely. To give any window a chance to get a piece of the message pump action, MFC provides a special virtual function, PreTranslateMessage. If you were brave enough to peer at the message pump inside CWinThread, you'd see something more or less equivalent to this:

  // simplified from CWinThread 
  
while (GetMessage(...)) {
if (PreTranslateMessage(...)) {
// continue looping
} else {
TranslateMessage(...);
DispatchMessage(...);
}
}

      CWinThread::PreTranslateMessage is virtual. The default implementation calls another virtual function by the same name, CWnd::PreTranslateMessage, for your app's windows. The result is that if you ever need to do something inside the message loopâ€"like translate acceleratorsâ€"all you have to do is override your window's PreTranslateMessage. In fact, this is how CFrameWnd handles accelerators.

  BOOL CFrameWnd::PreTranslateMessage(MSG* pMsg) 
  
{
���
if (pMsg->message >= WM_KEYFIRST &&
pMsg->message <= WM_KEYLAST)
{
::TranslateAccelerator(m_hAccelTable,...);
}
}

      Where does CFrameWnd get the accelerator table? When you load your frame, CFrameWnd::LoadFrame looks for an accelerator table with the same ID as your document template (for example, IDR_MAINFRAME), and loads it into m_hAccelTable. All this magic happens totally behind the scenes, courtesy of MFC, without you having to lift your pinkyâ€"but only for main frames! Dialogs are a totally different story. Since CDialog is not derived from CFrameWnd, it doesn't inherit any of the accelerator stuff.
      Not to worry. You can easily add accelerators to any dialog just by mimicking CFrameWnd. The first thing to do is load the accelerators. The best place to load the accelerators is in your dialog's OnInitDialog function.

  BOOL CMyDlg::OnInitDialog() 
  
{
CDialog::OnInitDialog();
���
// Load accelerators
m_hAccel = ::LoadAccelerators(AfxGetResourceHandle(),
m_lpszTemplateName);
ASSERT(m_hAccel);
return TRUE;
}

You can use any ID you want for the accelerator table. Since it was more than my brain could do to think of another ID, I used the same one as the dialog itself (m_lpszTemplateName, which is either a string name or integer ID formed with MAKEINT RESOURCE):

  // In DlgKeys.rc 
  
IDD_MYDIALOG ACCELERATORS DISCARDABLE
BEGIN
VK_RETURN, ID_MY_ENTER, VIRTKEY, NOINVERT
END

      Once you've taken care of loading your accelerators, all you have to do is override PreTranslateMessage.

  BOOL CMyDlg::PreTranslateMessage(MSG* pMsg) 
  
{
if (WM_KEYFIRST <= pMsg->message &&
pMsg->message <= WM_KEYLAST)
{
HACCEL hAccel = m_hAccel;
if (hAccel &&
::TranslateAccelerator(m_hWnd, hAccel, pMsg))
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
}

The reason for checking for messages in the keystroke range (WM_ KEYFIRST to WM_KEYLAST) is speed. You don't want to waste time calling TranslateAccelerator if you know the message isn't a keystroke. And since PreTranslateMessage is virtual, you don't have to add a message map entry. Just write the function.
      Load accelerators and override PreTranslateMessageâ€"that's all you have to do to add accelerators to any dialog in MFC. Needless to say, if you decide to use accelerators, you don't have to bother with OnGetDefID; you can map VK_RETURN to an ID that has no handler. In the final code in Figure 2, I've encapsulated the dialog-with-accelerators into a new class, CdlgWinAccelerators, that's totally generic. Use it with my blessing.

Q When I use a modeless dialog in the SDK, I have to insert a statement in the main loop like this:

  while(GetMessage(&msg,NULL,0,0) { 
  
if(!IsDialogMessage(hdlg,&msg){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

      My question is: why do I need this IsDialogMessage call? Isn't a modeless dialog just another window like the main window? So without this addition can't the message be dispatched to the dialog? If I have to add this statement, does that mean that if I have several modeless dialogs I have to add a separate IsDialogMessage call for each one? If so, how does MFC handle this? I can't touch the main loop in MFC, so how do I add IsDialogMessage to it?

Hua Yuan

A Yikesâ€"that's more than one question! But let me try to answer. First of all, part of the confusion is the name. The purpose of IsDialogMessage isn't to check whether the message is for the dialog; it's to translate the message. A better name would be TranslateDialogMessage.
      As with TranslateAccelerator in the previous question, IsDialogMessage translates messages for modeless dialogs. For example, it maps Tab and Shift+Tab to the next/previous control, and maps the arrow keys to their respective operations in a dialog. You can think of it as translating the message based on a special built-in accelerator table just for dialogs. In fact, you don't have to use IsDialogMessage for dialogsâ€"you can use it for any window that contains controls to get the same keyboard behavior as a dialog. But, since the first argument of IsDialogMessage is the HWND of the dialog, you do in fact have to call it for every modeless dialog your app has running at a given time. Normally you'd have a global HWND variable, g_hDlg, that you set when you create the dialog.
      That explains why you need IsDialogMessage, and answers the question, "Do I have to add one call for each dialog?" The next question is: how does MFC handle this? As I described for the previous question, MFC uses a sophisticated routing scheme using the virtual function CWnd::PreTranslateMessage, which lets any window hook itself into the main loop without modifying the loop itself. The default implementation for CDialog::PreTranslateMessage looks like this:

  BOOL CDialog::PreTranslateMessage(MSG* pMsg) 
  
{
���
// many lines of intimidating code
���
return PreTranslateInput(pMsg);
}

In other words, CDialog passes the buck to yet another virtual function, CWnd::PreTranslateInput:

  BOOL CWnd::PreTranslateInput(LPMSG lpMsg) 
  
{
if (/* keyboard or mouse message */)
{
return IsDialogMessage(lpMsg);
}
return FALSE;
}


      As you might guess, CWnd::IsDialogMessage is a wrapper that calls ::IsDialogMessage with m_hWnd as the HWND. The result of all this is that in MFC, each dialog translates its own input. If you have five dialogs running, each dialog's PreTranslateMessage calls IsDialogMessageâ€"so you don't have to. Pretty nifty! In MFC, you don't ever have to worry about IsDialogMessageâ€"it gets taken care of automatically.

Paul DiLascia is the author of*Windows++: Writing Reusable Windows Code in C++ *(Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at cppqa@microsoft.com or https://www.dilascia.com.

From the July 2000 issue of MSDN Magazine.