深入浅出MFC第2版(PDF格式)-第122部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
CPen m_penCur; // pen created according to
。。。
}
593
…………………………………………………………Page 656……………………………………………………………
第篇 深入 MFC 程式設計
现在重新考虑文件初始化的动作,将Step1 的:
void CScribbleDoc::InitDocument()
{
m_nPenWidth = 2; // default 2 pixel pen width
// solid; black pen
m_penCur。CreatePen(PS_SOLID; m_nPenWidth; RGB(0;0;0));
}
改变为Step2 的:
void CScribbleDoc::InitDocument()
{
m_bThickPen = FALSE;
m_nThinWidth = 2; // default thin pen is 2 pixels wide
m_nThickWidth = 5; // default thick pen is 5 pixels wide
ReplacePen(); // initialize pen according to current width
}
维护UI 对象状态(UPDATE_MAND_UI)
上一节我曾提过WizardBar 右侧的【Messages 】清单中,针对各个命令项,会出现
MAND 和UPDATE_MAND_UI 两种选择。后者做什么用?
一个菜单拉下来,使用者可以从命令项的状态(打勾或没打勾、灰色或正常)得到一些
状态提示。如果Document 中没有任何资料的话,【Edit/Clear All 】照道理就不应该起作
用,因为根本没资料又如何〃Clear All〃 呢?! 这时候我们应该把这个命令项除能
(disable )。又例如在粗笔状态下,程序的【Pen/Thick Line 】命令项应该打一个勾(所
谓的check mark ),在细笔状态下不应该打勾。此外,菜单命令项的状态应该同步影响
到对应之工具栏按钮状态。
所有UI 对象状态的维护可以依赖所谓的UPDATE_MAND_UI 消息。
传统SDK 程序中要改变菜单命令项状态, 可以调用EnableMenuItem 或是
CheckMenuItem ,但这使得程序杂乱无章,因为你没有一个固定的位置和固定的原则处
理命令项状态。MFC 提供一种直觉并且仍旧依赖消息观念的方式,解决这个问题,这就
594
…………………………………………………………Page 657……………………………………………………………
第9章 訊息映射與命令繞行
是UPDATE_MAND_ UI 消息。其设计理念是,每当菜单被拉下并尚未显示之前,
其命令项(以及对应之工具栏按钮)都会收到UPDATE_MAND_ UI 消息,这个消
息和WM_MAND 有一样的绕行路线,我们(程序员)只要在适当的类别中放置其
处理函数,并在函数中做某些判断,便可决定如何显示命令项。
这种方法的最大好处是,不但把问题的解决方式统一化,更因为Framework 传给
UPDATE_MAND_ UI 处理例程的参数是一个「指向CCmdUI 对象的指针」,而
CCmdUI 对象就代表着对应的菜单命令项,因此你只需调用CCmdUI 所准备的,专门
用来处理命令项外观的函数(如Enable 或SetCheck )即可。我们的工作量大为减轻。
选按【Edit/Clear All】 CScribbleDoc::OnEditClearAll
对象
CScribbleDoc (清除文件内容)
ID_EDIT_CLEAR_ALL
UI 对象被选择 ON_MAND
有机会修改UI 对象状态 ON_UPDATE_MAND_UI
mand Target Message Map
拉下【Edit】菜单 CScribbleDoc::OnUpdateEditClearAll
(菜单命令项即将显示) (将菜单项致能或除能、打勾或取消)
图9…7 ON_MAND 和ON_UPDATE_MAND_UI 的运作
图9…7 以【Edit/Clear All 】实例说明ON_MAND 和ON_ UPDATE_MAND_ UI
的运作。为了拦截UPDATE_MAND_ UI 消息,你的mand Target 对象(也许是
Application ,也许是windows ,也许是Views ,也许是Documents )要做两件事情:
1。 利用WizardBar (或ClassWizard )加上一笔Message Map 项目如下:
ON_UPDATE_MAND_UI(ID_xxx; OnUpdatexxx)
595
…………………………………………………………Page 658……………………………………………………………
第篇 深入 MFC 程式設計
2。 提供一个OnUpdatexxx 函数。这个函数的写法十分简单,因为Framework 传
来一个代表UI 对象(也就是菜单命令项或工具栏按钮)的CCmdUI 对象指
标,而对UI 对象的各种操作又都已设计在CCmdUI 成员函数中。举个例子:
void CScribbleDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI)
{
pCmdUI…》Enable(!m_strokeList。IsEmpty());
}
void CScribbleDoc::OnUpdatePenThickOrThin(CCmdUI* pCmdUI)
{
pCmdUI…》SetCheck(m_bThickPen);
}
如果命令项与某个工具栏按钮共享同一个命令ID ,上述的Enable 动作将不只影响命令
项,也影响按钮。命令项的打勾(checked )即是按钮的按下(depressed ),命令项没有
打勾(unchecked )即是按钮的正常化(松开)。
现在,Scribble 第二版全部修改完毕,制作并测试之:
在整合环境中按下【Build/Build Scribble 】编译并联结。
按下【Build/Execute】执行Scribble。测试细笔粗笔的运作情况,以及【Edit /Clear
All 】是否生效。
596
…………………………………………………………Page 659……………………………………………………………
第9章 訊息映射與命令繞行
从写程序(而不是挖背后意义)的角度去看Message Map ,我把Step2 所进行的菜单改
变对Message Map 造成的影响做个总整理。一共有四个相关成份会被ClassWizard (或
WizardBar )产生出来,下面就是相关源代码,其中只有第4 项的函数内容是我们撰写的,
其它都由工具自动完成。
1。 CSRIBBLEDOC。CPP
BEGIN_MESSAGE_MAP(CScribbleDoc; CDocument)
//{{AFX_MSG_MAP(CScribbleDoc)
ON_MAND(ID_EDIT_CLEAR_ALL; OnEditClearAll)
ON_MAND(ID_PEN_THICK_OR_THIN; OnPenThickOrThin)
ON_UPDATE_MAND_UI(ID_EDIT_CLEAR_ALL; OnUpdateEditClearAll)
ON_UPDATE_MAND_UI(ID_PEN_THICK_OR_THIN; OnUpdatePenThickOrThin)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
不要去掉//{{ 和//}},否则下次ClassWizard 或WizardBar 不能正常工作。
2。 CSRIBBLEDOC。H
class CScribbleDoc : public CDocument
{
。。。
// Generated message map functions
protected:
//{{AFX_MSG(CScribbleDoc)
afx_msg void OnEditClearAll();
afx_msg void OnPenThickOrThin();
afx_msg void OnUpdateEditClearAll(CCmdUI* pCmdUI);
afx_msg void OnUpdatePenThickOrThin(CCmdUI* pCmdUI);
//}}AFX_MSG
。。。
};
3。 RESOURCE。H
#define ID_PEN_THICK_OR_THIN 32772
#define ID_PEN_WIDTHS 32773
(另一个项目ID_EDIT_CLEAR_ALL 已经在AFXRES。H 中定义了)
597
…………………………………………………………Page 660……………………………………………………………
第篇 深入 MFC 程式設計
4。 SCRIBBLEDOC。CPP
void CScribbleDoc::OnEditClearAll()
{
DeleteContents();
SetModifiedFlag(); // Mark the document as having been modified; for
// purposes of confirming File Close。
UpdateAllViews(NULL);
}
void CScribbleDoc::OnPenThickOrThin()
{
// Toggle the state of the pen between thin or thick。
m_bThickPen = !m_bThickPen;
// Change the current pen to reflect the new user…specified width。
ReplacePen();
}
void CScribbleDoc::ReplacePen()
{
m_nPenWidth = m_bThickPen? m_nThickWidth : m_nThinWidth;
// Change the current pen to reflect the new user…specified width。
m_penCur。DeleteObject();
m_penCur。CreatePen(PS_SOLID; m_nPenWidth; RGB(0;0;0)); // solid black
}
void CScribbleDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI)
{
// Enable the mand user interface object (menu item or tool bar
// button) if the document is non…empty; i。e。; has at least one stroke。
pCmdUI…》Enable(!m_strokeList。IsEmpty());
}
void CScribbleDoc::OnUpdatePenThickOrThin(CCmdUI* pCmdUI)
{
// Add check mark to Draw Thick Line menu item; if the current
// pen width is 〃thick〃。
pCmdUI…》SetCheck(m_bThickPen);
}
598
…………………………………………………………Page 661……………………………………………………………
第9章 訊息映射與命令繞行
本章回顾
这一章主要为Scribble Step2 增加新的菜单命令项。在这个过程中我们使用了工具栏编
辑器和ClassWizard (或Wizardbar )等工具。工具的使用很简单,但是把消息的处理常
式加在什么地方却是关键。因此本章一开始先带你深入探索MFC 源代码,了解消息的
递送以及所谓Message Map 背后的意义,并且也解释了命令消息(WM_MAND) 特
异的绕行路线及其原因。
我在本章中挖出了许多MFC 源代码,希望藉由源代码的自我说明能力,加深你对消息