⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap7_2.htm

📁 非常好的应用vc++6.0进行相关网络程序开发的教程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
    <p>lines.AddTail(item);</p>
    <p>nCount++;</p>
    <p>}</p>
    <p>catch(CArchiveException *e)</p>
    <p>{</p>
    <p>if(e-&gt;m_cause!=CArchiveException::endOfFile)</p>
    <p>{</p>
    <p>TRACE0(&quot;Unknown exception loading file!\n&quot;);</p>
    <p>throw;</p>
    <p>}else</p>
    <p>{</p>
    <p>TRACE0(&quot;End of file reached...\n&quot;);</p>
    <p>e-&gt;Delete();</p>
    <p>}</p>
    <p>break;</p>
    <p>}</p>
    <p>}</p>
    <p>nLineNum=nCount;</p>
    <p>}</p>
    <p>}</p>
    <p> </p>
    <p>在If子句中,从字符串链表中逐行读取字符串,然后通过调用CArchive对象的&lt;&lt;操作符,将文本行写入ar对象中。在else子句中,从CArchive对象逐一读入字符串对象,然后加入到链表中。由于在Serialize()函数的载入文档调用之前,框架已经调用CDocument的DeleteContents()成员函数作好了清理工作,这里不必再重复清理字符串链表。在载入字符串对象的同时,统计了字符串的个数即文本行数。由于这里使用CString的串行化,因此获得的文件不同于普通的文本文件。</p>
    <p>文档串行化与一般文件处理方式最大的不同在于:在串行化中,对象本身对读和写负责。在上面的例子中,CArchive并不知道也不需要知道CString类的文本行内部数据结构,它只是调用CString类的串行化方法实现对象到文件的读写操作,也就是说,实际完成读写操作的是CString类,CArchive只是对象到CFile类的对象的一个中介。而文档的串行化正是通过调用文档中需要保存的各个对象的串行化方法来完成的。这几个对象的关系如图7-8所示。这里的对象必须是MFC对象,如果想让自己设计的对象也具有串行化能力,就必须定制该对象的串行化方法。有关定制串行化对象的技术在后面再作详细介绍。</p>
    <p align="center"></font><img src="T7_8.gif" alt="T7_8.tif (124988 bytes)" WIDTH="405" HEIGHT="208"><font SIZE="3"></p>
    <p align="center">图7-8 文档对象和文件对象</p>
    <p>CArchive对象使用重载的插入(&lt;&lt;)和提取(&gt;&gt;)操作符执行读和写操作。有人会说,这种方式很象C++的输入输出流。其实,一个archive对象就是可以理解成一种二进制流。象输入/输出流一样,一个archive对象与一个文件相关联,并提供缓冲读写机制。但是,一个输入/输出流处理的是ASCII字符,而一个archive对象处理的是二进制对象。</p>
    <p>如果不是使用框架创建和希望自己创建CArchive的话,可以这么做:</p>
    <blockquote>
      <p>CFile file;//声明一个CFile类对象</p>
      <p>file.Open(“c:\\readme.txt”,CFile::modeCreate|CFile::modeWrite);//打开文件</p>
      <p>CArchive ar(&amp;file,CArchive::store);//用指向file的指针创建CArchive类对</p>
      <p>//象,指定模式为store即存储,如果需要从CArchive //中载入,可设为load</p>
      <p>...//一些串行化工作</p>
      <p>ar.Close();//首先关闭CArchive,然后关闭file</p>
      <p>file.Close();</font></p>
    </blockquote>
    <p><font SIZE="3"> </p>
    <p><font color="#3973DE">在文档中引用视图类</font></p>
    <p>有时要在文档对象中访问视图对象,而一个文档可能会对应多个视图,此时可以采用如下方法:</p>
    <p>POSITION pos=GetFirstViewPosition();//获取视图链表的头指针</p>
    <p>CEditorView *MyView=(CMyView*)GetNextView(pos);</p>
    <p></font><font SIZE="4"> </p>
    <p></font><font SIZE="4" color="#3973DE">7.2.3 文本编辑器的视图类</font><font SIZE="3"></p>
    <p><font color="#3973DE">视图类数据成员设计</font></p>
    <p>现在设计文本编辑器的视图类。由于编辑器需要提供显示字体选择功能,因此在编辑器内增加一个数据成员代表当前所用的字体。另外,还需要两个变量lHeight和cWidth分别代表所用字体的高度和宽度,以便控制输出,因为Windows以图形方式输出,输出文本也需要程序员自己计算坐标。修改后的视图类如下面的片段所示:</p>
    <p>class CEditorView : public CView</p>
    <p>{</p>
    <p>protected: // create from serialization only</p>
    <p>CEditorView();</p>
    <p>DECLARE_DYNCREATE(CEditorView)</p>
    <p> </p>
    <p>CFont* pFont;</p>
    <p>int lHeight;</p>
    <p>int cWidth;</p>
    <p>...</p>
    <p>}</p>
    <p>也许有人会问:既然文档类包含应用程序的数据,而视图只负责输出,为什么不把数据全部放在文档类之中呢?从应用程序角度来看,视图是不包含数据的,显示文档的所有数据都是从文档对象中读取的。但这并不意味着视图不能包含数据成员。视图是从CView派生出来的类,作为类,它当然可以包含数据成员。而且,为了显示输出的需要,它经常包含一些与显示相关的数据成员。设计文档视结构的关键就是确切的定义用户文档应当包含哪些信息。那么,如何合理分配文档和视图的数据成员呢?一条简单的原则是:如何使用更方便,就如何分配数据成员。另外,还要看该数据成员是否需要保存到文档中,如果要保存到文档中,就必须放在文档中。因为文档可以对应多个视图,如果放在视图中,由于不同的视图的数据成员可以有不同的数值,这样文档保存时就不知道该使用哪一个数值了。一般的,与显示相关的数据成员都可以放在视图类中。在上面的文本编辑器中,我们并不需要保存编辑器使用何种字体这一信息,而这一信息又与文档显示密切相关,因此把它放在视图类中是很恰当的。这样的话,还可以用多个使用不同字体的视图观察同一文档。但是,如果编辑器是一个类似于Microsoft 
    WORD之类的字处理器,在显示中支持多种字体的同一屏幕输出,这时需要保存字体信息,就要把字体信息放在文档类中了。</p>
    <p> </p>
    <p>视图数据成员的初始化</p>
    <p>在文档类中,通过成员函数OnNewDocument()来完成文档类数据成员的初始化工作。视图类也提供了一个CView::OnInitialUpdate()成员函数来初始化视图类的数据成员。</p>
    <p>在以下情况下,应用程序将自动执行视图类的OnInitialUpdate()来初始化视图类数据成员:</p>
    <blockquote>
      <p>用户启动应用程序</p>
      <p>从File菜单选择New菜单项,CWinApp::OnFileNew在调用CDocument::OnNewDocument后即调用OnInitialUpdate准备绘图输出;</p>
      <p>用File-&gt;Open命令打开一个文件,此时希望清除视图原有的显示内容 
      </p>
    </blockquote>
    <p>在编辑器中要做的主要工作是对编辑器使用的字体的初始化,见清单7.7。</p>
    <p>清单7.7 视图的OnInitialUpdate方法</p>
    <p>void CEditorView::OnInitialUpdate() </p>
    <p>{</p>
    <p>// TODO: Add your specialized code here and/or call the base class</p>
    <p> </p>
    <p>CDC *pDC=GetDC();</p>
    <p>pFont=new CFont();</p>
    <p>if(!(pFont-&gt;CreateFont(0,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,</p>
    <p>ANSI_CHARSET,OUT_TT_PRECIS,CLIP_TT_ALWAYS,</p>
    <p>DEFAULT_QUALITY,DEFAULT_PITCH,&quot;Courier New&quot;)))</p>
    <p>{</p>
    <p>pFont-&gt;CreateStockObject(SYSTEM_FONT);</p>
    <p>}</p>
    <p> </p>
    <p>CFont* oldFont=pDC-&gt;SelectObject(pFont);</p>
    <p>TEXTMETRIC tm;</p>
    <p>pDC-&gt;GetTextMetrics(&amp;tm);</p>
    <p>lHeight=tm.tmHeight+tm.tmExternalLeading;</p>
    <p>cWidth=tm.tmAveCharWidth;</p>
    <p>pDC-&gt;SelectObject(oldFont);</p>
    <p>CView::OnInitialUpdate();</p>
    <p>}</p>
    <p>OnInitialUpdate()首先调用GetDC()取得当前窗口的设备上下文指针并存放在pDC中。设备上下文(简称DC,英文全称是device 
    context)Windows数据结构,它描述了在一个窗口中绘图输出时所需的信息,包括使用的画笔、画刷、当前选用的字体及颜色(前景色和背景色)、绘图模式,以及其它所需要的绘图信息。MFC提供一个CDC类封装设备上下文,以简化存取DC的操作。</p>
    <p>然后OnInitialUpdate()创建视图显示时所用的字体。同前面提到的其他MFC对象如框架窗口一样,字体对象的创建也分为两步:第一步,创建一个C++对象,初始化CFont的实例;第二步,调用CreateFont()创建字体。除了CreateFont之外,还有两个创建字体的函数:CreateFontIndirect和FromHandle(),前者要求一个指向所需字体的LOGFONT(逻辑字体)的指针作参数,后者需要一个字体句柄作参数。如果CreateFont()因为某种原因失败,那么就调用CreateStockObject()从预定义的GDI对象中创建字体。</p>
    <div align="center"><center><table border="2" cellpadding="2" cellspacing="0" width="100%" bgcolor="#8EDBFF">
      <tr>
        <td width="100%">注意:在Windows的GDI中,包含一些预定义的GDI对象,无需用户去创建,马上就可以拿来使用。这些对象称作库存(Stock)对象。库存对象包括BLACK_BRUSH(黑色画刷)、DKGRAY_BRUSH(灰色画刷)、HOLLOW_BRUSH(空心画刷)、WHITE_BRUSH(白色画刷)、空画刷、黑色画笔、白色画笔以及一些字体和调色板等。</font><font SIZE="2">C</font><font SIZE="3">GdiObject:: CreateStockObject()并不真正创建对象,而只是取得库存对象的句柄,并将该句柄连到调用该函数的GDI对象上。</font></td>
      </tr>
    </table>
    </center></div><font SIZE="3"><p>然后调用CDC的SelectObject()方法,将字体选入到设备上下文中。SelectObject()函数原型如下:</p>
    <blockquote>
      <blockquote>
        <p>CPen* SelectObject( CPen* pPen );</p>
        <p>CBrush* SelectObject( CBrush* pBrush );</p>
        <p>virtual CFont* SelectObject( CFont* pFont );</p>
        <p>CBitmap* SelectObject( CBitmap* pBitmap );</p>
        <p>int SelectObject( CRgn* pRgn );</p>
      </blockquote>
    </blockquote>
    <p> </p>
    <p>SelectObject的参数可以是一个画笔、画刷、字体、位图或区域,它们统称为GDI(图形设备接口)对象。SelectObject将一个GDI对象选入到一个设备上下文中,新选中的对象将替换原有的同类型对象,然后返回指向被替换的对象的指针。SelectObject()知道它所选中的对象的类型,且总是返回同类的旧对象的指针。还要存储返回的CFont指针,在退出OnInitialUpdate之前调用pDC-&gt;SelectObject(oldFont),将CDC重新设置成原来的初始状态。</p>
    <p>读者以后编程也应当养成这样一个习惯:在用SelectObject选择新的GDI对象时,应当保存指向原先使用的GDI对象的指针,在绘图结束后,再用SelectObject选择原来的对象,设置CDC为其初始状态。否则的话,会有非法句柄留在设备上下文对象中,积累下去将导致无法预见的错误。但是,如果该设备上下文是自己创建而不是用参数传递过来的,则不必恢复画笔或刷子。象上面的例子,其实用户不必在退出时恢复原来的字体。而在下面要讲的OnDraw函数中,由于pDC是框架传给OnDraw的,因此在退出时必须恢复设备上下文中原来的字体设置。总之,如果用户能肯定画笔或刷子等GDI对象废弃以前设备对象会被销毁,则不必恢复设备上下文中GDI对象的设置。不过,为概念上的明确,还是建议调用恢复过程。</p>
    <p>TEXTMETRIC是一个数据结构,它包含字体的宽度、高度、字的前后空白等字段。调用CDC::GetTextMetrics()获取字体的TEXTMETRIC,从而取得字体的宽度和高度等信息。最后调用CView类的OnInitialUpdate()函数来画视图。</p>
    <p>由于在堆栈上创建了视图所用的字体对象pFont,在关闭视图时就需要删除该字体对象。这部分工作在视图的析构函数中完成。修改视图的析构函数:</p>
    <p>CEditorView::~CEditorView()</p>
    <p>{</p>
    <p> </p>
    <p>if(pFont!=NULL)</p>
    <p>delete pFont;</p>
    <p>}</p>
    <p>视图的绘制</p>
    <p>现在要让视图显示编辑器中的文本。AppWizard为视图类CEditorView生成了一个OnDraw()方法,当需要重画视图时,该函数就会被调用。清单7.8是编辑器的OnDraw函数定义:</p>
    <p>清单7.8 视图的OnDraw方法</p>
    <p>/////////////////////////////////////////////////////////////////////////////</p>
    <p>// CEditorView drawing</p>
    <p>void CEditorView::OnDraw(CDC* pDC)</p>
    <p>{</p>
    <p>CEditorDoc* pDoc = GetDocument();</p>
    <p>ASSERT_VALID(pDoc);</p>
    <p>// TODO: add draw code for native data here</p>
    <p>CFont *oldFont;</p>
    <p>//选择新字体</p>
    <p>oldFont=pDC-&gt;SelectObject(pFont);</p>
    <p> </p>
    <p>//纵向yval坐标为0</p>
    <p>int yval=0;</p>
    <p>POSITION pos;</p>
    <p>CString line;</p>
    <p>//取得文本行链表的头指针</p>
    <p>if(!(pos=pDoc-&gt;lines.GetHeadPosition()))</p>
    <p>{</p>
    <p>return;</p>
    <p>}</p>
    <p>//循环输出各文本行</p>
    <p>while(pos!=NULL)</p>
    <p>{</p>
    <p>line=pDoc-&gt;lines.GetNext(pos);</p>
    <p>pDC-&gt;TextOut(0,</p>
    <p>yval,</p>
    <p>line,</p>
    <p>line.GetLength());</p>
    <p>//更新y坐标值,让它加上文本行所用字体的高度</p>
    <p>yval+=lHeight;</p>
    <p>}</p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -