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

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

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




…………………………………………………………Page 748……………………………………………………………

                第篇    深入  MFC  程式設計    



                 CPrintInfo 成员名称          参考到的打印页 



                 GetMinPage/SetMinPage           Document  中的第一页 



                 GetMaxPage/SetMaxPage          Document  中的最后一页 



                 GetFromPage                将被印出的第一页(出现在【打印】对话框,图12…1b) 



                 GetToPage                  将被印出的最后一页(出现在【打印】对话框) 



                m_nCurPage                                  目前正被印出的一页(出现在【打印状态】对话框) 



                m_nNumPreviewPages                预览窗口中的页数(稍后将讨论之) 



                 注:页码从1  (而不是0 )开始。 



                 CPrintInfo 结构中记录的「页」数,指的是打印机的页数;Framework 针对每一「页」 



                 调用OnPrepareDC  以及OnPrint 时,所指的「页」也是打印机的页。当你改写 



                 OnPreparePrinting 时指定Document  的长度,所用的单位也是打印机的「页」。如果 



                Document  的一页恰等于打印机的一页(一张纸),事情就单纯了;如果不是,你必须在 



                 两者之间做转换。 



                 Scribble Step5 设定让每一份Document 使用打印机的两页。第一页只是单纯印出文件名 



                 称(文件名称),第二页才是文件内容。假设我利用View 窗口卷动杆在整个Document 



                 四周画一四方圈的话,我希望这一四方圈落入第二页(第二张纸)中。当然,边界留白 



                 必须考虑在内,如图12…6。除此之外,我希望第二页(文件内容)最顶端留一点空间, 



                 做为表头。本例在表头中放的是文件名称。 



686 


…………………………………………………………Page 749……………………………………………………………

                                                第 12 章    列印與預樱А�



     图12…6 Scribble Step5 的每一份文件打印时有两页,第一页是文件名称, 



          第二页是文件内容, 最顶端留有一个表头。 



配置GDI 绘图工具 



    绘图难免需要各式各样的笔、刷、颜色、字形、工具。这些GDI 资源都会占用内存, 



    而且是GDI 模块的heap 。虽说Windows 95 对于USER 模块和GDI 模块的heap  已 



    有大幅改善,使用32 位heap ,不再局限64KB,但我们当然仍然不希望看到浪费的情 



    况发生,因此最好的方式就是在打印之前配置这些GDI 绘图对象,并在打印后立刻释放。 



    看看图12…5,配置GDI 对象的最理想时机显然是OnBeginPrinting,两个理由: 



      1。 每当Framework 开始一份新的打印工作,它就会调用此函数一次,因此不同 



        打印工作所需的不同工具可在此有个替换。 



      2。 此函数的参数是一个和「打印机DC 」有附着关系的CDC 对象指针,我们直接 



        从此一CDC 对象中配置绘图工具即可。 



                                                                 687 


…………………………………………………………Page 750……………………………………………………………

                第篇    深入  MFC  程式設計    



                配置得来的GDI 对象可以储存在View  的成员变量中,供整个打印过程使用。使用时 



                机当然是OnPrint。如果你必须对不同的打印页使用不同的GDI 对象,CPrintInfo 中的 



                m_nCurPage 可以帮你做出正确的决定。 



                释放GDI 对象的最理想时机当然是在OnEndPrinting,这是每当一份打印工作结束后, 



                Application Framework 会调用的函数。 



                Scribble 没有使用什么特殊的绘图工具,因此下面这两个虚拟函数也就没有修改,完全 



                保留AppWizard  当初给我们的样子: 



                void CScribbleView::OnBeginPrinting(CDC* /*pDC*/; CPrintInfo* /*pInfo*/) 



                { 



                // TODO: add extra initialization before printing 



                } 



                void CScribbleView::OnEndPrinting(CDC* /*pDC*/; CPrintInfo* /*pInfo*/) 



                { 



                // TODO: add cleanup after printing 



                } 



           尺寸与方向:关于映射模式(坐标系统) 



                回忆所谓的坐标系统,我已经在上一章描述过CScrollView 如何为了卷动效果而改变座 



                标系统的原点。除了改变原点,我们甚至可以改变坐标系统的单位长度,乃至于改变座 



                标系统的横纵比例(scale)。这些就是这一节要讨论的重点。 



                Document 有大小可言吗?有的,在打印过程中,为了计算Document 对应到打印机的页 



                数,我们需要Document  的尺寸。CScribbleDoc 的成员变量m_sizeDoc ,就是用来记录 



                Document  的大小。它是一个CSize 对象: 



688 


…………………………………………………………Page 751……………………………………………………………

                                                       第 12 章    列印與預樱А�



                                                         800 



                                                                     9 

 void CScribbleDoc::InitDocument() 

  void CScribbleDoc::InitDocument() 

                                                                     0 

                                                                     0 

  {{                                                  Document 

   。。。 

    。。。 

   m_sizeDoc = CSize(800;900); 

    m_sizeDoc = CSize(800;900); 

  } 

  } 



事实上,所谓「逻辑坐标」原本是没有大小的,如果我们说一份Document 宽800 高 



900;那么若逻辑坐标的单位是英寸;这就是 8 英寸宽 9 英寸高;若逻辑座标的单位是 



公分,这就是8 公分宽9 公分高。如果逻辑单位是图素(Pixel )呢?那就是800 个图 



素宽900 个图素高。图素的大小随着输出装置而改变,在14 Super VGA                  (1024x768) 



显示器上,800x900 个图素大约是21。1 公分宽23。6 公分高,而在一部300 DPI              (Dot Per 



Inch,每英嫉闶'y )的激光打印机上,将是2…2/3 英伎3 英几摺 



预设情况下GDI 绘图函数使用MM_TEXT  映射模式(Mapping Mode ,也就是坐标系 



统,注),于是逻辑坐标等于装置坐标,也就是说一个逻辑单位是一个图素。如果不重 



新设定映射模式,可以想见屏幕上的图形一放到300 DPI 打印机上都嫌太小。 



解决的方法很简单:设定一种与真实世界相符的逻辑坐标系统。Windows 提供的八种映 



像模式中有七种是所谓的metric  映射模式,它们的逻辑单位都建立在公分或英嫉幕 



上,这正是我们所要的。如果把OnDraw  内的绘图动作都设定在MM_LOENGLISH  映射 



模式上(每单位0。01 英迹敲床宦凼涑龅狡聊簧匣虻酱蛴』隙蓟竦孟嗤某叨取 



真正要为「多少图点才能画出一英汲ぁ股四越畹氖亲爸们'y动程序,不是我们。 



                                                                             689 


…………………………………………………………Page 752……………………………………………………………

                 第篇    深入  MFC  程式設計    



                 注:GDI  的八种映射模式及其意义如下: 



                  MM_TEXT :以图素(pixel )为单位,Y 轴向下为正,X 轴向右为正。 



                  MM_LOMETRIC :以0。1 公分为单位,Y 轴向上为正,X 轴向右为正。 



                  MM_HIMETRIC :以0。01 公分为单位,Y 轴向上为正,X 轴向右为正。 



                  MM_LOENGLISH :以0。01 英嘉5      ノ唬Y 轴向上为正,X 轴向右为正。 



                  MM_HIENGLISH :以0。001 英嘉5     ノ唬Y 轴向上为正,X 轴向右为正。 



                  MM_TWIPS:以1/1440 英嘉5      ノ唬Y 轴向上为正,X 轴向右为正。 



                  MM_ISOTROPIC :单位长度可任意设定,Y 轴向上为正,X 轴向右为正。 



                  MM_ANISOTROPIC :单位长度可任意设定,且X 轴单位长可以不同于Y 轴 



                    单位长(因此圆可能变形)。Y 轴向上为正,X 轴向右为正。 



                回忆上一章为了卷动窗口,曾有这样的动作: 



                    void CScribbleView::OnInitialUpdate() 

                    { 

                            SetScrollSizes(MM_TEXT; GetDocument()…》GetDocSize()); 

                            CScrollView::OnInitialUpdate(); 

                    } 



                 映射模式可以在SetScrollSizes  的第一个参数指定。现在我们把它改为: 



                    void CScribbleView::OnInitialUpdate() 

                    { 

                            SetScrollSizes(MM_LOENGLISH; GetDocument()…》GetDocSize()); 

                            CScrollView::OnInitialUpdate(); 

                    } 



                 注意,OnInitialUpdate 更在OnDraw 之前被调用,也就是说我们在真正绘图动作 



                 OnDraw 之前完成了映射模式的设定。 



                 映射模式不仅影响逻辑单位的尺寸,也影响Y 轴坐标方向。MM_TEXT 是Y 轴向下, 



                 MM_LOENGLISH  (以及其它任何映射模式)是Y 轴向上。但,虽然有此差异,我们的 



                 Step5 程序代码却不需为此再做更动,因为DPtoLP  已经完成了这个转换。别忘了,鼠标 



690 


…………………………………………………………Page 753……………………………………………………………

                                                          第 12 章    列印與預樱А�



左键传来的点坐标是先经过DPtoLP 才储存到CStroke 对象并且然后才由LineTo  画 



出的。 



然而,程序的某些部份还是受到了Y 轴方向改变的冲击。映射模式只会改变GDI 各相 



关函数,不使用DC  的地方,就不受映射模式的影响,例如CRect 的成员函数就不知 



晓所谓的映射模式。于是,本例中凡使用到CRect 的地方,要特别注意做些调整: 



1。 修正「线条外围四方形」的计算方式。原计算方式是在FinishStroke  中这么做: 



   for (int i=1; i 《 m_pointArray。GetSize(); i++) 

    { 

       pt = m_pointArray'i'; 

       m_rectBounding。left   = min(m_rectBounding。left; pt。x); 

       m_rectBounding。right  = max(m_rectBounding。right; pt。x); 

       m_rectBounding。top    = min(m_rectBounding。top; pt。y); 

       m_rectBounding。bottom = max(m_rectBounding。bottom; pt。y); 

    } 

   m_rectBounding。InflateRect(CSize(m_nPenWidth; m_nPenWidth)); 



  新的计算方式是: 



   for (int i=1; i 《 m_pointArray。GetSize(); i++) 

    { 

       pt = m_pointArray'i'; 

       m_rectBounding。left     = min(m_rectBounding。left; pt。x); 

       m_rectBounding。right    = max(m_rectBounding。right; pt。x); 

       m_rectBounding。top      = max(m_rectBounding。top; pt。y); 

       m_rectBounding。bottom   = min(m_rectBounding。bottom; pt。y); 

    } 

   m_rectBounding。InflateRect(CSize(m_nPenWidth; …(int)m_nPenWidth)); 



  这是因为在Y 轴向下的系统中,四方形的最顶点位置应该是找Y 坐标最小者;而在 



  Y 轴向上的系统中,四方形的最顶点位置应该是找Y 坐标最大者;同理,对于四方 



  形的最底点亦然。 



                                                                                691 


…………………………………………………………Page 754……………………………………………………………

                    第篇    深入  MFC  程式設計    



                         0                         x               y 



                                              min y                                    max y 



                                                                                           min y 

                                                  max y 

                                                                0 

                          y                                                              x 



                       2。 我们在OnDraw 中曾经以IntersectRect 计算两个四方形是否有交集。这个函数 



                       也是CRect 成员函数,它假设:一个四方形的底坐标Y 值必然大于顶坐标的 



                       Y 值(这是从装置坐标,也就是MM_TEXT ,的眼光来看) ;如果事非如此,它 



                       根本不可能找出两个四方形的交集。因此我们必须在OnDraw 中做以下修改, 



                       把逻辑坐标改为装置坐标: 



                       void CScribbleView::OnDraw(CDC* pDC) 

                       { 

                               CScribbleDoc* pDoc = GetDocument(); 

                               ASSERT_VALID(pDoc); 



                               // Get the invalidated rectangle of the view; or in the case 

                               // of printing; the clipping region of the printer dc。 

                               CRect rectClip; 

                               CRect rectStroke; 

                               pDC…》GetClipBox(&rectClip); 

                               pDC…》LPtoDP(&rectClip); 

                               rectClip。InflateRect(1; 1); // avoid rounding to nothing 



                               // Note: CScrollView::OnPaint() will have already adjusted the 

                               // viewport origin before calling OnDraw(); to reflect the 

                               // currently scrolled position。 



                               // The view delegates the drawing of individual strokes to 

                               // CStroke::DrawStroke()。 

                               CTypedPtrList& strokeList = pDoc…》m_strokeList; 

                               POSITION pos = strokeList。GetHeadPosition(); 

                               while (pos != NULL) 

                               { 

                                       CStroke* pStroke = strokeList。GetNext(pos); 

                                       rectStroke = pStroke…》GetBoundingRect(); 



692 


…………………………………………………………Page 755……………………………………………………………

                                                             第 12 章    列印與預樱А�



                      pDC…》LPtoDP(&rectStroke); 

                      rectStroke。InflateRect(1; 1); // avoid rounding to nothing 

                      if (!rectStroke。IntersectRect(&rectStroke; &rectClip)) 

                              continue; 

                      pStroke…》DrawStroke(pDC); 

              } 

      } 



分页 



   Scribble 程序的Document 大小固定是800x900,而且我们让它填满打印机的一页。因 



   此Scribble 并没有「将Document 分段打印」这种困扰。如果真要分段打印,Scribble 应 



   该改写OnPrepareDC,在其中视打印的页数调整DC  的原点和截割区域。 



   即便如此,Scribble 还是在分页方面加了一些动作。本例一份Document 打印时被视为 



   一张标题和一张图片的组合,因此打印一份Document  固定要耗掉两张打印纸。我们可 



   以这么设计: 



   BOOL CScribbleView::OnPreparePrinting(CPrintInfo* pInfo) 

   { 

              pInfo…》SetMaxPage(2);   //文件总共有两页经线: 

                                          //第一页是标题页 (title page) 

                                          //第二页是文件页 (图形) 

           BOOL bRet = DoPreparePrinting(pInfo);   // default preparation 

           pInfo…》m_nNumPreviewPages = 2;  // Preview 2 pages at a time 

           // Set this value after calling DoPreparePrinting to override 

           // value read from 。INI file 

           return bRet; 

   } 



  接下来打算设计一个函数用以输出标题页,一个函数用以输出文件页。后者当然应该由 



  OnDraw 负责# 蛭U 馕募th页不是单纯的Document  内容,还有所谓的表头,而这 



  是打印时才做的东西,屏幕显示时并不需要的,所以我们希望把列打印头的工作独立于 



  OnDraw 之外,那么最好的安置地点就是OnPrint 了(请参考图12…5 之后的补充说明 



  的最后一点)。 



                                                                                   693 


…………………………………………………………Page 756……………………………………………………………

                    第篇    深入  MFC  程式設計    



                    Scribble Step5 把列打印头的工作独立为一个函数。总共这三个额外的函数应该声明于 



                    SCRIBBLEVIEW。H 中,其中的PrintPageHeader 在下一节列出。 



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