八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第127部分

深入浅出MFC第2版(PDF格式)-第127部分

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

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






    窗口的产生导至WM_PAINT 产生,于是OnDraw 发生效用,把文件内容画出来: 



         图11…2 一份Document 连结两个Views , 没有同步修正画面。 



    但是, 此后如果你在Scrib1:1 窗口上绘图而未缩放其尺寸的话(也就是不产生 



     WM_PAINT ),Scrib1:2 窗口内看不到后续绘图内容。我们并不希望如此,不幸的是上 



    一章的Scribble Step3 正是如此。 



    不能同步更新的关键在于,没有人通知所有的兄弟们(Views )一起动手…动手调用 



    OnDraw 。你是知道的,只有当WM_PAINT 产生,OnDraw 才会被调用。因此,解决方 



    式是对每一个兄弟都发出WM_PAINT ,或任何其它方法…只要能通知到就好。也就是 



    说,让附属于同一Document  的所有Views 都能够立即反应Document  内容变化的方法 



    就是,始作俑者(被使用者用来修改Document  内容的那个View )必须想办法通知其 



    他兄弟。 



                                                                   629 


…………………………………………………………Page 692……………………………………………………………

                 第篇    深入  MFC  程式設計 



                 经由CDocument::UpdateAllViews,MFC 提供了这样的一个标准机制。 



                 让所有的Views  同步更新资料的关键在于两个函数: 



                   1。 CDocument::UpdateAllViews 这个函数会巡访所有隶属同一份Document  的各 



                    个Views ,找到一个就通知一个,而所谓「通知」就是调用其OnUpdate 函数。 



                  2。 CView::OnUpdate 我们可以在这个函数中设计绘图动作。或许是全部重绘(这 



                     比较笨一点),或许想办法只绘必要的一小部份(这比较聪明一些)。 



                                              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 一一被调用起来,这是更新画面的时机。 



                如果想让绘图程序聪明一些,不要每次都全部重绘,而是只择「必须重绘」的区域重绘, 



                那么OnUpdate 需要被提示什么是「必须重绘的区域」,这就必须借助于UpdateAllViews 



                的参数: 



                 virtual void UpdateAllViews(CView* pSender; 

                                                  LPARAM lHint; 

                                                  CObject* pHint); 



                  

                    第一个参数代表发出此一通牒的始作俑者。这个参数的设计无非是希望避免重 



                  复而无谓的通牒,因为始作俑者自己已经把画面更新过了(在鼠标消息处理常 



                  式中),不需要再被通知。 



                   



630 


…………………………………………………………Page 693……………………………………………………………

                                      第 11 章    View 功能之加強與重繪效率之提昇 



     ■后面两个参数lHint 和pHint  是所谓的提示参数(Hint ),它们会被传送到同 



      一Document 所对应的每一个Views  的OnUpdate 函数去。lHint 可以是一些 



      特殊的提示值,pHint  则是一个衍生自CObject 的对象指针。靠着设计良好的 



       「提示」,OnUpdate 才有机会提高绘图效率。要不然直接通知OnDraw 就好 



      了,也不需要再搞出一个OnUpdate。 



    另一方面,OnUpdate 收到三个参数(由CDocument:: UpdateAllViews 发出): 



       virtual void OnUpdate(CView* pSender; 



                            LPARAM lHint; 



                            CObject* pHint); 



    因此,一旦Document 资料改变,我们应该调用CDocument::UpdateAllViews  以通知所有 



    相关的Views 。而在CMyView::OnUpdate 函数中我们应该以效率为第一考量,利用参数 



    中的hint 设定重绘区,使后续被唤起的OnDraw 有最快的工作速度。注意,通常你不 



    应该在OnUpdate 中执行绘图动作,所有的绘图动作最好都应该集中在OnDraw;你在 



    OnUpdate 函数中的行为应该是计算哪一块区域需要重绘, 然后调用 



    CWnd::InvalidateRect ,发出WM_PAINT 让OnDraw 去画图。 



    结论是,改善同步更新以及绘图效率的前置工作如下: 



     1。 定义hint  的数据类型,用以描述已遭修改的资料区域。 



     2。 当使用者透过View 改变了Document  内容,程序应该产生一个hint ,描述此 



       一修改,并以它做为参数,调用UpdateAllViews。 



     3。 改写CMyView::OnUpdate,利用hint 设计高效率绘图动作,使hint 描述区之 



       外的区域不要重画。 



在View 中定义一个hint 



    以Scribble 为例,当使用者加上一段线条,如果我们计算出包围此一线条之最小四方 



    形,那么只有与此四方形有交集的其它线条才需要重画,如图11…3。因此在Step4 中 



    把hint 设计为RECT 类型,差堪为用。 



                                                                        631 


…………………………………………………………Page 694……………………………………………………………

                第篇    深入  MFC  程式設計 



                效率考量上,当然我们还可以精益求精取得各线条与此四方形的交点,然后只重绘四方 



                形内部的那一部份即可,但这么做是否动用太多计算,是否使工程太过复杂以至于不划 



                算,你可得谨慎评估。 



                                 #5 



                            #1 



                         #2             #4 



                               #3 



                  图11…3 在rect。SCB:1 窗口中新增一线条#5 ,那么,只有与虚线四方形 (此 



                         四方形将#5 包起来) 有交集之其它线条, 也就是#1 和#4 , 才 



                         有必要在rectSCB:2 窗口中重画。 



                前面曾说UpdateAllViews 函数的第三个参数必须是CObject 衍生对象之指针。由于本例 



                十分单纯,与其为了Hint 特别再设计一个类别,勿宁在CStroke 中增加一个变量(事 



                实上是一个CRect 对象),用以表示前述之hint  四方形,那么每一条线条就外罩了一 



                个小小的四方壳。但是我们不能把CRect 对象指针直接当做参数来传,因为CRect 并 



                不衍生自CObject。稍后我会说明该怎么做。 



                可以预期的是,日后一定需要一一从每一线条中取出这个「外围四方形」,所以现在先 



                声明并定义一个名为GetBoundingRect 的函数。另外再声明一个FinishStroke  函数,其 



                作用主要是计算这四方形尺寸。稍后我会详细解释这些函数的行为。 



632 


…………………………………………………………Page 695……………………………………………………………

                                            第 11 章    View 功能之加強與重繪效率之提昇 



// in SCRIBBLEDOC。H 

class CStroke : public CObject 

 { 

 。。。 

public: 

        UINT      m_nPenWidth; 

        CDWordArray m_pointArray; 

        CRect     m_rectBounding; // smallest rect that surrounds all 

                                     // of the points in the stroke 

public: 

        CRect& GetBoundingRect() { return m_rectBounding; } 

        void FinishStroke(); 

 。。。 

 }; 



我想你早已熟悉了Scribble Document 的数据结构。为了应付Step4 的需要,现在每一 



线条必须多一个成员变量,那是一个CRect 对象,如图11…4 所示。 



  CScribble Step4 Document :: 

                             :: 



                               CObList 对象 



                                        CObList 的每个元素都是一个CObject 指针。 



                                        我们令它指向CStroke 对象。CStroke 衍生自 

 文件内含一个COblist 串行                       CObject。 



   CObList  m_strokeList 

    CObList  m_strokeList 



                                                        包围整个线条的 

                                                        “外围四方形” 



          UINT m_nPenWidth                          CArray 对象 

           (代表笔的宽度) 

                                                     (线条可以CPoint 数组表示) 



                 图11…4 CScribbleDoc 对象中的各项资料 



                                                                                        633 


…………………………………………………………Page 696……………………………………………………………

                    第篇    深入  MFC  程式設計 



                    设计观念分析完毕,现在动手吧。我们必须在SCRIBDOC。CPP 中的Document 初始化 



                    动作以及文件读写动作都加入m_rectBounding 这个新成员: 



                    // in SCRIBDOC。CPP 

                    IMPLEMENT_SERIAL(CStroke; CObject; 2)  //  

                                                                   注意schema no。 改变为2 



                    CStroke::CStroke(UINT nPenWidth) 

                     { 

                            m_nPenWidth = nPenWidth; 

                            m_rectBounding。SetRectEmpty(); 

                     } 



                    void CStroke::Serialize(CArchive& ar) 

                     { 

                            if (ar。IsStoring()) 

                            { 

                                    ar  m_rectBounding; 

                                    WORD w; 

                                    ar 》》 w; 

                                    m_nPenWidth = w; 

                                    m_pointArray。Serialize(ar); 

                            } 

                     } 



                    如果我们改变了文件读写的格式,我们就应该改变schema number  (可视为版本号码)。 



                    由于Scribble 资料文件(。SCB)格式改变了,多了一个m_rectBounding ,所以我们在 



                    IMPLEMENT_SERIAL 宏中改变Document  的Schema no。 , 以便让不同版本的 



                    Scribble 程序识得不同版本的文件档。如果你以Scribble Step3 读取Step4 所产生的文 



                    件,会因为Schema 号码的不同而得到这样的消息: 



634 


…………………………………………………………Page 697……………………………………………………………

                                                第 11 章    View 功能之加強與重繪效率之提昇 



     我们还需要一个函数,用

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的