现在的位置: 首页 > 综合 > 正文

MFC-命令分派

2013年05月10日 ⁄ 综合 ⁄ 共 2096字 ⁄ 字号 评论关闭

    MFC的命令大体上由两类界面元素引发,一种是菜单项,另一种是如按钮,复选框等的通用控件。从消息来看,其实就是处理WM_COMMAND消息。尽管命令消息的进入点仍然是CWnd::OnWndMsg,不过MFC让它走了另一条路,即OnCommand

      让命令消息作另外处理是有原因的,比如说菜单命令,往往处理它的并不是FrameWnd,而是ViewDocument或其他的类。所以菜单命令并不是简单的发给拥有菜单的窗口,而是在整个框架绕了一圈。

      CWnd::OnCommand调用了OnCmdMsg这是一个虚函数,不同的类对命令的派发是不一样的,因此各各覆盖OnCmdMsg作具体的处理。

      先看看FrameWnd的:

BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,

   AFX_CMDHANDLERINFO* pHandlerInfo)

{

   // pump through current view FIRST

   CView* pView = GetActiveView();

   if (pView != NULL &&
pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))

       return TRUE;

 

   // then pump through frame

   if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))

       return TRUE;

 

   // last but not least, pump through app

   CWinApp* pApp = AfxGetApp();

   if (pApp != NULL &&
pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))

       return TRUE;

 

   return FALSE;

}

      FrameWnd上的命令大多数是菜单命令,为了让框架中的其他类能够处理菜单命令,它在三个地方调用OnCmdMsg,首先得到处理的是View,其次是FrameWnd自己,最后是WinApp。其实View处理的时候会让Document类处理命令,而Document又会让DocTemplate类处理,如果不中断,整个处理顺序大概是这样的:

      CView::OnCmdMsg

      CDocument::OnCmdMsg

      CDocTemplate::OnCmdMsg

      CFrameWnd::OnCmdMsg

      pApp->OnCmdMsg

      无论上面的类怎么处理,最后的分派都归结到CCmdTarget::OnCmdMsg,这个函数可以简化成这两句代码:

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,

   AFX_CMDHANDLERINFO* pHandlerInfo)

{

   if (FindMessageEntry(…))

       _AfxDispatchCmdMsg(…)

}

_AfxDispatchCmdMsg真正将消息派发到处理函数去,其做法与CWnd::OnWndMsg是很相似的。

对话框的命令要简单得多,去除其他复杂的因素,甚至可以认为CDialog::OnCmdMsg直接分派消息,也就是只有CDialog的具体子类才能处理它上面的按钮命令。

 

MFC的命令处理可以应付简单的应用程序,但对于复杂如Office这样的程序,则太过于简陋了。举一个简单的例子,一个对话框有非常多的界面元素,并且这些界面元素要根据数据状态实时更新,利用MFC的命令处理会比较麻烦,因为它没有对话框的实时更新机制。一种理想的方法是这样的,在消息处理Idle的时候,每个界面元素向数据查询状态,这种查询可以认为是非常快速的,根据状态实时更新自己的表现形式,比如EnabledVisible等。这就不仅为命令引入了“执行”的行为,还引入了“更新”的行为。执行和更新是命令机制的基础,看看Office的工具条,如果没有Idle的更新机制,每个按钮的更新可以想象有多么的复杂。

这一点在VCL里面做得比较好,它设计了一套Action的框架,与界面元素最大程度的分离开来,更加集中的处理“执行”和“更新”这种两种行为。每一种界面元素,不管是菜单还是按钮,只要与一个Action挂接,那么它的状态和行为完成由Action来掌管。这样做的好处很多,我列出两个:

1.        多个界面元素可以对应一个Action,这种情况经常出现,比如菜单项与工具栏上的某个按钮,如果它们都挂上同一个Action,则它们的行为和状态都会变得一致。

2.        减少命令与界面的依赖,以后若想换一套新的菜单控件,只要这套控件支持Action机制,则可以在不影响程序行为的情况下轻松更换。

如果是MFC,如何来提供这样的命令机制呢?我想这是考验MFC的可扩展性的时候了。 

抱歉!评论已关闭.