📄 chap7_3.htm
字号:
<p><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">其实,我们在前面介绍弹出式菜单时已经使用了</font><font FACE="Times New Roman" SIZE="3">ClientToScreen</font><font SIZE="3">函数。在那里,由于弹出式菜单使用的是屏幕坐标,因此当处理弹出式菜单快捷键</font><font FACE="Times New Roman" SIZE="3">shift+F10</font><font SIZE="3">时,如果要在窗口左上角</font><font FACE="Times New Roman" SIZE="3">(5,5)</font><font SIZE="3">处显示快捷菜单,就必须先调用</font><font FACE="Times New Roman" SIZE="3">ClientToScreen</font><font SIZE="3">函数将客户区坐标(</font><font FACE="Times New Roman" SIZE="3">5,5</font><font SIZE="3">)转化为屏幕坐标。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>CRect rect;</p>
<p>GetClientRect(rect);</p>
<p>ClientToScreen(rect);</p>
<p>point = rect.TopLeft();</p>
<p>point.Offset(5, 5);</p>
<p></font><font SIZE="3">在视图滚动后,如果用户在视图中单击鼠标,那么会得到鼠标位置的设备(视图)坐标。在使用这个数据处理文档(比如画点或画线)时,需要把它转化为文档坐标。这是因为利用</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">绘图时,所有传递给</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">作图的坐标都是逻辑坐标。当调用</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">绘图函数绘图时,</font><font FACE="Times New Roman" SIZE="3">Windows</font><font SIZE="3">自动将逻辑坐标转换成设备坐标,然后再绘图。设备上下文类</font><font FACE="Times New Roman" SIZE="3">CDC</font><font SIZE="3">提供了两个成员函数</font><font FACE="Times New Roman" SIZE="3">LPToDP</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">DPToLP</font><font SIZE="3">完成逻辑坐标和设备坐标之间的转换工作。如其名字所示那样,</font><font FACE="Times New Roman" SIZE="3">LPToDP</font><font SIZE="3">将逻辑坐标转换为设备坐标,</font><font FACE="Times New Roman" SIZE="3">DPToLP</font><font SIZE="3">将设备坐标转换为逻辑坐标。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>void LPtoDP( LPPOINT lpPoints, int nCount = 1 ) const;</p>
<p>void LPtoDP( LPRECT lpRect ) const;</p>
<blockquote>
<blockquote>
<p>void LPtoDP( LPSIZE lpSize ) const;</p>
<p>void DPtoLP( LPPOINT lpPoints, int nCount = 1 ) const;</p>
<p>void DPtoLP( LPRECT lpRect ) const;</p>
<p>void DPtoLP( LPSIZE lpSize ) const;</p>
</font><p> </p>
</blockquote>
</blockquote>
<font SIZE="3"><b><p></b><font color="#3973DE">7.3.2 滚动文档</font></p>
</font><font FACE="Times New Roman" SIZE="3"><p></font><font SIZE="3">由于</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">绘图函数使用的是逻辑坐标,因此用户可以在一个假想的通常是比视图要大的“文档窗口”中绘图;</font><font FACE="Times New Roman" SIZE="3">Windows</font><font SIZE="3">自动在幕后完成坐标转换工作,并将落在视图范围内的那一部分“文档窗口”显示出来,其余的部分被裁剪。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">但是光这样还不能卷滚文档。要卷滚显示文档,还必须知道文档卷滚到了什么位置;一旦用户拖动滚动条时要告诉视图改变在文档中的相应位置。所有这些,由</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">的</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">来完成。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>MFC</font><font SIZE="3">提供了</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">类,简化了滚动需要处理的大量工作。除了管理文档中的滚动操作外,</font><font FACE="Times New Roman" SIZE="3">MFC</font><font SIZE="3">还通过调用</font><font FACE="Times New Roman" SIZE="3">Windows API</font><font SIZE="3">函数画出滚动条、箭头和滚动光标。它还负责处理:</p>
<blockquote>
<p>用户初始化滚动条范围</font><font FACE="Times New Roman" SIZE="3">(</font><font SIZE="3">通过滚动视图的</font><font FACE="Times New Roman" SIZE="3">SetScrollRange()</font><font SIZE="3">方法</font><font FACE="Times New Roman" SIZE="3">)</p>
</font><font SIZE="3"><p>处理滚动条消息,并滚动文档到相应位置</p>
<p>管理窗口和视图的尺寸大小</p>
<p>调整滚动条上滑块(或称拇指框)的位置,使之与文档当前位置相匹配</font></p>
</blockquote>
<p><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">程序员要做的工作是:</p>
<blockquote>
<p>从</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">类中派生出自己的视图类,以支持卷滚</p>
<p>提供文档大小,确定滚动范围和设置初始位置</p>
<p>协调文档位置和屏幕坐标</font></p>
</blockquote>
<p><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">要让应用程序支持卷滚,可以在用</font><font FACE="Times New Roman" SIZE="3">AppWizard</font><font SIZE="3">生成框架程序时就指定视图的基类为</font><font FACE="Times New Roman" SIZE="3">CSrollView</font><font SIZE="3">。可以在</font><font FACE="Times New Roman" SIZE="3">AppWizard</font><font SIZE="3">的</font><font FACE="Times New Roman" SIZE="3">MFC AppWizard-Step 6 of 6</font><font SIZE="3">对话框中,在对话框上方应用程序所包含的类中选择</font><font FACE="Times New Roman" SIZE="3">CEditorView</font><font SIZE="3">,然后在</font><font FACE="Times New Roman" SIZE="3">Base Class</font><font SIZE="3">下拉列表框中选择应用程序视图类的基类为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">,如图</font><font FACE="Times New Roman" SIZE="3">7-11</font><font SIZE="3">所示:</font><font FACE="Times New Roman" SIZE="3"></p>
<p align="center"></font><img src="T7_13.gif" alt="T7_13.tif (317268 bytes)" WIDTH="504" HEIGHT="405"><font FACE="Times New Roman" SIZE="3"></p>
<p align="center"></font><font SIZE="3">图</font><font FACE="Times New Roman" SIZE="3">7-13
</font><font SIZE="3">为应用程序的视图类指定基类</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">现在我们要手工修改</font><font FACE="Times New Roman" SIZE="3">CEditorView</font><font SIZE="3">,使它的基类为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>1. </font><font SIZE="3">修改视图类所对应的头文件,将所有用到</font><font FACE="Times New Roman" SIZE="3">CView</font><font SIZE="3">的地方改为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">。通常,首先修改视图类赖以派生的父类,形式如下:</font><font FACE="Times New Roman" SIZE="3"></p>
<p>class CEditorView:public CScrollView</p>
<p>2. </font><font SIZE="3">修改视图类实现的头文件,把所有用到</font><font FACE="Times New Roman" SIZE="3">CView</font><font SIZE="3">的地方改为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">。首先修改</font><font FACE="Times New Roman" SIZE="3">IMPLEMENT_DYNACREATE</font><font SIZE="3">一行:</font><font FACE="Times New Roman" SIZE="3"></p>
<p>IMPLEMENT_DYNACREATE(CEditorView,CScrollView)</p>
<p></font><font SIZE="3">然后修改</font><font FACE="Times New Roman" SIZE="3">BEGIN_MESSAGE_MAP</font><font SIZE="3">宏</font><font FACE="Times New Roman" SIZE="3"></p>
<p>BEGIN_MESSAGE_MAP(CEditorView,CScrollView)</p>
<p></font><font SIZE="3">然后将其他所有用到</font><font FACE="Times New Roman" SIZE="3">CView</font><font SIZE="3">的地方改为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">一个更简单的方法是:使用</font><font FACE="Times New Roman" SIZE="3">Edit-Replace</font><font SIZE="3">功能,进行全局替换。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">到现在为止,已经将编辑器视图类</font><font FACE="Times New Roman" SIZE="3">CEditorView</font><font SIZE="3">的基类由</font><font FACE="Times New Roman" SIZE="3">CView</font><font SIZE="3">转化为</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">现在,要设置文档大小,以便让</font><font FACE="Times New Roman" SIZE="3">CScrollView</font><font SIZE="3">知道该如何处理文档。视图必需知道文档的卷滚范围,这样才能确定何时卷滚到文档的头部和尾部,以及当拖动卷滚条的滑块时按适当比例调整文档当前显示位置。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">为此,我们首先在文档类</font><font FACE="Times New Roman" SIZE="3">CEditorDoc</font><font SIZE="3">的头文件</font><font FACE="Times New Roman" SIZE="3">editordoc.h</font><font SIZE="3">中增加一个</font><font FACE="Times New Roman" SIZE="3">CSize</font><font SIZE="3">类型的数据成员</font><font FACE="Times New Roman" SIZE="3">m_sizeDoc</font><font SIZE="3">用以表示文档的大小。</font><font FACE="Times New Roman" SIZE="3">CSize</font><font SIZE="3">对象包含</font><font FACE="Times New Roman" SIZE="3">cx</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">cy</font><font SIZE="3">两个数据成员,分别用于存放文档的</font><font FACE="Times New Roman" SIZE="3">x</font><font SIZE="3">方向坐标范围和</font><font FACE="Times New Roman" SIZE="3">y</font><font SIZE="3">方向坐标范围。另外,还要提供一个成员函数</font><font FACE="Times New Roman" SIZE="3">GetDocSize()</font><font SIZE="3">来访问该文档大小范围数据成员。修改后的</font><font FACE="Times New Roman" SIZE="3">editordoc.h</font><font SIZE="3">如清单</font><font FACE="Times New Roman" SIZE="3">7.11</font><font SIZE="3">。<b></p>
<p>清单</font><font FACE="Times New Roman" SIZE="3">7.11 CEditorDoc</font><font SIZE="3">头文件</font></b><font FACE="Times New Roman" SIZE="3"></p>
<p>class CEditorDoc : public CDocument</p>
<p>{</p>
<p>protected: // create from serialization only</p>
<p>CEditorDoc();</p>
<p>DECLARE_DYNCREATE(CEditorDoc)</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>//</font><font SIZE="3">保存文档大小</font><font FACE="Times New Roman" SIZE="3"></p>
<p>CSize m_sizeDoc;</p>
<p>// Attributes</p>
<p>public:</p>
<p>CSize GetDocSize(){return m_sizeDoc;}</p>
<p></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>// Operations</p>
<p>public:</p>
<p>CStringList lines;</p>
<p>int nLineNum;</b></p>
<p>......</p>
<p>};</p>
<p></font><font SIZE="3">既然增加了</font><font FACE="Times New Roman" SIZE="3">m_sizeDoc</font><font SIZE="3">这一数据成员,就需要在</font><font FACE="Times New Roman" SIZE="3">CEditorDoc</font><font SIZE="3">构造函数中进行初始化,给</font><font FACE="Times New Roman" SIZE="3">m_sizeDoc</font><font SIZE="3">设置一合理的数值,比如说</font><font FACE="Times New Roman" SIZE="3">x=700</font><font SIZE="3">,</font><font FACE="Times New Roman" SIZE="3">y=800</font><font SIZE="3">。构造函数如清单</font><font FACE="Times New Roman" SIZE="3">7.12</font><font SIZE="3">。<b></p>
<p>清单</font><font FACE="Times New Roman" SIZE="3">7.12 CEditorDoc</font><font SIZE="3">的构造函数</font></b><font FACE="Times New Roman" SIZE="3"></p>
<p>CEditorDoc::CEditorDoc()</p>
<p>{</p>
<p>// TODO: add one-time construction code here</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>nLineNum=0;</p>
<p>m_sizeDoc=CSize(700,800);</b></p>
<p>}</p>
<p></font><font SIZE="3">一个设计优秀的应用程序应当能够动态调整文档的卷滚范围。比如,在</font><font FACE="Times New Roman" SIZE="3">WORD</font><font SIZE="3">中新建一个文件时,在“页面模式”下将可卷滚范围设为一页大小。随着用户输入,逐渐增加文档的卷滚范围。但是这里为简明起见,将文档卷滚范围设为固定大小</font><font FACE="Times New Roman" SIZE="3">700X800</font><font SIZE="3">点像素大小。设置文档大小通过由视图类的</font><font FACE="Times New Roman" SIZE="3">CEditorView::OnInitialUpdate()</font><font SIZE="3">调用</font><font FACE="Times New Roman" SIZE="3">SetScrollSizes()</font><font SIZE="3">成员函数来完成。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>SetScrollSizes()</font><font SIZE="3">用于设置文档卷滚范围。一般在重载</font><font FACE="Times New Roman" SIZE="3">OnInitialUpdate()</font><font SIZE="3">成员函数或</font><font FACE="Times New Roman" SIZE="3">OnUpdate()</font><font SIZE="3">时调用该函数,用以调整文档卷滚特性。比如,在文档初始显示或文档大小作了调整之后。<b></p>
<p>清单</font><font FACE="Times New Roman" SIZE="3">7.13 </font><font SIZE="3">在</font><font FACE="Times New Roman" SIZE="3">OnInitialUpdate()</font><font SIZE="3">中设置卷滚范围</font></b><font FACE="Times New Roman" SIZE="3"></p>
<p>void CEditorView::OnInitialUpdate() </p>
<p>{</p>
<p></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>// TODO: Add your specialized code here and/or call the base class</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>CDC *pDC=GetDC();</p>
<p>pFont=new CFont();</p>
<p>if(!(pFont->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,"Courier New")))</p>
<p>{</p>
<p>pFont->CreateStockObject(SYSTEM_FONT);</p>
<p>}</p>
<p>CFont* oldFont=pDC->SelectObject(pFont);</p>
<p>TEXTMETRIC tm;</p>
<p>pDC->GetTextMetrics(&tm);</p>
<p>lHeight=tm.tmHeight+tm.tmExternalLeading;</p>
<p>cWidth=tm.tmAveCharWidth;</p>
<p>SetScrollSizes(MM_TEXT,GetDocument()->GetDocSize());</b></p>
<p>CScrollView::OnInitialUpdate();</p>
<p>}</p>
<p></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>SetScrollSizes()</font><font SIZE="3">第一个参数为映射模式。</font><font FACE="Times New Roman" SIZE="3">SetScrollSizes()</font><font SIZE="3">可以使用除</font><font FACE="Times New Roman" SIZE="3">MM_ISOTROPIC</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">MM_ANISOTROPIC</font><font SIZE="3">之外的其他任何映射模式。</font><font FACE="Times New Roman" SIZE="3">SetScrollSizes()</font><font SIZE="3">第二个参数为文档大小,用一个</font><font FACE="Times New Roman" SIZE="3">CSize</font><font SIZE="3">类型的数值表示。</font><font FACE="Times New Roman" SIZE="3"></p>
<p></font><font SIZE="3">另外,我们还要检查两个包含绘图输出功能的函数:</font><font FACE="Times New Roman" SIZE="3">CEditorView::OnChar()</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">CEditorView::OnDraw()</font><font SIZE="3">函数。</font><font FACE="Times New Roman" SIZE="3"></p>
<p>void CEditorView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) </p>
<p>{</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p>CEditorDoc* pDoc=GetDocument();</p>
<p>CClientDC dc(this);</p>
<p>CString line("");//</font><font SIZE="3">存放编辑器当前行字符串</font><font FACE="Times New Roman" SIZE="3"></p>
<p>POSITION pos=NULL;//</font><font SIZE="3">字符串链表位置指示</font><font FACE="Times New Roman" SIZE="3"></p>
<p>if(nChar=='\r')</p>
<p>{</p>
<p>pDoc->nLineNum++;</p>
<p>}</p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -