友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第102部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
数值(hex) 说明
00000066;00000031 第二条线条的第一个点坐标
00000066;00000031 第二条线条的第二个点坐标
8003 这是(wOldClassTag | nClassIndex) 的组合结果,表示接
下来的对象使用索引为3 的旧类别。
00000011 第二个四方形的左
00000022 第二个四方形的上
00000033 第二个四方形的右
00000044 第二个四方形的下
8005 这是(wOldClassTag | nClassIndex) 的组合结果,表示接
下来的对象使用索引为 的旧类别。
5
00000055 第二个圆形的中心点X 坐标
00000066 第二个圆形的中心点Y 坐标
00000077 第二个圆形的半径
图8…10b TEST。SCB 文件内容剖析。别忘了Intel 采用〃little…endian〃 位
组排列方式,每一字组的前后字节系颠倒放置。本图已将之摆正。
Document 与View 交流为Step4 做准备
虽然Scribble Step1 已经可以正常工作,有些地方仍值得改进。
在一个子窗口上作画,然后选按【Window/New Window 】,会蹦出一个新的子窗口,内
有第一个子窗口的图形,同时,第一个子窗口的标题加上:1 字样,第二个子窗口的标
题则有:2 字样。这是Document/View 架构带给我们的礼物,换句话说,想以多个窗口
观察同一份资料,程序员不必负担什么任务。但是,如果此后使用者在其中一个子窗口
上作画而不缩放窗口尺寸的话(也就是没有产生WM_PAINT),另一个子窗口内看不到
新的绘图内容:
543
…………………………………………………………Page 606……………………………………………………………
第篇 深入 MFC 程式設計
这不是好现象!一体的两面怎么可以不一致呢?!
那么,让「作用中的View 窗口」以消息通知隶属同一份Document 的其它「兄弟窗口」,
是不是就可以解决这个问题?是的,而且Framework 已经把这样的机制埋伏下去了。
CView 之中的三个虚拟函数:
CView::OnInitialUpdate 负责View 的初始化。
CView::OnUpdate 当Framework 调用此函数,表示Document 的内容已有变化。
CView::OnDraw Framework 将在WM_PAINT 发生后,调用此函数。此函数应
负责更新View 窗口的内容。
这些函数往往成为程序员改写的目标。Scribble 第一版就是因为只改写了其中的OnDraw
函数,所以才有「多个View 窗口不能同步更新」的缺失。想要改善这项缺失,我们必
须改写OnUpdate。
让所有的View 窗口「同步」更新资料的关键在于两个函数:
CDocument::UpdateAllViews 如果这个函数执行起来,它会巡访所有隶属同一
Document 的各个Views ,找到一个就通知一个,而所谓「通知」就是调用View
的OnUpdate 函数。
544
…………………………………………………………Page 607……………………………………………………………
第8章 Document…View 深入探討
CView::OnUpdate 这是一个虚拟函数,我们可以改写它,在其中设计绘图动作,
也许全部重绘(这比较笨一点),也许想办法只绘必要的一小部份(这样速度
比较快,但设计上比较复杂些)。
因此,当一个Document 的资料改变时,我们应该设法调用其UpdateAllViews,通知所
有的Views 。什么时候Scribble 的资料会改变?答案是鼠标左键按下时! 所以你可能猜
测到,我打算在CView::OnLButtonDown 内调用CDocument::UpdateAllViews。这个猜测
的立论点是对的而结果是错的,Scribble Step4 的作法是在CView::OnButtonUp 内部调用
它。
CView::OnUpdate 被调用,代表着View 被告知:「嘿,Document 的内容已经改变了,
请你准备修改你的显示画面」。如果你想节省力气,利用Invalidate(TRUE) 把窗口整个
设为重绘区(无效区)并产生WM_PAINT,再让CView::OnDraw 去伤脑筋算了。但是
全部重绘的效率低落,程序看起来很笨拙,Step4 将有比较精致的作法。
Document
。
View:1 View:2 View:3
o1 使用者在View:1 做动作(View 扮演使用者接口的第一线)。
o2 View:1 调用GetDocument ,取得Document 指针,更改资料内容。
o3 View:1 调用Document 的UpdateAllViews
o4 View:2 和View:3 的OnUpdate 一一被调用起来,这是更新画面的时机。
图8…11 假设一份Document 联结了三个Views
注意:在MFC 手册或其它书籍中,你可能会看到像「View1 以消息通知Document 」
或「Document 以消息通知View2、View3 」的说法。这里所谓的「消息」是对象导向学
术界的术语,不要和Windows 的消息混淆了。事实上整个过程中并没有任何一个Windows
消息参与其中。
545
…………………………………………………………Page 608……………………………………………………………
第篇 深入 MFC 程式設計
546
…………………………………………………………Page 609……………………………………………………………
第9章
消息映射与命令绕行
Message Mapping and mand Routing
消息映射机制与命令绕行,活像是米诺托斯的迷宫,
是MFC 最曲折幽深的神秘地带。
你已经从前一章中彻底了解了MFC 程序极端重要的Document/View 架构。本章的重点
有两个,第一个是修改程序的人机接口,增添菜单项目和工具栏按钮。这一部份藉Visual
C++ 工具之助,非常简单,但是我们往往不知道该在程序的什么地方(哪一个类别之中)
处理来自菜单和工具栏的消息(也就是WM_MAND 消息)。本章第二个重点就是
要解决这个迷惑,我将对所谓的消息映射(Message Map )和命令绕行(mand Routing)
机制做深入的讨论。这两个机制宛如MFC 最曲折幽深的神秘地带,是把杂乱无章的
Windows API 函数和Windows 消息对象导向化的大功臣。
到底要解决什么
Windows 程序的本质系借着消息来维持脉动。每一个消息都有一个代码,并以WM_ 开
头的常数表示之。消息在传统SDK 程序方法中的流动以及处置方式,在第1章已经交
待得很清楚。
各种消息之中,来自菜单或工具栏者,都以WM_MAND 表示,所以这一类消息我
们又称之为命令消息(mand Message ),其wParam 记录着此一消息来自哪一个菜单
项目。
547
…………………………………………………………Page 610……………………………………………………………
第篇 深入 MFC 程式設計
除了命令消息,还有一种消息也比较特殊,出现在对话框函数中,是控制组件(controls )
传送给父窗口(即对话框)的消息。虽然它们也是以WM_MAND 为外衣,但特别
归类为「notification 消息」。
注意:Windows 95 新的控制组件(所谓的mon controls )不再传送WM_MAND
消息给对话框,而是送WM_NOTIFY 。这样就不会纠缠不清了。但为了回溯兼容,旧有
的控制组件(如edit 、list box、bo box。。。 )都还是传送WM_MAND。
消息会循着Application Framework 规定的路线,游走于各个对象之间,直到找到它的依
归(消息处理函数)。找不到的话,Framework 最终就把它交给::DefWindowProc 函数
去处理。
但愿你记忆犹新,第6章曾经挖掘MFC 源代码,得知MFC 在为我们产生窗口之前,
如果我所指定的窗口类别是NULL ,MFC 会自动先注册一个适当的窗口类别。这个类别
在动态联结、除错版、非Unicode 环境的情况下,可能是下列五种窗口类别之一:
〃AfxWnd42d〃
〃AfxControlBar42d〃
〃AfxMDIFrame42d〃
〃AfxFrameOrView42d〃
! AfxOleControl42d!§ ¨
每一个窗口类别有它自己的窗口函数。根据SDK 的基础,我们推想,不同窗口所获得
的消息,应该由不同的窗口函数来处理。如果都没有能够处理,最后再交由Windows API
函数::DefWindowProc 处理。
这是很直觉的想法,而且对于一般消息(如WM_MOVE 、WM_SIZE、WM_CREATE 等)
也是天经地义的。但是今天Application Framework 比传统的SDK 程序多出了一个
Document/View 架构,试想,如果菜单上有个命令项关乎文件的处理,那么让这个命令
548
…………………………………………………………Page 611……………………………………………………………
第9章 訊息映射與命令繞行
消息流到Document 类别去不是最理想吗?一旦流入Document 大本营,我们(程序员)
就可以很方便地取得Document 成员变量、调用Document 成员函数,做爱做的事。
但是Document 不是窗口,也没有对应的窗口类别,怎么让消息能够七拐八弯地流往
Document 类别去?甚至更往上流向Application 类别去?这就是所谓的命令绕行机制!
而为了让消息的流动有线路可循,MFC 必须做出一个巨大的网,实现所有可能的路线,
这个网就是所谓的消息映射地图(Message map )。最后,MFC 还得实现一个消息推动
引擎,让消息依Framework 的意旨前进,该拐的时候拐,该弯的时候弯,这个邦浦机制
埋藏在各个类别的WindowProc、Onmand、OnCmdMsg、DefWindowProc 虚拟函数
中。
没有命令绕行机制,Document/View 架构就像断了条胳臂,会少掉许多功用。
很快你就会看到所有的秘密。很快地,它们统统不再对你构成神秘。
消息分类
Windows 的消息都是以WM_xxx 为名,WM_ 的意思是〃Windows Message〃。消息可以
是来自硬件的「输入消息」,例如WM_LBUTTONDOWN ,也可以是来自USER 模块的
「窗口管理消息」,例如WM_CREATE。这些消息在MFC 程序中都是隐晦的(我的意
思是不像在SDK 程序中那般显明),我们不必在MFC 程序中撰写switch case 指令,
不必一一识别并处理由系统送过来的消息;所有消息都将依循Framework 制定的路线,
并参照路中是否有拦路虎(你的消息映射表格)而流动。WM_PAINT 一定流往你的
OnPaint 函数去,WM_SIZE 一定流往你的OnSize 函数去。
所有的消息在MFC 程序中都是暗潮汹涌,但是表面无波。
MFC 把消息分为三大类:
■ 命令消息(WM_MAND):命令消息意味着「使用者命令程序做某些动作」。
凡由UI 对象产生的消息都是这种命令消息,可能来自菜单或加速键或工具栏
549
…………………………………………………………Page 612……………………………………………………………
第篇 深入 MFC 程式設計
按钮,并且都以WM_MAND 呈现。如何分辨来自各处的命令消息?SDK
程序主要靠消息的wParam 辨识之,MFC 程序则主要靠菜单项目的识别码
(menu ID )辨识之…两者其实是相同的。
什么样的类别有资格接受命令消息?凡衍生自CCmdTarget 之类别,皆有资格。从
mand target 的字面意义可知,这是命令消息的目的地。也就是说,凡衍生自
CCmdTarget 者,它的骨子里就有了一种特殊的机制。把整张MFC 类别阶层图摊开
来看,几乎构造应用程序的最重要的几个类别都衍生自CCmdTarget,剩下的不能接
收消息的,是像CFile、CArchive、CPoint、CDao (数据库)、Collection Classes (纯
粹数据处理)、GDI
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!