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