友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!





式的绘图结果都会受它的影响。如果我们想在绘图之前(也就是进入OnDraw 之前)调 



整DC  , 我们可以改写虚拟函数OnPrepareDC , 因为Framework 是先调用 



OnPrepareDC,然后才调用OnDraw 并把DC 传进去。好,窗口由无滚动条到增设滚动条的 



过程中,之所以不必修改OnDraw 函数内容,就是因为CScrollView  已经改写了CView 



的OnPrepareDC 虚拟函数。Framework 先调用CScrollView::OnPrepareDC 再调用 



CScribbleView::OnDraw,所有因为滚动条而必须做的特别处理都已经在进入OnDraw 之前 



完成了。 



注意上面的叙述,别把CScrollView 和CSribbleView 混淆了。下图是个整理。 



                        CWnd 

                         CWnd 



                        CView 

                         CView 



                    x    CScrollView  此类别的OnPrepareDC 虚拟函数 

                          CScrollView 

                                      会因滚动条的位置而调整DC 原点。 



                  CScribbleView 此类别原针对「无滚动条窗口」而设计,;所以Step4 之前 

                   CScribbleView 



                                的View 类别是直接衍生自CView。彼时所写的OnDraw 



                                函数内容在如今改变了继承关系后(改继承自 



                                CScrollView),依然完全适用,原因是所有的差异性早 



                                都在进入OnDraw 之前便由更早被Framework 调用的 



                                CScrollView::OnPrepare 处理掉了。 



 DC 就是Device Context ,在Windows  中凡绘图动作之前一定要先获得一个DC ,它可 



  能代表屏幕,也可能代表一个窗口,或一块内存,或打印机。。。。DC  中有许多绘图所需 



  的元素,包括坐标系统(映射模式)、原点、绘图工具(笔、刷、颜色。。。)等等。它还 



 连接到低阶的输出装置驱动程序。由于DC ,我们在程序中对屏幕作画和对打印机作画 



  的动作才有可能完全相同。 



                                                                   645 


…………………………………………………………Page 708……………………………………………………………

                   第篇    深入  MFC  程式設計 



                   4 修正鼠标坐标。虽说OnDraw 不必因为坐标原点的变化而有任何改变,但是幕后出 



                   力的CScrollView::OnPrepareDC 却不知道什么是Windows 消息!这话看似牛头和马 



                   嘴,但我一点你就明白了。CScrollView::OnPrepareDC 虽然能够因着滚动条行为而改变GDI 



                   原点,但「改变GDI 原点」这个动作却不会影响你所接收的WM_LBUTTONDOWN 或 



                   WM_LBUTTONUP  或WM_MOUSEMOVE  的坐标值,原因是Windows 消息并非DC  的 



                   一个成份。因此,我们作画的基础,也就是鼠标移动产生的轨迹点坐标,必须由「以视 



                   窗绘图区左上角为原点」的窗口坐标系统,改变为「以文件左上角为原点」的逻辑坐标 



                   系统。文件中储存的,也应该是逻辑坐标。 



                   下面是修改坐标的程序动作。其中调用的OnPrepareDC 是哪一个类别的成员函数?想 



                   想看,CScribbleView 衍生自CScrollView,而我们并未在CScribbleView 中改写此一函 



                   式,所以程序中调用的是CScrollView::OnPrepareDC。 



                   // in SCRIBVW。CPP 

                   void CScribbleView::OnLButtonDown(UINT; CPoint point) 

                   { 

                           //由于CScrollView 改变了DC 原点和映射模式,所以我们必须先把 

                           //装置坐标转换为逻辑坐标,再储存到Document 中。 

                           CClientDC dc(this); 

                           OnPrepareDC(&dc); 

                           dc。DPtoLP(&point); 



                           m_pStrokeCur = GetDocument()…》NewStroke(); 

                           m_pStrokeCur…》m_pointArray。Add(point); 



                           SetCapture();       // 抓住鼠标 

                           m_ptPrev = point;   //做为直线绘图的第一个点 



                           return; 

                   } 

                   void CScribbleView::OnLButtonUp(UINT; CPoint point) 

                   { 

                           。。。 

                           if (GetCapture() != this) 

                                   return; 



                           CScribbleDoc* pDoc = GetDocument(); 



                           CClientDC dc(this); 



646 


…………………………………………………………Page 709……………………………………………………………

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



        OnPrepareDC(&dc);  // 设定映射模式和DC 原点 

        dc。DPtoLP(&point); 

        。。。 

 } 

void CScribbleView::OnMouseMove(UINT; CPoint point) 

 { 

        。。。 

        if (GetCapture() != this) 

                return; 



        CClientDC dc(this); 

        OnPrepareDC(&dc); 

        dc。DPtoLP(&point); 



        m_pStrokeCur…》m_pointArray。Add(point); 

        。。。 

 } 



除了上面三个函数,还有什么函数牵扯到坐标?是的,线条四周有一个外围四方形,那 



将在OnUpdate 中出现,也必须做坐标系统转换: 



void CScribbleView::OnUpdate(CView* /* pSender */; LPARAM /* lHint */; 

        CObject* pHint) 

 { 

    if (pHint != NULL) 

    { 

        if  (pHint…》IsKindOf(RUNTIME_CLASS(CStroke))) 

        { 

            // hint 的确是一个CStroke 对象。现在将其外围四方形设为重绘区 

            CStroke* pStroke =  (CStroke*)pHint; 

            CClientDC dc(this); 

            OnPrepareDC(&dc); 

            CRect rectInvalid = pStroke…》GetBoundingRect(); 

            dc。LPtoDP(&rectInvalid); 

            InvalidateRect(&rectInvalid); 

            return; 

        } 

    } 

    //无法识别hint;只好假设整个画面都需重绘。 

    Invalidate(TRUE); 

    return; 

 } 



                                                                                           647 


…………………………………………………………Page 710……………………………………………………………

                   第篇    深入  MFC  程式設計 



                   注意,上面的LPtoDP 所受参数竟然不是CPoint*,而是CRect*,那是因为LPtoDP 有 



                   多载函数(overloaded function ),既可接受点,也可接受四方形。DPtoLP  也有类似的多 



                   载能力。 



                   线条的外围四方形还可能出现在CStroke::FinishStroke  中,不过那里只是把线条数组中 



                   的点拿出来比大小,决定外围四方形罢了;而你知道,线条数组的点已经在加入时做过 



                   坐标转换了(分别在OnLButtonDown、OnMouseMove、OnLButtonUp 函数中的AddPoint 



                   动作之前)。 



                   至此,Document  的资料格式比起Step1,有了大幅的变动。让我们再次分析文件档的格 



                   式,以期获得更深入的认识与印证。我以图11…6a 为例,共四条线段,宽度分别是2; 5; 



                   10; 20  (十进制)。分析内容显示在图11…6b。 



                    CArchive::WriteObject 



                                                                          CArchive::ReadObject 



                     Turbo Dump  Version 3。1 Copyright (c) 1988; 1992 Borland International 

                                       Display of File PENWIDTH。SCB 



                     000000: 20 03 00 00 84 03 00 00  04 00 FF FF 02 00 07 00  。。。。。。。。。。。。。。。 

                     000010: 43 53 74 72 6F 6B 65 28  00 00 00 15 00 00 00 2C CStroke(。。。。。。。; 

                     000020: 00 00 00 19 00 00 00 02  00 02 00 2A 00 00 00 17 。。。。。。。。。。。*。。。。 

                     000030: 00 00 00 2A 00 00 00 17  00 00 00 01 80 24 00 00 。。。*。。。。。。。。。。。 

                     000040: 00 26 00 00 00 2E 00 00  00 30 00 00 00 05 00 02 。&。。。。。。。0。。。。。。 

                     000050: 00 29 00 00 00 2B 00 00  00 29 00 00 00 2B 00 00 。)。。。+。。。)。。。+。。 

                     000060: 00 01 80 1F 00 00 00 3D  00 00 00 33 00 00 00 51 。。。。。。。=。。。3。。。Q 

                     000070: 00 00 00 0A 00 02 00 29  00 00 00 47 00 00 00 29 。。。。。。。)。。。G。。。) 

                     000080: 00 00 00 47 00 00 00 01  80 15 00 00 00 54 00 00 。。。G。。。。。。。。。T。。 

                     000090: 00 3D 00 00 00 7C 00 00  00 14 00 02 00 29 00 00 。=。。。|。。。。。。。)。。 

                     0000A0: 00 68 00 00 00 29 00 00  00 68 00 00 00 00 00 00 。h。。。)。。。h。。。。。。 



                                图11…6a 四条线段的图形与文件文件倾印码。 



648 


…………………………………………………………Page 711……………………………………………………………

数值(hex)                说明(共173 bytes ) 



00000320                           Document 宽度(800 ) 



00000384                           Document 高度(900 ) 



0004                                       表示此文件有四个CObList 元素 



FFFF                                      FFFF 亦即…1,表示New Class Tag 



0002                                        Scheme no。,代表Document 版本号码 



0007                                       表示后面接着的「类别名称」有7 个字符 



43 53 74 72 6F 6B 65            类别名称〃CStroke〃 的ASCII 码 



00000028 00000015        外围四方形的左上角坐标(膨胀一个笔宽) 



0000002C 00000019             外围四方形的右下角坐标(膨胀一个笔宽) 



0002                  第一条线条的宽度 



0002                  第一条线条的点数 



0000002A;00000017     第一条线条的第一个点坐标 



0000002A;00000017     第一条线条的第二个点坐标 



8001                  表示接下来的对象仍旧使用旧的类别 



00000024 00000026     外围四方形的左上角坐标(膨胀一个笔宽) 



0000002E 00000030     外围四方形的右下角坐标(膨胀一个笔宽) 



0005                  第二条线条的宽度 



0002                  第二条线条的点数 



00000029;0000002B     第二条线条的第一个点坐标 



00000029;0000002B     第二条线条的第二个点坐标 



8001                 表示接下来的对象仍旧使用旧的类别 



0000001F 0000003D     外围四方形的左上角坐标(膨胀一个笔宽) 



00000033 00000051     外围四方形的右下角坐标(膨胀一个笔宽) 



000A                  第三条线条的宽度 



0002                  第三条线条的点数 



00000029;00000047     第三条线条的第一个点坐标 



00000029;00000047     第三条线条的第二个点坐标 



                                                                                649 


…………………………………………………………Page 712……………………………………………………………

              第篇    深入  MFC  程式設計 



               数值(hex)               说明(共173 bytes ) 



               8001            表示接下来的对象仍旧使用旧的类别 



               00000015 00000054  外围四方形的左上角坐标(膨胀一个笔宽) 



               0000003D 0000007C  外围四方形的右下角坐标(膨胀一个笔宽) 



               0014            第四条线条的宽度 



               0002            第四条线条的点数 



               00000029 00000068  第四条线条的第一个点坐标 



               00000029 00000068  第四条线条的第二个点坐标 



                         图11…6b 文件档 (  图11…6a ) 的分析 



        大窗口中的小窗口:Splitter 



              MDI 程序的标准功能是允许你为同一份Document 开启一个以上的Views 。这种情况类 



              似我们以多个观景窗观看同一份资料。我们可以开启任意多个Views ,各有滚动条,那么 



              我们就可以在屏幕上同时观察一份资料的不同区域。这许多个View 窗口各自独立运 



              作,因此它们的观看区可能互相重叠。 



              如果这些隶属同一Document  的Views 能够结合在一个大窗口之内,又各自有独立的行 



              为(譬如说有自己的滚动条),似乎可以带给使用者更好的感觉和更方便的使用,不是吗? 



          分裂窗口的功能 



              把View 做成所谓的「分裂窗口(splitter )」是一种不错的想法。这种窗口可以分裂出 



              数个窗口,如图11…7,每一个窗口可以映射到Document  的任何位置,窗口与窗口之间 



              彼此独立运作。 



650 


…………………………………………………………Page 713……………………………………………………………

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



        splitter box 



         在Splitter Box 上以鼠标左键快按两下,就可以将窗口分裂开来。Splitter Box 有 



         水平和垂直两种。分裂窗口的窗口个数,由程序而定,本例是2x2。不同的窗口可 



         以观察同一份Document 的不同区域。本例虽然很巧妙地安排出一张完整的图出 



         来,其实四个窗口各自看到原图的某一部份。 



                       图11…7 分裂窗口 (splitter window) 



      在Splitter Box 上以鼠标左键快按两下,就可以将窗口分裂开来。Splitter Box 有水平和 



      垂直两种。分裂窗口的窗口个数,由程序而定,本例是2x2 。不同的窗口可以观察同一 



      份Document  的不同区域。本例虽然很巧妙地安排出一张完整的图出来,其实四个窗口 



      各自看到原图的某一部份。 



分裂窗口的程序概念 



      回忆第8章所说的Document/View 架构,每次打开一个Document ,需有两个窗口通力 



      合作才能完成显示任务,一是CMDIChildWnd 窗口,负责窗口的外框架与一般行为,一 



      是CView 窗口,负责资料的显示。但是当分裂窗口引入,这种结构被打破。现在必须有 



      三个窗口通力合作完成显示任务(图11…8): 



        1。 Document Frame 窗口:负责一般性窗口行为。其类别衍生自CMDIChildWnd。 



        2。 Splitter 窗口:负责管理各窗口。通常直接使用CSplitterWnd 类别。 



        3。 View 窗口:负责资料的显示。其类别衍生自CView。 



                                                                       651 


…………………………………………………………Page 714……………………………………………………………

                 第篇    深入  MFC  程式設計 



                                                                          splitter 窗口, 

                                                                          采用CSplitterWnd. 



                                                                            View 窗口, 



                                                                            通常采用CView. 



                                                                             Document Frame 窗口, 

                                                                             采用CMDIChildWnd. 



                       图11…8 欲使用分裂窗口, 必须三个对象合作才能完成显示任务, 一是 



                       Document Frame 窗口, 负责一般性窗口行为; 二是 



                       CSplitterWnd 窗口,管理窗口内部空间 ( 各个窗口);三是CView 



                       窗口, 负责显示资料。 



                                          给SDK 程序员 



                         你有以SDK 撰写MDI 程序的经验吗?MDI 程序有三层窗口架构: 



                            MDI Frame   在SDK 程序中,MDI Frame  窗口的消息预设处理 



                                        函数是DefFrameProc ,而不是DefWindowProc 。 



                                      MDI Frame窗口发出MDI 消息(如WM_MDICASCADE 、 

                                         _ 

                                       WM  MDITILE ),命令MDI Client 窗口管理其子窗口 



                                       (管理动作包括窗口产生、位置排列等等) 。 



                            MDI Client  MDI Client 是Windows 预设好的窗口类别,名为 



                                        〃MDIClient〃 。你也可以把它视为一种控制组件。 



                                            在SDK 程序中,MDI Child  窗口的消息预设处理函数 



                                            是DefMDIChildProc() ,而不是Def
返回目录 上一页 下一页 回到顶部 9 10
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!