Figure 3   MFC Message Routing

Message Translation
Function
CWinApp::PreTranslateMessage
CWinThread::PreTranslateMessage (32 bit MFC)
Purpose
Translate accelerator keys.
MFC Implementation(s)
CWinThread calls CWnd::PreTranslateMessage, or the window that sent the message and its parents, plus the main frame. This lets each window implement its own message translation. If any window handles the message, it returns TRUE and routing stops.
When to override
Rarely to never. Use if you have an applicationwide accelerator table that applies for all windows. You should almost always call the base class default.
Function
CWnd::PreTranslateMessage
Purpose
Translate accelerator keys.
MFC Implementation(s)
CWnd returns FALSE.
CDialog calls ::IsDialogMessage to translate modeless dialog keys like TAB and control mnemonics. Modal dialogs handled by Windows.
CFrameWnd translates using IDR_ accelerator table, if any. Looks first for pActiveDoc->GetDefaultAccelerator.
CMDIFrame gives active child a chance to translate, then calls ::TranslateMDISysAccel to translate standard Windows MDI accelerators.
CMDIChildFrame translates using resource accelerators. Looks first for document accelerators.
CControlBar calls ::IsDialogMessage to translate dialog keys in case the control bar is a dialog bar.
COleIpFrameWnd first tries to translate using local accelerators, then lets the container translate.
When to override
Override when you want to use accelerators in some place not handled by MFC (that is, other than a frame window). For example, to build a dialog-based calculator program. (Dialogs don't normally have an accelerator table.) Or if the accelerator table varies based on state other than the active document. Override CDocument::GetDefaultAccelerator if you want document-specific accelerators.
Message Handling/Dispatch
Function
CWnd::WindowProc
Purpose
Handle a message. This function is the C++ equivalent of the window procedure. First function called to handle messages.
MFC Implementation(s)
CWnd sends WM_COMMAND and WM_NOTIFY off for special handling; all other messages are dispatched via message map.
CControlBar routes WM_COMMAND, owner-draw messages and VKEY stuff to parent frame.
When to override
Override when you want first crack at a message before MFC can get it, and/or to bypass message maps, say for performance reasons. You should almost always call base class default.
Function
CWnd::OnChildNotify
Purpose
Give child windows a chance to process their own notifications, which would otherwise go to their parents only. Lets children implement their own behavior instead of having their parents do it. Returns TRUE if handled.
MFC Implementation(s)
CWnd returns FALSE.
CButton, CListBox, CComboBox, CStatusBarCtrl, CTabCtrl, CListCtrl, and CHeaderCtrl all route owner-draw messages to new virtual functions like OnDraw and OnCompareItem, so owner-draw controls can implement themselves.
When to override
Override when you want to handle a notification (WM_COMMAND or WM_NOTIFY) in the child window instead of the parent. For example, a combo box might fill itself by handling its own CBN_DROPDOWN notification. Or an edit control might validate its own input. Return TRUE if you don't want the message to go to the parent.
Command Routing
Function
CWnd::OnCommand
Purpose
Handle WM_COMMAND (command or control notification). All WM_COMMANDs go through here.
MFC Implementation(s)
CWnd passes control notifications to control's OnChildNotify function. If not handled or a command, pass to OnCmdMsg.
CFrameWnd does special help processing.
CMDIFrameWnd gives the active child window a crack at the command.
When to override
Rarely. Override when you want to handle a command before MFC routes it through the message maps, for example in 16-bit MFC when the ID is a run-time variable. (In 32-bit MFC, use ON_COMMAND_RANGE for this.) Another situation is when you've subclassed an ill-behaved control that violates the rules of WM_COMMAND (HWND/notify code in hi/lo word of LPARAM) and you need to bypass MFC's ASSERTions.
Function
CWnd::OnNotify
Purpose
Handle WM_NOTIFY (control notification). All WM_NOTIFYs go through here.
MFC Implementation(s)
CWnd gives control a chance to handle its own notification by calling control's OnChildNotify. If not handled, pass to OnCmdMsg.
CSplitterWnd passes unhandled notifications to the parent frame.
When to override
Rarely. Same as for OnCommand.
Function
CCmdTarget
Purpose
Base class for objects that have message maps and can receive commands and notifications.
MFC Implementation(s)
Base class for CWnd, CDocument, CDocTemplate, and CWinThread (CWinApp).
When to override
If you want a class to be able to handle commands and/or update UI objects, derive it from CCmdTarget.
Function
CCmdTarget::OnCmdMsg
Purpose
Handle command, notification, or CN_UPDATE_COMMAND_UI.
MFC Implementation(s)
CCmdTarget dispatches the command through the object's message map.
CFrameWnd calls OnCmdMsg for the following objects in the following order: active view (CView), itself (CWnd), the application (CWinApp), thus implementing the doc/view command architecture.
CView passes to m_pDocument->OnCmdMsg, then itself.
When to override
Override when you want to include more objects in the command route, such as a child window that's not a CView, or a modeless dialog or some nonwindow command target. You should almost always call the base class default or command handling will fall apart. You can use OnCmdMsg as a general way to send commands and notifications directly to any CCmdTarget-derived class. Return TRUE if you want to end the routing.
User Interface Objects
Function
CCmdUI
Purpose
Represents a UI object that can be updated by any command target in the command route.
MFC Implementation(s)
CCmdUI is used for menu items and controls. Private derived classes CStatusCmdUI and CToolCmdUI are used for status bar panes and toolbar buttons.
Provides virtual functions like SetText, Enable, SetCheck, and so on to manipulate the item.
When to override
Advanced. Derive from CCmdUI to implement some new kind of user interface item that requires updating.
Function
CCmdUI::DoUpdate
Purpose
Update the user interface item.
MFC Implementation(s)
Sends CN_UPDATE_COMMAND_UI, with the CCmdUI object as parameter, to the given command target. The target is usually the main frame, so the message is routed to all the objects in the doc/view architecture (doc, view, frame, app), any one of which may update the item.
When to override
I can't think of any situation where you would need to override this function.
Function
WM_IDLEUPDATECMDUI CWnd::OnIdleUpdateCmdUI
Purpose
Private MFC message (defined in AFXPRIV.H) that MFC sends to the main window and all its children during OnIdle.
MFC Implementation(s)
CControlBar calls OnUpdateCmdUI.
CFrameWnd updates the layout (position, size of control bars and client window), frame menu, and title.
CMDIFrameWnd updates the menu bar.
COleIPFrameWnd updates main and document container frames.
When to override
Advanced. You can handle this message to update user interface items during idle processing. The window must be a descendant (nth generation child) of the main frame window, so this won't work for modeless dialogs where parent=desktop. You must include AFXPRIV.H to get this message.
Function
CControlBar::OnUpdateCmdUI
Purpose
Special UI update function for control bars.
MFC Implementation(s)
CToolBar updates its buttons.
CStatusBar updates its status panes.
CDialogBar calls UpdateDialogControls to update its controls.
When to override
Advanced. Override to update items in specialized control bars. Use this instead of handling update message directly.
Function
CWnd::UpdateDialogControls
Purpose
Update all the controls in a dialog.
MFC Implementation(s)
CWnd sends CN_UPDATE_COMMAND_UI to each control in the dialog.
When to override
Call this whenever you want to update the controls in a dialog. You could call it from OnIdleUpdateCmdUI for modeless dialogs, or whenever something has changed in a modal dialog. Rarely need to override.


Figure 7   Message Map Entries Handled by OnCmdMsg


 ON_COMMAND(id, memberFn)
 ON_COMMAND_RANGE(id, idLast, memberFn)
 ON_COMMAND_EX(id, memberFn)
 ON_COMMAND_EX_RANGE(id, idLast, memberFn)
 ON_UPDATE_COMMAND_UI(id, memberFn)
 ON_UPDATE_COMMAND_UI_RANGE(id, idLast, memberFn)
 ON_NOTIFY(wNotifyCode, id, memberFn)
 ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFn)
 ON_NOTIFY_EX(wNotifyCode, id, memberFn)
 ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFn)
 ON_CONTROL(wNotifyCode, id, memberFn)
 ON_CONTROL_RANGE(wNotifyCode, id, idLast, memberFn)

Figure 11   CMDLEARN FileOpenTrace


 // This trace shows how a simple command travels through the system.
 // It begins when I select "File Open" from the menu.
 •
 •
 •
 CMainFrame[006608DC]::WindowProc(WM_COMMAND, 0xe101, 0x00000000)
 | // Main frame gets WM_COMMAND message...
 CMainFrame[006608DC]::OnCommand(ID_FILE_OPEN, 0x00000000)
 || // CMDIFrameWnd passes it to the active MDI child frame...
 ||CFileMDIChildWnd[00663998]::WindowProc(WM_COMMAND, 0xe101, 0x00000000)
 |||CFileMDIChildWnd[00663998]::OnCommand(ID_FILE_OPEN, 0x00000000)
 |||| // Send CN_UPDATE_COMMAND_UI to check that File Open is enabled
 ||||CFileMDIChildWnd[00663998]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064F82C, NULL)
 |||||CFileView[00663AEC]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064F82C, NULL)
 ||||||CFileDoc[00663728]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064F82C, NULL)
 ||||||FALSE
 |||||FALSE
 |||||CApp[00407008]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064F82C, NULL)
 |||||FALSE
 ||||FALSE // No ON_UPDATE_COMMAND_UI for ID_FILE_OPEN.
 |||| // Execute the command: route to all the command targets
 ||||CFileMDIChildWnd[00663998]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, NULL, NULL)
 |||||CFileView[00663AEC]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, NULL, NULL)
 ||||||CFileDoc[00663728]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, NULL, NULL)
 ||||||FALSE // Doc didn't handle it
 |||||FALSE // View didn't handle it
 |||||CApp[00407008]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, NULL, NULL)
 ||||||TRUE // App handled it!
 |||||TRUE
 ||||TRUE
 |||TRUE
 ||0x00000001 // MDI child done processing WM_COMMAND, returns 1 (handled)
 |TRUE
 0x00000001 // Main frame done processing WM_COMMAND, returns 1 (handled)

Figure 12   CMDLEARN Toolbar Trace


 // This trace shows how the toolbar updates its buttons. The toolbar
 // two buttons: Open and About. Trace begins when CWinThread::OnIdle
 // sends WM_IDLEUPDATECMDUI to the main frame and all its children.
 •
 •
 •
 CMainFrame[006608DC]::WindowProc(MFC:WM_IDLEUPDATECMDUI, 0x0001, 0x00000000)
 0x00000000 // Main frame doesn't do anything
 CToolBar[00660A18]::WindowProc(MFC:WM_IDLEUPDATECMDUI, 0x0001, 0x00000000)
 | // Tool bar creates CToolCmdUI for each button, calls DoUpdate, which
 | // routes CN_UPDATE_COMMAND_UI through the OnCmdMsg system
 |CMainFrame[006608DC]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 ||CApp[00407008]::OnCmdMsg(ID_FILE_OPEN, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 ||FALSE
 |FALSE 
 | // Nobody had an ON_UPDATE_COMMAND_UI for ID_FILE_OPEN, so now 
 | // CCmdUI::DoUpdate sees if there's a handler it. If not, it will
 | // automatically disable. Note last argument to OnCmdMsg is non-NULL.
 |CMainFrame[006608DC]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, 0x0064FAB8, 0x0064FA88)
 ||CApp[00407008]::OnCmdMsg(ID_FILE_OPEN, CN_COMMAND, 0x0064FAB8, 0x0064FA88)
 ||TRUE // App handles ID_FILE_OPEN!
 |TRUE
 | // Now repeat the whole process for ID_APP_ABOUT
 |CMainFrame[006608DC]::OnCmdMsg(ID_APP_ABOUT, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 ||CApp[00407008]::OnCmdMsg(ID_APP_ABOUT, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 ||FALSE
 |FALSE
 |CMainFrame[006608DC]::OnCmdMsg(ID_APP_ABOUT, CN_COMMAND, 0x0064FAB8, 0x0064FA88)
 ||CApp[00407008]::OnCmdMsg(ID_APP_ABOUT, CN_COMMAND, 0x0064FAB8, 0x0064FA88)
 ||TRUE
 |TRUE
 0x00000000 // tool bar done processing WM_IDLEUPDATECMDUI
 CStatusBar[006609A4]::WindowProc(MFC:WM_IDLEUPDATECMDUI, 0x0001, 0x00000000)
 | // Now Status bar gets WM_IDLEUPDATECMDUI. It creates a CStatusCmdUI
 | // for each indicator pane and calls CStatusCmdUI::DoUpate with the
 | // main frame as command target.
 |CMainFrame[006608DC]::OnCmdMsg(ID_INDICATOR_CAPS, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 |TRUE
 |CMainFrame[006608DC]::OnCmdMsg(ID_INDICATOR_NUM, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 |TRUE
 |CMainFrame[006608DC]::OnCmdMsg(ID_INDICATOR_SCRL, CN_UPDATE_COMMAND_UI, 0x0064FAB8, NULL)
 |TRUE
 0x00000000 // Status bar done processing WM_IDLEUPDATECMDUI

Figure 14   CComboCombo

COMCOMBO.H


 // "Owned" button: sends notifications to owner instead of parent.
 class COwnedButton : public CButton {
 public:
    virtual BOOL OnChildNotify(UINT, WPARAM, LPARAM, LRESULT*);  };
 
 // Combo box with Add/Delete buttons.
 class CComboCombo : public CComboBox {
    COwnedButton m_buttonAdd;     // "Add" button
    COwnedButton m_buttonDel;     // "Delete" button
    UINT m_idAdd;                 // ID of add button
    UINT m_idDel;                 // ID of delete button
    void UpdateButtons();         // helper to update button states
 public:
    BOOL SubclassControls(CWnd* pParent, UINT idCombo, UINT idAdd, UINT idDel);
 protected:
    virtual BOOL OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pLRes);
    //{{AFX_MSG(CComboCombo)
    afx_msg void OnCommandRange(UINT id);
    afx_msg void OnCommandUiRange(CCmdUI* pCmdUi);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()  };
COMCOMBO.CPP


 // COMCOMBO Shows how to implement a self-contained combo box with buttons.
 #include "stdafx.h"
 #include "comcombo.h"
 
 BEGIN_MESSAGE_MAP(CComboCombo, CComboBox)
    ON_COMMAND_RANGE(0, 0xFFFF, OnCommandRange)
    ON_UPDATE_COMMAND_UI_RANGE(0, 0xFFFF, OnCommandUiRange)
 END_MESSAGE_MAP()
 
 // Subclass the combo box and Add/Delete buttons. The Dialog must call this
 // to tell the combo box what the control IDs are and hook everything up.
 BOOL CComboCombo::SubclassControls(CWnd* pParent, 
    UINT idCombo, UINT idAdd, UINT idDel)
 {  if (!SubclassDlgItem(idCombo, pParent))
       return FALSE;
    if (!m_buttonAdd.SubclassDlgItem(idAdd, pParent))
       return FALSE;
 
    if (!m_buttonDel.SubclassDlgItem(idDel, pParent))
       return FALSE;
 
    m_idAdd = idAdd;
    m_idDel = idDel;
 
    // I own the buttons now
    m_buttonAdd.SetOwner(this);
    m_buttonDel.SetOwner(this);
    UpdateButtons();  // Update (enable/disable) the buttons
 
    return TRUE;  }
 
 // When the edit text or selection changes, 
 // button states may change, so I need to update them.
 BOOL 
 CComboCombo::OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pLRes) 
 {  if (msg= =WM_COMMAND) {
       int nCode = HIWORD(wp);
       if (nCode= =CBN_SELCHANGE || nCode= =CBN_EDITCHANGE)
          UpdateButtons();  }
    return CComboBox::OnChildNotify(msg,wp,lp,pLRes);  }
 
 // Helper function updates Add/Delete buttons
 void CComboCombo::UpdateButtons()
 {  GetParent()->UpdateDialogControls(this, FALSE);  }
 
 // Handle command in range 0 to 0xFFFF (all commands). Check for Add/Delete.
 void CComboCombo::OnCommandRange(UINT id)
 {  if (id= =m_idAdd) {
       // Add command: add contents of edit control to list box.
       // Beep if item already added.
       CString s;
       GetWindowText(s);
       if (FindStringExact(0, s) >= 0)
          MessageBeep(0);
       else {
          AddString(s);
          SetEditSel(0, -1);
          Clear();
          SetFocus(); }
       UpdateButtons();
    } else if (id= =m_idDel) {
       // Delete command: Delete selected item.
       DeleteString(GetCurSel());
       UpdateButtons();  }
 }
 
 // Handle command update (0-0xFFFF, all commands). Check for Add/Delete.
 void CComboCombo::OnCommandUiRange(CCmdUI* pCmdUI)
 {  UINT id = pCmdUI->m_nID;
    if (id= =m_idAdd)
       // Add button is enabled iff there's text.
       pCmdUI->Enable(GetWindowTextLength()>0);
    else if (id= =m_idDel)
       // Delete is enable iff an item is selected.
       pCmdUI->Enable(GetCurSel()>=0);
 }
 
 // Re-route WM_COMMAND notification to owner instead of parent. bLockout flag
 // prevents infinite recursion, because owner will call OnChildNotify too.
 BOOL 
 COwnedButton::OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pLRes) 
 {  static BOOL bLockout = FALSE;
    if (msg= =WM_COMMAND && !bLockout) {
       bLockout = TRUE;
       GetOwner()->SendMessage(msg, wp, lp);
       bLockout = FALSE;  }
    return CButton::OnChildNotify(msg,wp,lp,pLRes);
}