友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第80部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
消息映射表的每一笔记录是这样的形式:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
内中包括一个Windows 消息、其控制组件ID 以及通告码(notification code ,对消息的
更多描述,例如EN_CHANGED 或CBN_DROPDIOWN 等)、一个签名记号、以及一个
CCmdTarget 衍生类别的成员函数。任何一个ON_ 宏会把这六个项目初始化起来。例
如:
#define ON_WM_CREATE ()
{ WM_CREATE; 0; 0; 0; AfxSig_is;
(AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate };
你看到了可怕的类型转换动作,这完全是为了保持类型安全(type…safe )。
580
…………………………………………………………Page 643……………………………………………………………
第9章 訊息映射與命令繞行
有一个很莫名其妙的东西:AfxSig_ 。要了解它作什么用,你得先停下来几分钟,想想另
一个问题:当上一节的推动引擎比对消息并发现吻合之后,就调用对应的处理例程,但
它怎么知道要交给消息处理例程哪些参数呢?要知道,不同的消息处理例程需要不同的
参数(包括个数和类型),而其函数指针(AFX_PMSG )却都被定义为这付德行:
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
这么简陋的信息无法表现应该传递什么样的参数,而这正是AfxSig_ 要贡献的地方。当
推动引擎比对完成,欲调用某个消息处理例程lpEntry…》pfn 时,动作是这样子地(出现
在CWnd::OnWndMsg 和DispatchCmdMsg 中):
union MessageMapFunctions mmf;
mmf。pfn = lpEntry…》pfn;
switch (lpEntry…》nSig)
{
case AfxSig_is:
lResult = (this…》*mmf。pfn_is)((LPTSTR)lParam);
break;
case AfxSig_lwl:
lResult = (this…》*mmf。pfn_lwl)(wParam; lParam);
break;
case AfxSig_vv:
(this…》*mmf。pfn_vv)();
break;
。。。
}
注意两样东西:MessageMapFunctions 和AfxSig_ 。AfxSig_ 定义于AFXMSG_。H 档:
enum AfxSig
{
AfxSig_end = 0; // 'marks end of message map'
AfxSig_bD; // BOOL (CDC*)
AfxSig_bb; // BOOL (BOOL)
AfxSig_bWww; // BOOL (CWnd*; UINT; UINT)
AfxSig_hDWw; // HBRUSH (CDC*; CWnd*; UINT)
AfxSig_hDw; // HBRUSH (CDC*; UINT)
581
…………………………………………………………Page 644……………………………………………………………
第篇 深入 MFC 程式設計
AfxSig_iwWw; // int (UINT; CWnd*; UINT)
AfxSig_iww; // int (UINT; UINT)
AfxSig_iWww; // int (CWnd*; UINT; UINT)
AfxSig_is; // int (LPTSTR)
AfxSig_lwl; // LRESULT (WPARAM; LPARAM)
AfxSig_lwwM; // LRESULT (UINT; UINT; CMenu*)
AfxSig_vv; // void (void)
AfxSig_vw; // void (UINT)
AfxSig_vww; // void (UINT; UINT)
AfxSig_vvii; // void (int; int) // wParam is ignored
AfxSig_vwww; // void (UINT; UINT; UINT)
AfxSig_vwii; // void (UINT; int; int)
AfxSig_vwl; // void (UINT; LPARAM)
AfxSig_vbWW; // void (BOOL; CWnd*; CWnd*)
AfxSig_vD; // void (CDC*)
AfxSig_vM; // void (CMenu*)
AfxSig_vMwb; // void (CMenu*; UINT; BOOL)
AfxSig_vW; // void (CWnd*)
AfxSig_vWww; // void (CWnd*; UINT; UINT)
AfxSig_vWp; // void (CWnd*; CPoint)
AfxSig_vWh; // void (CWnd*; HANDLE)
AfxSig_vwW; // void (UINT; CWnd*)
AfxSig_vwWb; // void (UINT; CWnd*; BOOL)
AfxSig_vwwW; // void (UINT; UINT; CWnd*)
AfxSig_vwwx; // void (UINT; UINT)
AfxSig_vs; // void (LPTSTR)
AfxSig_vOWNER; // void (int; LPTSTR); force return TRUE
AfxSig_iis; // int (int; LPTSTR)
AfxSig_wp; // UINT (CPoint)
AfxSig_wv; // UINT (void)
AfxSig_vPOS; // void (WINDOWPOS*)
AfxSig_vCALC; // void (BOOL; NCCALCSIZE_PARAMS*)
AfxSig_vNMHDRpl; // void (NMHDR*; LRESULT*)
AfxSig_bNMHDRpl; // BOOL (NMHDR*; LRESULT*)
AfxSig_vwNMHDRpl; // void (UINT; NMHDR*; LRESULT*)
AfxSig_bwNMHDRpl; // BOOL (UINT; NMHDR*; LRESULT*)
AfxSig_bHELPINFO; // BOOL (HELPINFO*)
AfxSig_vwSIZING; // void (UINT; LPRECT) …return TRUE
// signatures specific to CCmdTarget
AfxSig_cmdui; // void (CCmdUI*)
AfxSig_cmduiw; // void (CCmdUI*; UINT)
AfxSig_vpv; // void (void*)
AfxSig_bpv; // BOOL (void*)
582
…………………………………………………………Page 645……………………………………………………………
第9章 訊息映射與命令繞行
// Other aliases (based on implementation)
AfxSig_vwwh; // void (UINT; UINT; HANDLE)
AfxSig_vwp; // void (UINT; CPoint)
AfxSig_bw = AfxSig_bb; // BOOL (UINT)
AfxSig_bh = AfxSig_bb; // BOOL (HANDLE)
AfxSig_iw = AfxSig_bb; // int (UINT)
AfxSig_ww = AfxSig_bb; // UINT (UINT)
AfxSig_bv = AfxSig_wv; // BOOL (void)
AfxSig_hv = AfxSig_wv; // HANDLE (void)
AfxSig_vb = AfxSig_vw; // void (BOOL)
AfxSig_vbh = AfxSig_vww; // void (BOOL; HANDLE)
AfxSig_vbw = AfxSig_vww; // void (BOOL; UINT)
AfxSig_vhh = AfxSig_vww; // void (HANDLE; HANDLE)
AfxSig_vh = AfxSig_vw; // void (HANDLE)
AfxSig_viSS = AfxSig_vwl; // void (int; STYLESTRUCT*)
AfxSig_bwl = AfxSig_lwl;
AfxSig_vwMOVING = AfxSig_vwSIZING; // void (UINT; LPRECT) …return TRUE
};
MessageMapFunctions 定义于 WINCORE。CPP 档:
union MessageMapFunctions
{
AFX_PMSG pfn; // generic member function pointer
// specific type safe variants
BOOL (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);
BOOL (AFX_MSG_CALL CWnd::*pfn_bb)(BOOL);
BOOL (AFX_MSG_CALL CWnd::*pfn_bWww)(CWnd*; UINT; UINT);
BOOL (AFX_MSG_CALL CWnd::*pfn_bHELPINFO)(HELPINFO*);
HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDWw)(CDC*; CWnd*; UINT);
HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDw)(CDC*; UINT);
int (AFX_MSG_CALL CWnd::*pfn_iwWw)(UINT; CWnd*; UINT);
int (AFX_MSG_CALL CWnd::*pfn_iww)(UINT; UINT);
int (AFX_MSG_CALL CWnd::*pfn_iWww)(CWnd*; UINT; UINT);
int (AFX_MSG_CALL CWnd::*pfn_is)(LPTSTR);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwl)(WPARAM; LPARAM);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwwM)(UINT; UINT; CMenu*);
void (AFX_MSG_CALL CWnd::*pfn_vv)(void);
void (AFX_MSG_CALL CWnd::*pfn_vw)(UINT);
void (AFX_MSG_CALL CWnd::*pfn_vww)(UINT; UINT);
void (AFX_MSG_CALL CWnd::*pfn_vvii)(int; int);
void (AFX_MSG_CALL CWnd::*pfn_vwww)(UINT; UINT; UINT);
void (AFX_MSG_CALL CWnd::*pfn_vwii)(UINT; int; int);
583
…………………………………………………………Page 646……………………………………………………………
第篇 深入 MFC 程式設計
void (AFX_MSG_CALL CWnd::*pfn_vwl)(WPARAM; LPARAM);
void (AFX_MSG_CALL CWnd::*pfn_vbWW)(BOOL; CWnd*; CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vD)(CDC*);
void (AFX_MSG_CALL CWnd::*pfn_vM)(CMenu*);
void (AFX_MSG_CALL CWnd::*pfn_vMwb)(CMenu*; UINT; BOOL);
void (AFX_MSG_CALL CWnd::*pfn_vW)(CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vWww)(CWnd*; UINT; UINT);
void (AFX_MSG_CALL CWnd::*pfn_vWp)(CWnd*; CPoint);
void (AFX_MSG_CALL CWnd::*pfn_vWh)(CWnd*; HANDLE);
void (AFX_MSG_CALL CWnd::*pfn_vwW)(UINT; CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vwWb)(UINT; CWnd*; BOOL);
void (AFX_MSG_CALL CWnd::*pfn_vwwW)(UINT; UINT; CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vwwx)(UINT; UINT);
void (AFX_MSG_CALL CWnd::*pfn_vs)(LPTSTR);
void (AFX_MSG_CALL CWnd::*pfn_vOWNER)(int; LPTSTR); // force return TRUE
int (AFX_MSG_CALL CWnd::*pfn_iis)(int; LPTSTR);
UINT (AFX_MSG_CALL CWnd::*pfn_wp)(CPoint);
UINT (AFX_MSG_CALL CWnd::*pfn_wv)(void);
void (AFX_MSG_CALL CWnd::*pfn_vPOS)(WINDOWPOS*);
void (AFX_MSG_CALL CWnd::*pfn_vCALC)(BOOL; NCCALCSIZE_PARAMS*);
void (AFX_MSG_CALL CWnd::*pfn_vwp)(UINT; CPoint);
void (AFX_MSG_CALL CWnd::*pfn_vwwh)(UINT; UINT; HANDLE);
};
其实呢,真正的函数只有一个pfn ,但通过union,它有许多类型不同的形象。pfn _vv 代
表「参数为void ,传回值为void 」;pfn _lwl 代表「参数为wParam 和lParam,传回
值为LRESULT 」;pfn _is 代表「参数为LPTSTR 字符串,传回值为int」。
相当精致,但是也有点儿可怖,是不是?使用MFC 或许应该像吃蜜饯一样; 蜜饯很好
吃,但你最好不要看到蜜饯的生产过程!唔,我真的不知道!
无论如何,我把所有的神秘都揭开在你面前了。
山高月小 水落石出
584
…………………………………………………………Page 647……………………………………………………………
第9章 訊息映射與命令繞行
Scribble Step2 :UI 对象的变化
理论基础建立完毕,该是实作的时候。Step2 将新增三个菜单命令项,一个工具栏按钮,
并维护这些UI 对象的使用状态。
改变菜单
Step2 将增加一个【Pen 】菜单,其中有两个命令项目;并在【Edit 】菜单中增加一个【Clear
All 】命令项目:
【Pen/Thick Line 】:这是一个切换开关,允许设定使用粗笔或细笔。如果使用
者设定粗笔,我们将在这项目的旁边打个勾(所谓的checked );如果使用者
选择细笔(也就是在打勾的本项目上再按一下),我们就把勾号去除(所谓的
unchecked )。
【Pen/Pen Widths 】:这会唤起一个对话框,允许设定笔的宽度。对话框的设计
并不在本章范围,那是下一章的事。
【Edit/Clear All 】:清除目前作用之Document 资料。当然对应之View 窗口
内容也应该清干净。
Visual C++ 整合环境中的菜单编辑器拥有非常方便的鼠标拖放(drag and drop )功能,所
以做出上述的菜单命令项不是难事。不过这些命令项目还得经过某些动作,才能与程序
码关联起来发生作用,这方面ClassWizard 可以帮助我们。稍后我会说明这一切。
以下利用Visual C++ 整合环境中的菜单编辑器修改菜单:
585
…………………………………………………………Page 648……………………………………………………………
第篇 深入 MFC 程式設計
激活菜单编辑器(请参考第4章)。Scribble 有两份菜单,IDR_MAINFRAME
适用于没有任何子窗口的情况,IDR_SCRIBTYPE 适用于有子窗口的情况。我
们选择后者。
IDR_SCRIBTYPE 菜单内容出现于画面右半侧。加入新增的三个命令项。每个
命令项会获得一个独一无二的识别码,定义于RESOURCE。H 或任何你指定的
文件中。图下方的【Menu Item Properties 】对话框在你双击某个命令项后出现,
允许你更改命令项的识别码与提示字符串(将出现在状态列中)。如果你对操作
过程不熟练,请参考Visual C++ User's Guide (Visual C++ Online 上附有此书之
电子版)。
三个新命令项的ID 值以及提示字符串整理于下:
【 】
Pen/Thick Line
ID : ID_PEN_THICK_OR_THIN
prompt : 〃Toggles the line thickness between thin and thicknToggle pen〃
586
…………………………………………………………Page 649……………………………………………………………
第9章 訊息映射與命令繞行
【 】
Pen/Pen Widths
ID : ID_PEN_WIDTHS
prompt : 〃Sets the size of the thin and thick pennPen thickness〃
【 】
Edit/Clear All
ID : ID_EDIT_CLEAR_ALL (这是一个预先定义的ID,有预设的提示字符串,请更改如下)
prompt : 〃Clears the drawingnErase All〃
注意:每一个提示字符串都有一个n 子字符串,那是作为工具栏按钮的「小黄卷标」
的卷标内容。「小黄卷标」(学名叫作tool tips)是Windows 95 新增的功能。
对Framework 而言,命令项的ID 是用以识别命令消息的唯一依据。你只需在【Properties 】
对话框中键入你喜欢的ID 名称(如果你不满意菜单编辑器自动给你的那个),至于它真
正的数值不必在意,菜单编辑器会在你的RESOURCE。H 档中加上定义值。
经过上述动作,菜单编辑器影响我们的程序代码如下:
// in RESOURCE。H
#define ID_PEN_THICK_OR_THIN 32772
#define ID_PEN_WIDTHS 32773
(注:另一个ID ID_EDIT_CLEAR_ALL 已预先定义于AFXRES。H 中)
// in SCRIBBLE。RC
IDR_SCRIBBTYPE MENU PRELOAD DISCARDABLE
BEGIN
。。。
POPUP 〃&Edit〃
BEGIN
。。。
MENUITEM 〃Clear &All〃;
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!