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

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

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



                    #0168          // drag to the current point。 

                    #0169          CPen* pOldPen = dc。SelectObject(GetDocument()…》GetCurrentPen()); 

                    #0170          dc。MoveTo(m_ptPrev); 

                    #0171          dc。LineTo(point); 

                    #0172          dc。SelectObject(pOldPen); 



492 


…………………………………………………………Page 555……………………………………………………………

                                                    第8章    Document…View  深入探討 



    #0173          m_ptPrev = point; 

    #0174          return; 

    #0175  } 



View 的重绘动作:GetDocument 和OnDraw 



    以下是CScribbleView 中与重绘动作有关的成员变量和成员函数。 



 CScribbleView 的成员变量 



      m_pStrokeCur :一个指针,指向目前正在工作的线条。 



      m_ptPrev :线条中的前一个工作点。我们将在这个点与目前鼠标按下的点之间 



      画一条直线。虽说理想情况下鼠标轨迹的每一个点都应该被记录下来,但如果 



       鼠标移动太快来不及记录,只好在两点之间拉直线。 



        

 CScribbleView 的成员函数 



     OnDraw :这是一个虚拟函数,负责将Document  的数据显示出来。改写它是程 



        

     式员最大的责任之一。 



     GetDocument:AppWizard 为我们做出这样的码,以inline 方式定义于头文件: 



       inline CScribbleDoc* CScribbleView::GetDocument() 



           { return (CScribbleDoc*)m_pDocument; } 



      其中m_pDocument 是CView 的成员变量。我们可以推测,当程序设定好Document 



      Template 之后,每次Framework 动态产生View 对象,其内的m_pDocument  已经被 



      Framework 设定指向对应之Document  了。 



        View 对象何时被动态产生?答案是当使用者选按【File/Open 】或【File/New 】。每 



        当产生一个Document ,就会产生一组Document/View/Frame          「三口组」。 



      OnPreparePrinting,OnBeginPrinting,OnEndPrinting:这三个CView 虚拟函数将 



       用来改善打印行为。AppWizard  只是先帮我们做出空函数。第12 章才会用到 



       它们。 



                                                                                    493 


…………………………………………………………Page 556……………………………………………………………

                  第篇    深入  MFC  程式設計 



                  我们来看看CView 之中居最重要地位的OnDraw,面对Scribble Document 的数据结构, 



                  将如何进行绘图动作。为了获得资料,OnDraw 一开始先以GetDocument 取得Document 



                  对象指针;然后以while 循环一一取得各线条,再调用CStroke::DrawStroke  绘图。想象 



                   中绘图函数应该放在View 类别之内(绘图不正是View  的责任吗),但是DrawStroke  却 



                   否!原因是把线条的资料和绘图动作一并放在CStroke 中是最好的包装方式。 



                  void CScribbleView::OnDraw(CDC* pDC) 

                  { 

                      CScribbleDoc* pDoc = GetDocument(); 

                      ASSERT_VALID(pDoc); 



                      // 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); 

                          pStroke…》DrawStroke(pDC); 

                      } 

                  } 



                   其中用到两个CObList 成员函数: 



                   

                     GetNext:取得下一个元素。 



                     GetHeadPosition:传回串行之第一个元素的「位置」。传回来的「位置」是一 

                   



                    个类型为POSITION  的数值,这个数值可以被使用于CObList 的其它成员函数 



                    中,例如GetAt 或SetAt 。你可以把「位置」想象是串行中用以标示某个节点 



                      node )的指针。当然,它并不真正是指针。 

                     ( 



494 


…………………………………………………………Page 557……………………………………………………………

                                                  第8章    Document…View  深入探討 



View 与使用者的交谈 (鼠标消息处理实例) 



    为了实现「以鼠代笔」的功能,CScribbleView 必须接受并处理三个消息: 



    BEGIN_MESSAGE_MAP(CScribbleView; CView) 

            ON_WM_LBUTTONDOWN() 

            ON_WM_LBUTTONUP() 

            ON_WM_MOUSEMOVE() 

            。。。 

    END_MESSAGE_MAP() 



    三个消息处理例程的的内容总括来说就是追踪鼠标轨迹、在窗口上绘图、以及调用CStroke 



    成员函数以修正线条内容……包括产生一个新的线条空间以及不断把坐标点加上去。三 



    个函数的重要动作摘记于下。这些函数的骨干及其在Messape Map 中的映射项目,不劳 



    我们动手,有ClassWizard 代劳。下一个小节我会介绍其操作方法。 



   void CScribbleView::OnLButtonDown(UINT nFlags; CPoint point) 



   { 



   // 当鼠标左键按下, 



   // 利用CScribbleDoc::NewStroke 产生一个新的线条空间; 



   // 利用CArray::Add 把这个点加到线条上去; 



   // 调用SetCapture 取得鼠标捕捉权(mouse capture); 



   // 把这个点记录为「上一点」(m_ptPrev); 



    } 



   void CScribbleView::OnMouseMove(UINT; CPoint point) 



    { 



   // 当鼠标左键按住并开始移动, 



   // 利用CArray::Add 把新坐标点加到线条上; 



   // 在上一点(m_ptPrev)和这一点之间画直线; 



   // 把这个点记录为「上一点」(m_ptPrev); 



    } 



   void CScribbleView::OnLButtonUp(UINT; CPoint point) 



    { 



   // 当鼠标左键放开, 



   // 在上一点(m_ptPrev)和这一点之间画直线; 



   // 利用CArray::Add 把新的点加到线条上; 

                                                                                 495 

   // 调用ReleaseCapture() 释放鼠标捕捉权(mouse capture)。 

    } 


…………………………………………………………Page 558……………………………………………………………

               第篇    深入  MFC  程式設計 



            ClassWizard 的辅佐 



                前述三个CScribbleView 成员函数(OnLButtonDown,OnLButtonUp,OnMouseMove)是 



                Message Map 的一部份,ClassWizard 可以很方便地帮助我们完成相关的Message Map 设 



                定工作。 



                首先,选按【View/ClassWizard 】激活ClassWizard,选择其【Message Map 】附页: 



               在图右上侧的【Class Name 】清单中选择CScribbleView,然后在图左侧的【Object IDs 】 



               清单中选择CScribbleView,再在图右侧的【Messages 】清单中选择WM_LBUTTONDOWN , 



               然后选按图右的【Add Function 】钮,于是图下侧的【Member functions 】清单中出现一 



               笔新项目。 



               然后,选按【Edit Code 】钮,文字编辑器会跳出来,你获得了一个OnLButtonDown 函数 



               空壳,请在这里键入你的程序代码: 



496 


…………………………………………………………Page 559……………………………………………………………

                                                   第8章    Document…View  深入探討 



另两个消息处理例程的实作作法雷同。 



Message Map 因此有什么变化呢?ClassWizard 为我们自动加上了三笔映射项目: 



BEGIN_MESSAGE_MAP(CScribbleView; CView) 

        //{{AFX_MSG_MAP(CScribbleView) 

        ON_WM_LBUTTONDOWN() 

        ON_WM_LBUTTONUP() 

        ON_WM_MOUSEMOVE() 

        //}}AFX_MSG_MAP 

        // Standard printing mands 

        ON_MAND(ID_FILE_PRINT; CView::OnFilePrint) 

        ON_MAND(ID_FILE_PRINT_DIRECT; CView::OnFilePrint) 

        ON_MAND(ID_FILE_PRINT_PREVIEW; CView::OnFilePrintPreview) 

END_MESSAGE_MAP() 



此外ScribbleView 的类别声明中也自动有了三个成员函数的声明: 



class CScribbleView : public CView 

 { 

 。。。 

// Generated message map functions 

protected: 

        //{{AFX_MSG(CScribbleView) 

        afx_msg void OnLButtonDown(UINT nFlags; CPoint point); 

        afx_msg void OnLButtonUp(UINT nFlags; CPoint point); 

        afx_msg void OnMouseMove(UINT nFlags; CPoint point); 

        //}}AFX_MSG 

 。。。 

 }; 



                                                                                    497 


…………………………………………………………Page 560……………………………………………………………

               第篇    深入  MFC  程式設計 



            WizardBar 的辅佐 



                WizardBar 是Visual C++ 4。0 之后的新增工具,也就是文字编辑器上方那个有着【Object 



                IDs 】和【Messages 】清单的横杆。关于修改Message Map 这件事,WizardBar 可以取代 



                ClassWizard 这个大家伙。 



                首先,进入ScribbleViwe。cpp  (因为我们确定要在这里加入三个鼠标消息处理例程),选 



                择WizardBar 上的【Object IDs  】为CScribbleView , 再选择【Messages  】为 

                  _ 

                WM LBUTTONDOWN ,出现以下画面: 



                回答Yes ,于是你获得一个OnLButtonDown 函数空壳,一如在ClassWizard  中所得。请 



                在函数空壳中输入你的程序代码。 



            Serialize :对象的文件读写 



                你可能对Serialization 这个名词感觉陌生,事实上它就是对象导向世界里的Persistence         (永 



                续生存),只是后者比较抽象一些。对象必须能够永续生存,也就是它们必须能够在程 



                式结束时储存到文件中,并且在程序重新激活时再恢复回来。储存和恢复对象的过程在 



                MFC 之中就称为serialization。负责这件重要任务的,是MFC CObject 类别中一个名为 



                Serialize  的虚拟函数,文件的「读」「写」动作均透过它。 



498 


…………………………………………………………Page 561……………………………………………………………

                                                       第8章    Document…View  深入探討 



      如果文件内容是借着层层类别向下管理(一如本例),那么只要每一层把自己份内的工 



      作做好,层层交待下来就可以完成整份资料的文件动作。 



Serialization 以外的文件读写动作 



      其实有时候我们希望在重重包装之中返璞归真一下,感受一些质朴的动作。在介绍 



      Serialization 的重重包装之前,这里给你一览文件实际读写动作的机会。 



      文件I/O 服务是任何操作系统的主要服务。Win32 提供了许多文件相关APIs :开档、 



      关文件、读文件、写文件、搜寻资料。。。。MFC 把这些操作都包装在CFile 之中。可想而知,它 



      必然有Open、Close、Read 、Write、Seek 。。。 等等成员函数。下面这段程序代码示范CFile 如 



      何读档: 



       char* pBuffer = new char'0x8000'; 

       CFile file(〃mydoc。doc〃; CFile::modeRead);  //  

                                                     打开mydoc。doc 文件,使用只读模式。 

       UINT nBytesRead = file。Read(pBuffer; 0x8000); // 读取8000h 个字节到pBuffer 中。 



       上述程序片段中,对象file   的构造式将打开mydoc。doc 档。并且由于此对象产生于函数 



       的堆栈之中,当函数结束,file   的析构式将自动关闭mydoc。doc 档。 



       开文件模式有许多种,都定义在CFile             (AFX。H )之中: 



       enum OpenFlags { 

             modeRead =    0x0000;  //只读 

             modeWrite =         0x0001;  // 唯写 

             modeReadWrite =0x0002;  // 可读可写 

             sharepat =      0x0000; 



            shareExclusive =    0x0010; // 唯我使用 

             shareDenyWrite =    0x0020; 

             shareDenyRead =0x0030; 

             shareDenyNone =0x0040; 

             modeNoInherit =0x0080; 

             modeCreate =        0x1000;  //产生新档(甚至即使已有相同名称之文件存在) 

             modeNoTruncate =   0x2000; 

             typeText =    0x4000; // typeText and typeBinary are used in 

             typeBinary =       (int)0x8000 // derived classes only 

             }; 

                   typeText = 0x4000; // typeText and typeBinary are used in 



                   typeBinary = (int)0x8000 // derived classes only 



                   }; 



                                                                                       499 


…………………………………………………………Page 562……………………………………………………………

                    第篇    深入  MFC  程式設計 



                    再举一例,下面这段程序代码可将文件mydoc。doc  的所有文字转换为小写: 



                        char* pBuffer = new char'0x1000'; 

                        CFile file(〃mydoc。doc〃; CFile::modeReadWrite); 

                        DWORD dwBytesRemaining = file。GetLength(); 

                        UINT nBytesRead; 

                        DWORD dwPosition; 



                        while (dwBytesRemaining) { 

                            dwPosition = file。GetPosition(); 

                            nBytesRead = file。Read(pBuffer; 0x1000); 

                            ::CharLowerBuff(pBuffer; nBytesRead); 

                            file。Seek((LONG)dwPosition; CFile::begin); 

                            file。Write(pBuffer; nBytesRead); 

                            dwBytesRemaining …= nBytesRead; 

                         } 

                        delete'' pBuffer; 



                      文件的操作常需配合对异常情况(exception )的处理,因为文件的异常情况特别多:档 



                      案找不到啦、文件handles 不足啦、读写失败啦。。。。上一例加入异样情况处理后如下: 



                        char* pBuffer = new char'0x1000'; 



                        try { 

                            CFile file(〃mydoc。doc〃; CFile::modeReadWrite); 

                            DWORD dwBytesRemaining = file。GetLength(); 

                            UINT nBytesRead; 

                            DWORD dwPosition; 



                            while (dwBytesRemaining) { 

                                dwPosition = file。GetPosition(); 

                                nBytesRead = file。Read(pBuffer; 0x1000); 

                                ::CharLowerBuff(pBuffer; nBytesRead); 

                                file。Seek((LONG)dwPosition; CFile::begin); 

                                file。Write(pBuffer; nBytesRead); 

                                dwBytesRemaining …= nBytesRead; 

                            } 

                         } 

                        catch (CFileException* e) { 

                            if (e…》cause == CFileException::fileNoteFound) 

                                MessageBox(〃File not found〃); 

                            else if (e…》cause == CFileException::tooManyOpeFiles) 

                                MessageBox(〃File handles not enough〃); 

                            else if (e…》cause == CFileException::hardIO) 



500 


…………………………………………………………Page 563……………………………………………………………

                                                          第8章    Document…View  深入探討 



                MessageBox(〃Hardware error〃); 

            else if (e…》cause == CFileException::diskFull) 

                MessageBox(〃Disk full〃); 

            else if (e…》cause == CFileException::badPath) 

                MessageBox(〃All or part of the path is invalid〃); 

            else 

                MessageBox(〃Unknown file error〃); 

            e…》Delete(); 

         } 

        delete'' pByffer; 



台面上的Serialize 动作 



    让我以Scribble 为例,向你解释台面上的(应用程序代码中可见的)serialization 动作。根 



    据图8…3 的数据结构,Scribble 程序的文件读写动作是这么分工的: 
返回目录 上一页 下一页 回到顶部 9 10
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!