按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
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