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

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

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





                                            是DefMDIChildProc() ,而不是DefWindowProc() 。 



                MDI Child1  MDI Child2 MDI Child3 



652 


…………………………………………………………Page 715……………………………………………………………

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



        程序员想要控制MDI Child 窗口的大小、位置、排列状态,必须藉助另一个已经由 



        Windows 系统定义好的窗口, 此窗口称为MDI Client 窗口, 其类别名称为 



        〃MDICLIENT〃。 



        Frame 窗口、Client 窗口和Child 窗口构成MDI  的三层架构。Frame 窗口产生之后, 



        通常在WM_CREATE 时机就以CreateWindow(〃MDICLIENT 〃;。。。); 的方式建立Client 视 



        窗,此后几乎所有对Child 窗口的管理工作,诸如产生新的Child 窗口、重新排列窗口、 



        重新排列图标、在菜单上列出已开启窗口。。。等等,都由Client 代劳,只要Frame 窗口向 



        Client 窗口下命令(送MDI 消息如WM_MDICREATE 或WM_MDITILE 就去)即可。 



        你可以把CSplitterWnd 对象视为MDI Client ,观念上比较容易打通。 



分裂窗口之实作 



     让我先把Scribble  目前使用的类别之中凡与本节主题有关的,做个整理。 



     Visual C++ 4。0 以前的版本,AppWizard 为Scribble 产生的类别是这样子的: 



       用途             类别名称                        基础类别(MFC 类别) 



      main frame      CMainFrame                        CMDIFrameWnd 



      document frame   直接使用MFC 类别CMDIChildWnd               CMDIChildWnd 



      view            CScribbleView                     CView 



      document        CScribbleDoc                      CDocument 



      而其CMultiDocTemplate 对象是这样子的: 



      pDocTemplate = new CMultiDocTemplate( 

                             IDR_SCRIBTYPE; 

                             RUNTIME_CLASS(CScribbleDoc); 

                             RUNTIME_CLASS(CMDIChildWnd); 

                             RUNTIME_CLASS(CScribbleView)); 



       为了加上分裂窗口,我们必须利用ClassWizard 新增一个类别(在Scribble 程序中名为 



                                                                                      653 


…………………………………………………………Page 716……………………………………………………………

                    第篇    深入  MFC  程式設計 



                    CScribbleFrame),衍生自CMDIChildWnd,并让它拥有一个CSplitterWnd 对象,名为 



                    m_wndSplitter 。然后为CSrcibbleFrame 改写OnCreateClient 虚拟函数,在其中调用 



                    m_wndSplitter。Create  以产生分裂窗口、设定窗口个数、设定窗口的最初尺寸等初始状态。 



                    最后,当然,我们不能够再直接以CMDIChildWnd 负责document frame 窗口,而必须以 



                    CScribbleFrame 取代之。也就是说,得改变CMultiDocTemplate 构造式的第三个参数: 



                    pDocTemplate = new CMultiDocTemplate( 

                                           IDR_SCRIBTYPE; 

                                           RUNTIME_CLASS(CScribbleDoc); 

                                           RUNTIME_CLASS(CScribbleFrame); 

                                           RUNTIME_CLASS(CScribbleView)); 



                    俱往矣!Visual C++ 4。0 之后的AppWizard 为Scribble 产生的类别是这个样子: 



                    用途                       类别名称                   基础类别 



                    main frame                  CMainFrame                  CMDIFrame Wnd 



                    document frame              CChildFrame                 CMDIChild Wnd 



                    view                        CScribble View              CView 



                    document                    CScribbleDoc                CDocument 



                    而其CMultiDocTemplate 对象是这样子的: 



                    pDocTemplate = new CMultiDocTemplate( 

                                           IDR_SCRIBTYPE; 

                                           RUNTIME_CLASS(CScribbleDoc); 

                                           RUNTIME_CLASS(CChildFrame); 

                                           RUNTIME_CLASS(CScribbleView)); 



                    这就方便多了, CChildFrame 相当于以前( MFC 4。0 之前) 你得自力完成的 



                    CScribbleFrame。现在,我们可以从「为此类别新添成员变量」开始作为。 



654 


…………………………………………………………Page 717……………………………………………………………

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



  以下是加上分裂窗口的步骤: 



  在ClassView (注意,不是ClassWizard )中选择CChildFrame。按下右键,选 



  择突冒式菜单中的【Add Variable 】 



   出现【Add Member Variable 】对话框。填充如下,然后选按【OK 】。 



     现在你可以从ClassView 画面中实时看到CChildFrame 的新变量。 



  打开ChildFrm。cpp,在WizardBar  的【Messages 】清单中选择OnCreateClient。 



   以Yes  回答WizardBar  的询问,产生处理例程。 



  在函数空壳中键入以下内容: 



   return m_wndSplitter。Create(this; 2; 2; CSize(10; 10); pContext); 



                                                                           655 


…………………………………………………………Page 718……………………………………………………………

                  第篇    深入  MFC  程式設計 



                     回到ClassView 之中,你可以看到新的函数。 



                  CSplitterWnd::Create 正是产生分裂窗口的关键,它有七个参数: 



                    1。 表示父窗口。这里的this 代表的是CChildFrame 窗口。 



                    2。 分裂窗口的水平窗口数(row ) 



                    3。 分裂窗口的垂直窗口数(column ) 



                    4。 窗口的最小尺寸(应该是一个CSize 对象) 



                    5。 在窗口上使用哪一个View 类别。此参数直接取用Framework 交给 



                      OnCreateClient 的第二个参数即可。 



                    6。 指定分裂窗口的风格。默认值是: WS_CHILD|WS_ VISIBLE|WS_HSCROLL | 



                      WS_ VSCROLL|SPLS_DYNAMIC_SPLIT ,意思就是一个可见的子窗口,有着水平卷 



                      轴和垂直滚动条,并支持动态分裂。关于动态分裂(以及所谓的静态分裂),第 



                      13 章将另有说明。 



                    7。 分裂窗口的ID 。默认值是AFX_IDW_PANE_FIRST ,这将成为第一个窗口的ID 。 



                      我们的源代码有了下列变化: 



                  // in CHILDFRM。H 

                  class CChildFrame : public CMDIChildWnd 

                   { 

                  protected: 

                      CSplitterWnd    m_wndSplitter; 

                  protected: 

                      virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs; CCreateContext* pContext); 

                   。。。 

                   }; 



                  // in CHILDFRM。CPP 

                  BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT /* lpcs */; 

                                                         CCreateContext* pContext 

                   { 

                          return m_wndSplitter。Create(this; 

                                  2; 2;           // TODO: adjust the number of rows; columns 

                                  CSize(10; 10);  // TODO: adjust the minimum pane size 



656 


…………………………………………………………Page 719……………………………………………………………

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



                    pContext); 

     } 



本章回顾 



   这一章里我们追求的是精致化。 



   Scribble Step3  已经有绘图、文件读写、变化笔宽的基本功能,但是「连接到同一份 



   Document  的不同的Views 」之间却不能够做到同步更新的视觉效果,此外View 窗口 



    中没有滚动条也是遗憾的事情。 



    Scribble Step4 弥补了上述遗憾。它让「连接到同一份Document  的不同的Views 」之间 



    做到同步更新…关键在于CDocument::UpdateAllViews 和CView::Update 两个虚拟函 



    式。而由于同步更新引发的绘图效率问题,所以我们又学会了如何设计所谓的hint ,让 



    绘图动作更聪敏些。也因为hint 缘故,我们改变了Document  的格式,为每一线条加上 



    一个外围四方形记录。 



    在滚动条方面,MFC 提供了一个名为CScrollView 的类别,内有滚动条功能,因此直接拿来 



    用就好了。我们唯一要担心的是,从CView 改为CScrollView,原先的OnDraw 绘图动 



    作要不要修改?毕竟,卷来卷去把原点都不知卷到哪里去了,何况还有映射模式(坐标 



    系统)的问题。这一点是甭担心了,因为application framework 在调用OnDraw 之前, 



    已经先调用了OnPrepareDC,把问题解决掉了。唯一要注意的是,送进OnDraw 的滑 



    鼠坐标点应该改为逻辑坐标,以文件左上角为原点。DP2LP  函数可以帮我们这个忙。 



    此外,我们接触了另一种新而且更精致的UI 接口:分裂窗口,让一个窗口分裂为数个 



    窗口,每一个窗口容纳一个View 。MFC 提供CSplitterWnd 做此服务。 



                                                                     657 


…………………………………………………………Page 720……………………………………………………………

                第篇    深入  MFC  程式設計 



658 


…………………………………………………………Page 721……………………………………………………………

                                                         第 12 章    列印與預樱А



        12 

     第  章 



                           打印与预览 



     「打印」绝对是个大工程,「打印预览」是个更大的工程。如果你是一位SDK 程序员, 



    而你分配到的工作是为公司的绘图软件写一个印前预浏系统,那么我真的替你感到忧 



    郁。可如果你使用MFC ,情况又大不相同了。 



概观 



    Windows  的DC 观念,在程序的绘图动作与实际设备的驱动程序之间做了一道隔离,使 



    得绘图动作完全不需修改就可以输出到不同的设备上: 



                                 Display DC     Display DRV     Display  

                                  Display DC    Display DRV     Display  



                       GDI calls 



                                  Printer DC    Printer DRV     Printer  

                                                 Printer DRV     Printer  



     即便如此,打印仍然有其琐碎的工作需要由程序员承担。举个例子,屏幕窗口有卷动杆, 



     打印机没有,于是「分页」就成了一门学问。另外,如何中断打印?如何设计水平方向 



                                                                              659 


…………………………………………………………Page 722……………………………………………………………

              第篇    深入  MFC  程式設計    



              (landscape)或垂直方向(portrait )的打印输出? 



               landscape,风景画,代表横向打印;portrait ,人物画,代表纵向打印。 



              如果曾经有过SDK 程序经验,你一定知道,把资料输出到屏幕上和输出到打印机上几 



              乎是相同的一件事,只要换个DC       (注)就好了。MFC 甚至不要求程序员的任何动作, 



              即自动供应打印功能和预览功能。拿前面各版本的Scribble 为例,我们可曾为了输出任 



              何东西到打印机上而特别考虑什么程序代码?完全没有!但它的确已拥有打印和预览功 



              能,你不妨执行Step4 的【File/Print。。。 】以及【File/Print Preview 】看看,结果如图12



              1a。 



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



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



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



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



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



              Scribble 程序之所以不费吹灰之力即拥有打印与预览功能,是因为负责资料显示的 



              CSribbleView::OnDraw 函数接受了一个DC 参数,此DC 如果是display DC ,所有的 



             输出就往屏幕送,如果是printer DC ,所有输出就往打印机送。至于OnDraw 到底收到 



              什么样的DC ,则由Framework 决定…想当然耳Framework 会依使用者的动作决定之。 



             MFC 把整个打印机制和预览机制都埋在application framework 之中了,我们因此也有了 



              标准的UI 接口可以使用,如标准的【打印】对话框、【打印设定】对话框、【打印中】 



             对话框等等,请看图12…1。 



              我将在这一章介绍MFC  的打印与预览机制,以及如何强化它。 



660 


…………………………………………………………Page 723……………………………………………………………

                                               第 12 章    列印與預樱А



图12…1a 不需考虑任何与打印相关的程序动作, Scribble 即已具备打印与预 

       览功能 (  只要我们一开始在AppWizard 的步骤四对话框中选择 

        【Printing and Print Preview】项 目)。打印出来的图形大小 

       并不 符合理想, 从预览画面中就可察知。这正是本章要改善的地方。 



图12…1b 标准的打印UI 接口。本图是选按Scribble 的 【File/Print。。。】命 



       令项之后获得的 【打印】对话框。 



                                                                  661 


…………………………………………………………Page 724……………………………………………………………

              第篇    深入  MFC  程式設計    



              图12…1c 你可以选按Scribble 的 【File/Print Setup。。。 】命令项, 获得设 



                     定打印机的机会。 



                   图12…1d 打印过程中会出现一个标准的 【打印状态】对话框,允许使用者中 



                          断打印动作。 



               Scribble Step5 加强了打印功能以及预览功能。MFC 各现成类别之中已有打印和预览机 



               制,我要解释的是它的运作模式、执行效果、以及改善之道。图12…2 就是Scribble Step5 



               的预览效果,UI 方面并没有什么新东西,主要的改善是,图形的输出大小比较能够被接 



               受了,每一份文件并且分为两页,第一页是文件名称(文件名称),第二页才是真正的 



               文件内容,上有一表头。 



662 


…………………………………………………………Page 725……………………………………………………………

                                                         第 12 章    列印與預樱А



      图12…2 Scribble Step5 打印预览。第一页是文件名称,第二页是文件内容。 



打印动作的背景原理 



      开始介绍MFC  的打印机制之前,我想,如果先让你了解打印的背后原理,可以帮助你 



      掌握其本质。 



      Windows  的所有绘图指令,都集中在GDI 模块之中,称为GDI 绘图函数,例如: 



         TextOut(hPr; 50; 50; szText; strlen(szText)); // 输出一字符串 



         Rectangle(hPr; 10; 10; 50; 40);               // 画一个四方形 



         Ellipse(hPr; 200; 50; 250; 80);               // 画一个椭圆形 



         Pie(hPr; 350; 50; 400; 100; 400; 50; 400; 100); // 画一个圆饼图 



         MoveTo(hPr; 50; 100);                         // 将画笔移动到新位置 



         LineTo(hPr; 400; 50);                         // 从前一位置画直线到新位置 



      图形输往何方?关键在于DC ,这是任何GDI 绘图函数的第一个参数,可以是GetDC 



      或BeginPaint  函数所获得的「显示屏DC 」(以下是SDK 程序写法): 



                                                                            663 


…………………………………………………………Page 726……………………………………………………………

                   第篇    深入  MFC  程式設計    



                      HDC hDC; 

                      PAINTSTRUCT ps;    // paint structure 

                      hDC = BeginPaint(hWnd; &ps); 



                  也可以是利用CreateDC 获得的一个「打印机DC 」: 



                      HDC hPr; 

                      hPr = CreateDC(lpPrintDriver; lpPrintType; lpPrintPort; (LPSTR) NULL); 



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