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

📄 chap7_2.htm

📁 网络资源
💻 HTM
📖 第 1 页 / 共 4 页
字号:
    <p>//恢复原来DC所用的字体</p>
    <p>pDC-&gt;SelectObject(pFont);</p>
    <p>}</p>
    <p>框架调用视图的CView::OnDraw(CDC* pDC)方法完成屏幕显示、打印、打印预览功能,对于不同的输出功能它会传递不同的DC指针给OnDraw()函数。</p>
    <p>在OnDraw()函数中,首先调用GetDocument()函数,取得指向当前视图所对应的文档的指针。通过这个指针,来访问文档中的数据。以后在视图中修改文档中的数据,也是通过GetDocument()来取得文档指针,再通过该文档指针修改文档中的数据。</p>
    <p>在绘图时,可以通过传给OnDraw函数的一个设备上下文DC的指针pDC进行GDI调用。开始绘图之前,往往需要选择GDI资源(或GDI对象,包括画笔、刷子、字体等),将它选入到设备上下文中。在文本编辑器中,我们选择一种字体pFont到设备上下文中,以后在窗口客户区的文本输出就都会使用该字体绘制。在绘制过程中,绘图代码是设备无关的,也就是说它并不需要知道目前使用的是什么设备(屏幕、打印机或其他绘图设备)。</p>
    <p>读者以前如果用Borland C++或SDK编写过Windows程序的话,都会知道:当窗口或窗口的一部分变成无效的话(比如其他窗口从本窗口上拖过、窗口调整大小等),操作系统就会向窗口发送一条WM_PAINT消息。窗口接收到该消息之后,调用Borland 
    C++的EvPaint()或Visual C++的OnPaint()完成窗口绘制工作。这里OnDraw()函数也同样完成窗口绘图输出,这两者有什么关系呢?</p>
    <p>我们先看一下OnPaint()函数:</p>
    <p>void CMyWindow::OnPaint()</p>
    <p>{</p>
    <p>CPaintDC dc(this); //用于窗口绘制的设备上下文</p>
    <p>CString str(“Hello,world!”);</p>
    <p>...</p>
    <p>//绘图输出代码</p>
    <p>dc.TextOut(10,10,str,str.GetLength());</p>
    <p>}</p>
    <p>在OnPaint()函数中,首先创建一个CPaintDC类的对象dc。CPaintDC必需也只能用在WM_PAINT消息处理中。在CPaintDC类对象dc的构造函数中,调用了在SDK下需要显式调用的BeginPaint函数,取得处理WM_PAINT消息时所需的设备上下文。然后OnPaint()函数使用该设备上下文完成各种输出。在OnPaint()函数退出时,dc对象被删除。在dc对象的析构函数中,包含了对EndPaint函数的调用。EndPaint一方面释放设备上下文,另一方面还从应用消息队列中删除WM_PAINT消息。如果在处理WM_PAINT时不使用CPaintDC,则WM_PAINT不被消除,会产生不断重画的现象。</p>
    <p>视图是一个子窗口,它自然也从窗口类继承了OnPaint()成员函数,用以响应WM_PAINT消息。类似于上面的例子,视图OnPaint处理函数首先创建一个与显示器相匹配的CPaintDC类的设备上下文对象dc,但是OnPaint不再直接完成窗口输出,而是将设备上下文传给OnDraw()成员函数,由OnDraw()函数去完成窗口输出。当打印输出时,框架会调用视图的DoPreparePrinting创建一个与打印机相匹配的设备上下文并将该DC传递给OnDraw()函数,由OnDraw函数完成打印输出。这样,OnDraw()函数就把用于屏幕显示和打印机输出的工作统一起来,真正体现了设备无关的思想。如果想知道当前OnDraw函数是在用于屏幕显示还是打印输出,可以调用CView::IsPrinting()函数。当处于打印状态时,IsPrinting()返回TRUE;在用于屏幕显示时,返回FALSE。</p>
    <p><font color="#3973DE">文档修改时通知视图的更新</font></p>
    <p>当文档以某种方式变化时,必须通知视图作相应的更新即重绘,以反应文档的变化。这种情况通常发生在用户通过视图修改文档时。此时,视图将调用文档的UpdateAllViews成员函数通知同一文档的所有视图对自己进行更新。UpdateAllViews将调用每个视图的OnUpdate成员函数,使视图的客户区无效。</font></p>
    <p><font color="#3973DE">5 视图的消息处理</font><font SIZE="3"></p>
    <p>视图作为一个子窗口,当然可以处理消息。但是应用程序运行时,除了视图外,还有应用程序对象、主框架窗口、文档等,它们都是可以处理消息的。那么消息传递过程是什么样的呢?</p>
    <p>MFC的命令消息按以下方式传递:</p>
    <p align="center"></font><img src="T7_9.gif" alt="T7_9.tif (83342 bytes)" WIDTH="473" HEIGHT="130"><font SIZE="3"></p>
    <p align="center">图<font SIZE="3">7-9 </font>文档视结构中的消息传递</p>
    <p><font color="#3973DE">键盘消息处理</font></p>
    <p>前面的视图绘制就是完成窗口消息WM_PAINT的处理。编辑器要接收用户的键盘输入,就必须处理键盘消息;另外,在用户输入字符时,还必须马上就把用户输入的内容在屏幕上显示出来。</p>
    <p>用ClassWizard生成处理WM_CHAR消息的函数OnChar(),然后打开该函数进行编辑。修改后的OnChar函数如清单7.9:</p>
    <p>清单7.9 CEditorView的OnChar()成员函数</p>
    <p>void CEditorView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)</p>
    <p>{</p>
    <p> </p>
    <p>CEditorDoc* pDoc=GetDocument();</p>
    <p>CClientDC dc(this);</p>
    <p> </p>
    <p>CFont *oldFont;</p>
    <p>//选择新字体</p>
    <p>oldFont=dc.SelectObject(pFont);</p>
    <p> </p>
    <p>CString line(&quot;&quot;);//存放编辑器当前行字符串</p>
    <p>POSITION pos=NULL;//字符串链表位置指示</p>
    <p>if(nChar=='\r')</p>
    <p>{</p>
    <p>pDoc-&gt;nLineNum++;</p>
    <p>}</p>
    <p>else</p>
    <p>{</p>
    <p>//按行号返回字符串链表中位置值</p>
    <p>pos=pDoc-&gt;lines.FindIndex(pDoc-&gt;nLineNum);</p>
    <p>if(!pos)</p>
    <p>{</p>
    <p>//没有找到该行号对应的行,因此它是一个空行,</p>
    <p>//我们把它加到字符串链表中。</p>
    <p>line+=(char)nChar;</p>
    <p>pDoc-&gt;lines.AddTail(CString(line));</p>
    <p>}</p>
    <p>else{</p>
    <p>//当前文本行还没有换行结束,因此将文本加入到行末</p>
    <p>line=pDoc-&gt;lines.GetAt(pos);</p>
    <p>line+=(char)nChar;</p>
    <p>pDoc-&gt;lines.SetAt(pos,line);</p>
    <p>}</p>
    <p> </p>
    <p>TEXTMETRIC tm;</p>
    <p>dc.GetTextMetrics(&amp;tm);</p>
    <p>dc.TextOut(0,</p>
    <p>(int)pDoc-&gt;nLineNum*tm.tmHeight,</p>
    <p>line,</p>
    <p>line.GetLength());</p>
    <p>}</p>
    <p>pDoc-&gt;SetModifiedFlag();</p>
    <p>dc.SelectObject(oldFont);</p>
    <p>CView::OnChar(nChar,nRepCnt,nFlags);</p>
    <p>}</p>
    <p>因为编辑器要将用户输入内容加入到文本行缓冲区中,因此首先调用GetDocument()获取指向文档的指针,以便对文档中的数据进行修改。</p>
    <p>为了在收到键盘输入消息后在窗口中输入字符,需要定义一个CClientDC类的对象dc。CClientDC是用于管理窗口客户区的设备上下文对象,它在构造函数中调用GetDC()取得窗口客户区设备上下文,在析构函数中调用ReleaseDC()释放该设备上下文。CClientDC同样用于在窗口客户区的输出,它与CPaintDC不同之处在于:</p>
    <p>CPaintDC专门用于在窗口OnPaint()中的输出,而不能用于其它非窗口重画消息的处理。如果不是在OnDraw或OnPaint()中绘图,则需要创建一个CClientDC对象,然后调用CClientDC的方法来完成绘图输出。</p>
    <p>OnChar()接下去处理用户输入。如果输入是一个回车,则将总行数nLineNum加一,否则将输入字符加到当前行行末。最后调用TextOut函数输出当前编辑中的文本行。</p>
    <p>最后调用文档的SetModifiedFlag()方法设置文档的修改标志。SetModifiedFlag()函数原型如下:</p>
    <blockquote>
      <blockquote>
        <p>void SetModifiedFlag( BOOL bModified = TRUE );</font></p>
      </blockquote>
    </blockquote>
    <p><font SIZE="2"> </p>
    <p></font><font SIZE="3">从函数原型可以看出,函数缺省参数为TRUE。当调用SetModifiedFlag时,将文档内的修改标志置为真。如果用户执行了Save或Save 
    As操作,则将文档的修改标志置为假。这样,当用户关闭文档的最后一个视图时,框架根据该修改标记决定是否提示用户保存文档中的数据到文件。如果用户上次作了修改还没有存盘,则弹出一个消息框,提示是否保存文件。这些都是框架程序来完成的。</p>
    <p>用户如果在视图的其它任何地方修改了文档,也必须调用SetModifiedFlag来设置文档修改标记,以便关闭窗口时让框架提示保存文档。</p>
    <p></font><font SIZE="3" color="#3973DE">菜单消息处理 </font><font SIZE="3"></p>
    <blockquote>
      <blockquote>
      </blockquote>
    </blockquote>
    <font SIZE="3"><p> </p>
    <p>现在还要增加一个菜单,用户选择菜单时会弹出一个字体选择对话框,让用户选择视图输出文档时所用的字体。用菜单编辑器在View菜单下增加一个菜单项“Select 
    Font”,菜单项相关参数如下:</p>
    <p>菜单名:Select &amp;Font</p>
    <p>菜单ID:ID_SELECT_FONT</p>
    <p>提示文字:Select a font for current view</p>
    <p>然后用ClassWizard为该菜单项生成消息处理函数SelectFont。在选择消息响应的类时,用户可以选择文档、视图、框架或应用程序类,这根据具体情况而定。如果操作是针对某一视图(比如象本例中改变字体操作),则消息处理放在视图中比较合适。如果操作是针对文档的(比如要显示文档中对象的属性等),则放在文档中处理比较合适。如果选项对应用程序中的所有文档和视图都有效(即是全局的选项),那么可以把它放在框架窗口中。</p>
    <p ALIGN="JUSTIFY">修改OnSelectFont()函数,使它能显示字体选择对话框,修改后的OnSelect函数见清单7.10:</p>
    <p ALIGN="JUSTIFY"> </p>
    <p> </p>
    <p ALIGN="JUSTIFY">清单7.10 OnSelectFont()函数</p>
    <p>void CEditorView::OnSelectFont()</p>
    <p>{</p>
    <p> </p>
    <p>CFontDialog dlg;</p>
    <p> </p>
    <p>if(dlg.DoModal()==IDOK)</p>
    <p>{</p>
    <p>LOGFONT LF;</p>
    <p> </p>
    <p>//获取所选字体的信息</p>
    <p>dlg.GetCurrentFont(&amp;LF);</p>
    <p>//建立新的字体</p>
    <p>pFont-&gt;DeleteObject();</p>
    <p>pFont-&gt;CreateFontIndirect(&amp;LF);</p>
    <p> </p>
    <p>Invalidate();</p>
    <p>UpdateWindow();</p>
    <p>}</p>
    <p>}</p>
    <p>在OnSelectFont()消息处理函数中,首先定义一个选择字体公用对话框,然后显示该对话框,返回所选的字体。有关选择字体公用对话框的知识参见第五章对话框技术。字体对话框通过GetCurrentFont()返回逻辑字体信息。所谓逻辑字体是一种结构,它包含了字体的各种属性的描述,包括字体的名字、宽度、高度和是否斜体、加粗等信息。字体对象首先通过DeleteObject删除原来的字体对象,然后通过CreateFontIndirect、利用逻辑字体的属性来创建字体。由于我们选择了一种新的字体,所以要用新的字体来重绘视图。为此,调用Invalidate()函数向视图发送WM_PAINT消息。由于WM_PAINT消息级别比较低,不会立即被处理。因此,再调用UpdateWindow()强制窗口更新。这也是一种常用的技巧。</p>
    <p>现在已经完成了编辑器文档类和视图类的设计,对主框架窗口类不需要修改。编译、链接并运行程序,弹出文本编辑器窗口。试着输入几行文本,存盘。然后再载入刚才保存的文件,如图7-10。在File-Exit菜单项上面,有一个文件名列表,列出最近打开过的文件,这个表称作MRU表(MRU是英文Most 
    Recently Used的缩写)。可以从MRU中选择一个文件名,打开该文件。</p>
    <p align="center"></font><img src="T7_10.gif" alt="T7_10.tif (267820 bytes)" WIDTH="515" HEIGHT="368"><font SIZE="3"></p>
    </font><p align="center"></font><font size="2">图7-10 一个简单的文本编辑器</font><font SIZE="3"></p>
    <div align="center"><center><table border="0" cellpadding="0" cellspacing="0" width="615">
      <tr>
        <td><a href="chap7_1.htm">上一页</a></td>
        <td><p align="right"><a href="chap7_3.htm">下一页</a></td>
      </tr>
    </table>
    </center></div><font SIZE="5"><hr noshade color="#3973DE" size="1">
    <p align="center"></font><font size="2" color="#000000">本教程由<a href="http://vcdynasty.yeah.net">Visual C++王朝(Where programmers come together)</a>协助制作<br>
    未经许可,请勿以任何形式复制</font></font></td>
  </tr>
</table>
</center></div>

<p ALIGN="CENTER"><font SIZE="5"> </p>
</font><font SIZE="3">

<p> </p>
</font>

<p><font SIZE="3"> </p>

<p></font> </p>
</body>
</html>

⌨️ 快捷键说明

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