- MFC Under the Hood: Creating an MFC Application
- Using Better Tracing to Understand the Doc/View Architecture
- Command Routing Beyond a Split Frame
- Replacing a View in a Doc-View Application
Command Routing Beyond a Split Frame
This article was contributed by Bartosz Bien.
Abstract: The article presents a simple method of routing WM_COMMAND messages through a number of views in a split frame window. This simplifies dealing with command routing and UI updates for inactive views.
The standard framework route does not include inactive views, which causes toolbar buttons and menus to gray when their mother view is deactivated. Users are confused. To bring back their happiness, I have overridden the CCmdTarget::OnCmdMsg function in the main frame (obviously derived from CFrameWnd):
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { CDocument *pDoc = GetActiveDocument(); if(pDoc) { POSITION pos = pDoc->GetFirstViewPosition(); CView *pView = NULL; while(pView = pDoc->GetNextView(pos)) { if(pView != GetActiveView() && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } } return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); }
I used a list of views contained in the active document (if any). The command message is passed to all views but the active one, which had a chance to handle it before it was routed to the frame window. If the message is handled by one of the views (OnCmdMsg indicates it by returning TRUE), no further processing is needed and we can return. Otherwise, the base class member is called to restore the conventional framework route.
Comments
Visual C++ version 5 has a problem compiling the sample code. Here is how to correct this:
Due to some cosmetic changes to the MFC, the function CView::OnCmdMsg is no longer protected in VC6. (Why should it be, while it is public in CCmdTarget?)
In VC5, you can correct this simply by just changing CView* to CCmdTarget*:
if(pDoc) { POSITION pos = pDoc->GetFirstViewPosition(); CCmdTarget *pCT = NULL; while(pCT = pDoc->GetNextView(pos)) { if(pCT != GetActiveView() && pCT->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } }
It's that easy because CView is inherited from CCmdTarget (so you don't need to cast!), and (pCT != GetActiveView()) is just a comparison of pointers.
Bartosz Bien