http://blog.sina.com.cn/s/blog_4854085d0100fkdr.html
大家在编程的过程中一定遇到过这种情况:需要根据某个变量的值来设定菜单项是否被选中,设置工具栏按钮是否被按下或者在状态栏中显示一些信息。MFC提供了一种机制来帮助我们完成这项工作:只要用ClassWizard给相应的菜单项或者工具栏按钮添加一个UPDATE_COMMAND_UI处理函数,在其中用CcmdUI::SetCheck等函数来设置这些用户界面元素的状态就可以了。但是MFC是怎么实现这个功能的呢?
}
下面是CCmdUI::DoUpdate的代码:
BOOL CCmdUI::DoUpdate(CCmdTarget* pTarget, BOOL bDisableIfNoHndler){
BOOL bResult=pTarget->OnCmdMsg(m_nID,CN_UPDATE_COMMAND_UI,
this, NULL);
}
DoUpdate的流程就是:先向你的菜单项发一个CN_UPDATE_COMMAND_UI命令消息,让你的菜单项来进行显示前的更新,这就是你在classwizard中可以看到的UPDATE_COMMADN_UI消息,你加的处理函数就是在这个时候被调用的。如果你处理了CN_UPDATE_COMMAND_UI,那么m_bEnableChanged就变成true,接下来就直接返回了。否则,如果bDisableIfNoHndler也为true,那么就向菜单项发一个CN_COMMAND消息,如果你不响应这个消息,说明这个菜单项还没有处理函数,那么,bnHandler就是flase,然后Enable(false)就把你的菜单项变灰了。注意在CFrameWnd::OnInitMenuPopup中调用DoUpdate时的参数是m_bAutoMenuEnable && state.m_nID<0xF000,这说如果你一开始就把m_bAutoMenuEnable设为false的话,实际上就关闭了MFC自动diable没有处理函数的菜单项的功能。
BOOL CWinThread::OnIdle(LONG lCount){
//依次向main window及其所有子窗口发送WM_IDLEUPDATECMDUI消息,这个消息指示接收窗口进行更新操作
//接下来向本线程创建的所有frame window发送WM_IDLEUPDATECMDUI消息
_AFX_CMDTARGET_GETSTATE()->m_thread;
}
你的toolbar或者statusbar总是某个frame window的子窗口(包括子窗口的子窗口…),所以它肯定能收到WM_IDLEUPDATECMDUI消息。CToolBar和CStatusBar都是从CControlBar派生的,下面是CControlBar对这个消息的处理:
LRESULT CControlBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
}
OnUpdateCmdUI是CControlBar类的一个纯虚函数,CToolBar中对这个函数进行了定义:
void CToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler){
//如果toolbar没有更新自己,让pTarget(也就是离它最近的父frame window)来更新它。比如对于MFC自动生成的SDI框架来说,pTarget会指向CMainFrame
}
CCmdUI::DoUpdate的代码上面已经列出过了。至此,工具栏和状态栏也能顺利也进行更了。
有经验的朋友应该知道,如果你在一个基于对话框的程序里模仿doc/view结构中的方法使用UPDATE_COMMAND_UI来更新用户界面元素的话是不会有任何效果的。其原因是一个模态对话显示出来以后,程序就会进入这个对话框自己的消息循环(看看DoModal的源码就能了解这一点),此时不会再有WM_IDLEUPDATECMDUI被发送到这些界面元素中。下面说说这种情况下的解决办法,你可以自己查看MFC的源码来弄清它的原理:首先加一个头文件afxpriv.h(其中定义了KICKIDLE消息),然后添加一个消息映射来处理WM_KICKIDLE消息:ON_MESSAGE(WM_KICKIDLE,OnKickIdle)。其中OnKickIdle定义如下:
LRESULT CTabDialog::OnKickIdle(WPARAM wp, LPARAM lCount){
UpdateDialogControls(this, TRUE);
return 0;
}
完成这些工作以后,你就可以顺利地使用UPDATE_COMMAND_UI机制了。
---------------------------
CCmdUI
CCmdUI没有基类
它仅在一个CCmdTarget派生类的ON_UPDATE_COMMAND_UI处理程序中使用。
当用户在应用的下拉菜单时,要确定每个菜单项的显示状态——允许存取或禁止存取。菜单命令的目标通过实现一个ON_UPDATE_COMMAND_UI处理来提供这些信息。可以使用ClassWizard来浏览定位应用中的命令用户接口对象,然后为它建立一个消息映射入口,并为每个消息处理函数提供函数原型。
当菜单被下拉时,框架搜索并调用每个ON_UPDATE_COMMAND_UI处理,每个处理调用Enable和Check之类的成员函数,相应地,框架就可以正确地显示每个菜单项了。
菜单项可以用控件条按钮或者其它的命令用户接口对象替换,而在ON_UPDATE_COMMAND_UI处理中的代码不需要改动。
下表列出了各种命令用户接口上的CCmdGUI的成员函数。
用户接口项 Enable SetCheck SetRadio SetText
菜单项 允许或禁止存取该项 选中(ⅹ)或未选中 选中(有黑点) 设置项的文本
工具条按钮 允许或禁止存取该项 选中、未选中或不定 (不可用) 与SetCheck相同
状态条状态提示 文本可见或不可见 设置凸出或正常边框 与SetCheck相同 设置状态条的提示文本
CDialogBar中的普通按钮 允许或禁止存取该项 复选框选中或未选中 与SetCheck相同 设置按钮的文本
CDialogBar中的普通按钮 允许或禁止存取该项 (不可用) (不可用) 设置窗口中的文本
有关使用类CCmdGUI的更详细信息,请参阅联机文档“Visual C++教程”中的“构造用户界面”部分和联机文档“Visual C++程序员指南”中的“如何更新用户界面对象”部分。