📄 chap7_2.htm
字号:
<p><font >ASSERT_VALID(pDoc);</font></p>
<p><font >// TODO: add draw code for native data
here</font></p>
<p><font >CFont *oldFont;</font></p>
<p><font >//选择新字体</font></p>
<p><font >oldFont=pDC->SelectObject(pFont);</font></p>
<p><font >//纵向yval坐标为0</font></p>
<p><font >int yval=0;</font></p>
<p><font >POSITION pos;</font></p>
<p><font >CString line;</font></p>
<p><font >//取得文本行链表的头指针</font></p>
<p><font >if(!(pos=pDoc->lines.GetHeadPosition()))</font></p>
<p><font >{</font></p>
<p><font >return;</font></p>
<p><font >}</font></p>
<p><font >//循环输出各文本行</font></p>
<p><font >while(pos!=NULL)</font></p>
<p><font >{</font></p>
<p><font >line=pDoc->lines.GetNext(pos);</font></p>
<p><font >pDC->TextOut(0,</font></p>
<p><font >yval,</font></p>
<p><font >line,</font></p>
<p><font >line.GetLength());</font></p>
<p><font >//更新y坐标值,让它加上文本行所用字体的高度</font></p>
<p><font >yval+=lHeight;</font></p>
<p><font >}</font></p>
<p><font >//恢复原来DC所用的字体</font></p>
<p><font >pDC->SelectObject(pFont);</font></p>
<p><font >}</font></p>
<p><font > 框架调用视图的CView::OnDraw(CDC* pDC)方法完成屏幕显示、打印、打印预览功能,对于不同的输出功能它会传递不同的DC指针给OnDraw()函数。</font></p>
<p><font > 在OnDraw()函数中,首先调用GetDocument()函数,取得指向当前视图所对应的文档的指针。通过这个指针,来访问文档中的数据。以后在视图中修改文档中的数据,也是通过GetDocument()来取得文档指针,再通过该文档指针修改文档中的数据。</font></p>
<p><font > 在绘图时,可以通过传给OnDraw函数的一个设备上下文DC的指针pDC进行GDI调用。开始绘图之前,往往需要选择GDI资源(或GDI对象,包括画笔、刷子、字体等),将它选入到设备上下文中。在文本编辑器中,我们选择一种字体pFont到设备上下文中,以后在窗口客户区的文本输出就都会使用该字体绘制。在绘制过程中,绘图代码是设备无关的,也就是说它并不需要知道目前使用的是什么设备(屏幕、打印机或其他绘图设备)。</font></p>
<p><font > 读者以前如果用Borland C++或SDK编写过Windows程序的话,都会知道:当窗口或窗口的一部分变成无效的话(比如其他窗口从本窗口上拖过、窗口调整大小等),操作系统就会向窗口发送一条WM_PAINT消息。窗口接收到该消息之后,调用Borland
C++的EvPaint()或Visual C++的OnPaint()完成窗口绘制工作。这里OnDraw()函数也同样完成窗口绘图输出,这两者有什么关系呢?</font></p>
<p><font >我们先看一下OnPaint()函数:</font></p>
<p><font >void CMyWindow::OnPaint()</font></p>
<p><font >{</font></p>
<p><font >CPaintDC dc(this); //用于窗口绘制的设备上下文</font></p>
<p><font >CString str(“Hello,world!”);</font></p>
<p><font >...</font></p>
<p><font >//绘图输出代码</font></p>
<p><font >dc.TextOut(10,10,str,str.GetLength());</font></p>
<p><font >}</font></p>
<p><font > 在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不被消除,会产生不断重画的现象。</font></p>
<p><font > 视图是一个子窗口,它自然也从窗口类继承了OnPaint()成员函数,用以响应WM_PAINT消息。类似于上面的例子,视图OnPaint处理函数首先创建一个与显示器相匹配的CPaintDC类的设备上下文对象dc,但是OnPaint不再直接完成窗口输出,而是将设备上下文传给OnDraw()成员函数,由OnDraw()函数去完成窗口输出。当打印输出时,框架会调用视图的DoPreparePrinting创建一个与打印机相匹配的设备上下文并将该DC传递给OnDraw()函数,由OnDraw函数完成打印输出。这样,OnDraw()函数就把用于屏幕显示和打印机输出的工作统一起来,真正体现了设备无关的思想。如果想知道当前OnDraw函数是在用于屏幕显示还是打印输出,可以调用CView::IsPrinting()函数。当处于打印状态时,IsPrinting()返回TRUE;在用于屏幕显示时,返回FALSE。</font></p>
<p><font color="#3973DE" >文档修改时通知视图的更新</font></p>
<p><font > 当文档以某种方式变化时,必须通知视图作相应的更新即重绘,以反应文档的变化。这种情况通常发生在用户通过视图修改文档时。此时,视图将调用文档的UpdateAllViews成员函数通知同一文档的所有视图对自己进行更新。UpdateAllViews将调用每个视图的OnUpdate成员函数,使视图的客户区无效。</font></p>
<p><font color="#3973DE">5 视图的消息处理</font></p>
<p><font > 视图作为一个子窗口,当然可以处理消息。但是应用程序运行时,除了视图外,还有应用程序对象、主框架窗口、文档等,它们都是可以处理消息的。那么消息传递过程是什么样的呢?</font></p>
<p><font >MFC的命令消息按以下方式传递:</font></p>
<p align="center"><img src="T7_9.gif"
alt="T7_9.tif (83342 bytes)" width="473" height="130"></p>
<p align="center"><font >图7-9
文档视结构中的消息传递</font></p>
<p><font color="#3973DE" >键盘消息处理</font></p>
<p><font > 前面的视图绘制就是完成窗口消息WM_PAINT的处理。编辑器要接收用户的键盘输入,就必须处理键盘消息;另外,在用户输入字符时,还必须马上就把用户输入的内容在屏幕上显示出来。</font></p>
<p><font >用ClassWizard生成处理WM_CHAR消息的函数OnChar(),然后打开该函数进行编辑。修改后的OnChar函数如清单7.9:</font></p>
<p><font >清单7.9 CEditorView的OnChar()成员函数</font></p>
<p><font >void CEditorView::OnChar(UINT nChar,
UINT nRepCnt, UINT nFlags)</font></p>
<p><font >{</font></p>
<p><font >CEditorDoc* pDoc=GetDocument();</font></p>
<p><font >CClientDC dc(this);</font></p>
<p><font >CFont *oldFont;</font></p>
<p><font >//选择新字体</font></p>
<p><font >oldFont=dc.SelectObject(pFont);</font></p>
<p><font >CString line("");//存放编辑器当前行字符串</font></p>
<p><font >POSITION pos=NULL;//字符串链表位置指示</font></p>
<p><font >if(nChar=='\r')</font></p>
<p><font >{</font></p>
<p><font >pDoc->nLineNum++;</font></p>
<p><font >}</font></p>
<p><font >else</font></p>
<p><font >{</font></p>
<p><font >//按行号返回字符串链表中位置值</font></p>
<p><font >pos=pDoc->lines.FindIndex(pDoc->nLineNum);</font></p>
<p><font >if(!pos)</font></p>
<p><font >{</font></p>
<p><font >//没有找到该行号对应的行,因此它是一个空行,</font></p>
<p><font >//我们把它加到字符串链表中。</font></p>
<p><font >line+=(char)nChar;</font></p>
<p><font >pDoc->lines.AddTail(CString(line));</font></p>
<p><font >}</font></p>
<p><font >else{</font></p>
<p><font >//当前文本行还没有换行结束,因此将文本加入到行末</font></p>
<p><font >line=pDoc->lines.GetAt(pos);</font></p>
<p><font >line+=(char)nChar;</font></p>
<p><font >pDoc->lines.SetAt(pos,line);</font></p>
<p><font >}</font></p>
<p><font >TEXTMETRIC tm;</font></p>
<p><font >dc.GetTextMetrics(&tm);</font></p>
<p><font >dc.TextOut(0,</font></p>
<p><font >(int)pDoc->nLineNum*tm.tmHeight,</font></p>
<p><font >line,</font></p>
<p><font >line.GetLength());</font></p>
<p><font >}</font></p>
<p><font >pDoc->SetModifiedFlag();</font></p>
<p><font >dc.SelectObject(oldFont);</font></p>
<p><font >CView::OnChar(nChar,nRepCnt,nFlags);</font></p>
<p><font >}</font></p>
<p><font > 因为编辑器要将用户输入内容加入到文本行缓冲区中,因此首先调用GetDocument()获取指向文档的指针,以便对文档中的数据进行修改。</font></p>
<p><font > 为了在收到键盘输入消息后在窗口中输入字符,需要定义一个CClientDC类的对象dc。CClientDC是用于管理窗口客户区的设备上下文对象,它在构造函数中调用GetDC()取得窗口客户区设备上下文,在析构函数中调用ReleaseDC()释放该设备上下文。CClientDC同样用于在窗口客户区的输出,它与CPaintDC不同之处在于:</font></p>
<p><font >CPaintDC专门用于在窗口OnPaint()中的输出,而不能用于其它非窗口重画消息的处理。如果不是在OnDraw或OnPaint()中绘图,则需要创建一个CClientDC对象,然后调用CClientDC的方法来完成绘图输出。</font></p>
<p><font > OnChar()接下去处理用户输入。如果输入是一个回车,则将总行数nLineNum加一,否则将输入字符加到当前行行末。最后调用TextOut函数输出当前编辑中的文本行。</font></p>
<p><font > 最后调用文档的SetModifiedFlag()方法设置文档的修改标志。SetModifiedFlag()函数原型如下:</font></p>
<blockquote>
<blockquote>
<p><font >void SetModifiedFlag( BOOL
bModified = TRUE );</font></p>
</blockquote>
</blockquote>
<p><font > 从函数原型可以看出,函数缺省参数为TRUE。当调用SetModifiedFlag时,将文档内的修改标志置为真。如果用户执行了Save或Save
As操作,则将文档的修改标志置为假。这样,当用户关闭文档的最后一个视图时,框架根据该修改标记决定是否提示用户保存文档中的数据到文件。如果用户上次作了修改还没有存盘,则弹出一个消息框,提示是否保存文件。这些都是框架程序来完成的。</font></p>
<p><font > 用户如果在视图的其它任何地方修改了文档,也必须调用SetModifiedFlag来设置文档修改标记,以便关闭窗口时让框架提示保存文档。</font></p>
<p><font color="#3973DE" >菜单消息处理 </font></p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -