MFC 定制界面会用到自定义非窗口类控件,因为每一个小按钮都从窗口继承时浪费资源,效率低。本文描述如何在自定义非窗口类控件中实现 ON_COMMAND 和 ON_UPDATE_COMMAND_UI。
一、ON_COMMAND
ON_COMMAND 是 MFC 的命令映射和传递机制,简化消息处理接口,步骤如下:
1. 基类必须含 CCmdTarget
class AFX_EXT_CLASS HCtrl : public CCmdTarget
2. 加DECLARE_MESSAGE_MAP()和BEGIN_MESSAGE_MAP
3. 在主窗口的OnCmdMsg消息中加如下代码
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// 让自定义类第一次尝试该命令,不加这一句,收不到命令
if (m_pHCtrl->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// 否则,执行默认处理
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
4. 加入如下类似代码处理命令
BEGIN_MESSAGE_MAP(DynamicScan, HCtrl)
ON_COMMAND(TID_NUCORRECTION, OnNucorrection) // 不均匀校正
ON_COMMAND(TID_NOISE, OnNoise) // 模拟噪声设置
ON_COMMAND(TID_FREEZE, OnFreeze) // 冻结
END_MESSAGE_MAP()
好了,ON_COMMAND 功能完成,下面的功能更实用。
二、ON_UPDATE_COMMAND_UI
如果在逻辑代码中设置控件的状态,包括选择,不可用等,要设的地方很多,效率低,容易遗漏,不易修改。ON_UPDATE_COMMAND_UI 很好地解决了这个问题,基本原理是当进程空闲时,应用程序自动产生WM_IDLEUPDATECMDUI消息,更新所有控件状态。
1. 自定义更新类
// HCmdUI idle update through HCmdUI class
class AFX_EXT_CLASS HCmdUI : public CCmdUI
{
public:
HCmdUI();
virtual void Enable(BOOL bOn);
virtual void SetCheck(int nCheck);
virtual void SetText(LPCTSTR lpszText);
virtual void SetRadio(BOOL bOn = TRUE);
HCtrl* m_pUpdated;
};
2. 在自定义控件中加入如下代码
// 自动更新控件状态
void HCtrl::OnUpdateCmdUI(HCmdUI* pCmdUI, BOOL bDisableIfNoHndler)
{
ASSERT_VALID(m_pWnd);
// 先更新子控件
int n = m_arChildren.GetCount();
for( int i = 0; i < n; i++)
{
m_arChildren[i]->OnUpdateCmdUI( pCmdUI, bDisableIfNoHndler );
}
((HCmdUI*)pCmdUI)->m_pUpdated = this; // 本控件指针
pCmdUI->m_nID = m_nID;
pCmdUI->DoUpdate(m_pWnd, bDisableIfNoHndler);
((HCmdUI*)pCmdUI)->m_pUpdated = NULL;
}
3. 主窗口中添加消息处理
BEGIN_MESSAGE_MAP(HWnd, CWnd)
.
.
.
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI) // WM_IDLEUPDATECMDUI 类视图中没有这个消息
END_MESSAGE_MAP()
LRESULT HWnd::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
if (!(GetStyle() & WS_VISIBLE))
return 0L;
BOOL bDisableIfNoHndler = (BOOL)wParam;
HCmdUI state;
state.m_pOther = this;
m_ctrFrame.OnUpdateCmdUI( &state, bDisableIfNoHndler );
return 0L;
}
注意:mfc2010的类向导中没有WM_IDLEUPDATECMDUI这个消息,要手动添加消息处理。
4. 添加具体的处理函数
BEGIN_MESSAGE_MAP(PanlCtr, HCtrl)
ON_UPDATE_COMMAND_UI(TID_SAVE, OnSaveUpdateUI)
END_MESSAGE_MAP()
void PanlCtr::OnSaveUpdateUI(CCmdUI *pCmdUI)
{
if( !pCmdUI->m_pOther ) //CN_UPDATE_COMMAND_UI 不处理
return;
((HCmdUI*)pCmdUI)->m_pUpdated->setText( L"aaaaaaa" );
}
至此实现ON_UPDATE_COMMAND_UI。
以上只是这两个功能的一些要点,使用时根据实际情况调整。
有不妥之处请指正。