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

📄 chap8_3.htm

📁 着重介绍基于C++的web编程技术
💻 HTM
📖 第 1 页 / 共 4 页
字号:
    <p>DECLARE_DYNCREATE(CDrawView)</p>
    <p>// Attributes</p>
    <p>public:</p>
    <p>CDrawDoc* GetDocument();</p>
    <p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
    <p>protected:</p>
    <p>CStroke* m_pStrokeCur; // the stroke in progress</p>
    <p>CPoint m_ptPrev; // the last mouse pt in the stroke in progress</b></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>m_pStrokeCur</font><font SIZE="3">代表正在画的那一个笔划。</font><font FACE="Times New Roman" SIZE="3">m_ptPrev</font><font SIZE="3">保存鼠标上次移动位置。画图时,</font><font FACE="Times New Roman" SIZE="3">LineTo</font><font SIZE="3">从这个点到当前鼠标位置画一条直线。<b></p>
    <p></b><font color="#3973DE">视图初始化</font></font><font FACE="Times New Roman" SIZE="3"></p>
    <p></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">File-&gt;New</font><font SIZE="3">菜单或</font><font FACE="Times New Roman" SIZE="3">File-&gt;Open</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"></p>
    <p>void CDrawView::OnInitialUpdate()</p>
    <p>{</p>
    <p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
    <p>SetScrollSizes(MM_LOENGLISH, GetDocument()-&gt;GetDocSize());</b></p>
    <p>CScrollView::OnInitialUpdate();</p>
    <p>}</p>
    <p></font><font SIZE="3">注意我们这里将映射模式设置为</font><font FACE="Times New Roman" SIZE="3">MM_LOENGLISH</font><font SIZE="3">,</font><font FACE="Times New Roman" SIZE="3">MM_LOENGLISH</font><font SIZE="3">以</font><font FACE="Times New Roman" SIZE="3">0.01</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">MM_TEXT</font><font SIZE="3">的</font><font FACE="Times New Roman" SIZE="3">y</font><font SIZE="3">轴递增方向相反。<b></p>
    <p></b><font color="#3973DE">视图绘制</font></font><font FACE="Times New Roman" SIZE="3"></p>
    <p></font><font SIZE="3">在</font><font FACE="Times New Roman" SIZE="3">CDrawView::OnDraw()</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"></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">8-5 </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">2</font><font SIZE="3">从窗口</font><font FACE="Times New Roman" SIZE="3">1</font><font SIZE="3">上移开后,只需要重画阴影线所包围的区域就够了。</font><font FACE="Times New Roman" SIZE="3"></p>
    <p ALIGN="JUSTIFY"></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">Windows</font><font SIZE="3">在应用程序队列中放置</font><font FACE="Times New Roman" SIZE="3">WM_PAINT</font><font SIZE="3">消息。由于</font><font FACE="Times New Roman" SIZE="3">WM_PAINT</font><font SIZE="3">消息优先级最低,可调用</font><font FACE="Times New Roman" SIZE="3">UpdateWindows</font><font SIZE="3">直接立即向窗口发送</font><font FACE="Times New Roman" SIZE="3">WM_PAINT</font><font SIZE="3">消息,从而立即重绘。无效矩形区限制程序只能在该区域中绘图,越界的绘图将被裁剪掉。下面三个函数与无效矩形有关:</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>InvalidateRect </font><font SIZE="3">产生一个无效矩形,并生成</font><font FACE="Times New Roman" SIZE="3">WM_PAINT</font><font SIZE="3">消息</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>ValidateRect </font><font SIZE="3">使无效矩形区有效</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>GetUpdateRect </font><font SIZE="3">获得无效矩形坐标(逻辑)</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>Windows</font><font SIZE="3">为每个窗口保留一个</font><font FACE="Times New Roman" SIZE="3">PAINTSTRUCT</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">OnUpdate</font><font SIZE="3">成员函数。</p>
    <blockquote>
      <blockquote>
        </font><b><font FACE="Arial" SIZE="3"><p></font><font FACE="Times New Roman" SIZE="3">virtual</b> 
        <b>void</b> <b>CView::OnUpdate(</b> <b>CView*</b> <i>pSender</i><b>,</b> <b>LPARAM</b> <i>lHint</i><b>,</b> 
        <b>CObject*</b> <i>pHint</i> <b>);</b></p>
      </blockquote>
    </blockquote>
    <p ALIGN="JUSTIFY"></font><font SIZE="3">当调用文档的</font><font FACE="Times New Roman" SIZE="3">UpdateAllViews</font><font SIZE="3">时,框架会自动调用</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">函数,也可在视图类中直接调用该函数。</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">函数一般是这样处理的:访问文档,读取文档的数据,然后对视图的数据成员或控制进行更新,以反映文档的改动。可以用</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">函数使视图的某部分无效。以便触发视的</font><font FACE="Times New Roman" SIZE="3">OnDraw</font><font SIZE="3">,利用文档数据重绘窗口。缺省的</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">使窗口整个客户区都无效,在重新设计时,要利用提示信息</font><font FACE="Times New Roman" SIZE="3">lHint</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">pHint</font><font SIZE="3">定义一个较小的无效矩形。修改后的</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">成员函数如清单</font><font FACE="Times New Roman" SIZE="3">8.5</font><font SIZE="3">。<b></p>
    <p>清单</font><font FACE="Times New Roman" SIZE="3">8.5 </font><font SIZE="3">修改后的</font><font FACE="Times New Roman" SIZE="3">OnUpdate</font><font SIZE="3">成员函数</font></b><font FACE="Times New Roman" SIZE="3"></p>
    <p>void CDrawView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) </p>
    <p>{</p>
    <p>// TODO: Add your specialized code here and/or call the base class</p>
    <p>// The document has informed this view that some data has changed.</p>
    <p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
    <p>if (pHint != NULL)</p>
    <p>{</p>
    <p>if (pHint-&gt;IsKindOf(RUNTIME_CLASS(CStroke)))</p>
    <p>{</p>
    <p>// The hint is that a stroke as been added (or changed).</p>
    <p>// So, invalidate its rectangle.</p>
    <p>CStroke* pStroke = (CStroke*)pHint;</p>
    <p>CClientDC dc(this);</p>
    <p>OnPrepareDC(&amp;dc);</p>
    <p>CRect rectInvalid = pStroke-&gt;GetBoundingRect();</p>
    <p>dc.LPtoDP(&amp;rectInvalid);</p>
    <p>InvalidateRect(&amp;rectInvalid);</p>
    <p>return;</p>
    <p>}</b></p>
    <p>}</p>
    <p>// We can't interpret the hint, so assume that anything might</p>
    <p>// have been updated.</p>
    <p>Invalidate(TRUE);</p>
    <p>return;</p>
    <p>}</p>
    <p ALIGN="JUSTIFY"></font><font SIZE="3">这里,传给</font><font FACE="Times New Roman" SIZE="3">pHint</font><font SIZE="3">指针的内容是指向需要绘制的笔画对象的指针。采用强制类型转换将它转换为笔划指针,然后取得包围该笔划的最小矩形。</font><font FACE="Times New Roman" SIZE="3">OnPrepareDC</font><font SIZE="3">用于调整视图坐标原点。由于</font><font FACE="Times New Roman" SIZE="3">InvalidateRect</font><font SIZE="3">需要设备坐标,因此调用</font><font FACE="Times New Roman" SIZE="3">LPToDP(&amp;rectInvalid)</font><font SIZE="3">将逻辑坐标转换为设备坐标。最后,调用</font><font FACE="Times New Roman" SIZE="3">InvalidateRect</font><font SIZE="3">是窗口部分区域“无效”,也就是视图在收到</font><font FACE="Times New Roman" SIZE="3">WM_PAINT</font><font SIZE="3">消息后需要重绘这一区域。</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>InvalidateRect</font><font SIZE="3">函数原型为:</font><font FACE="Times New Roman" SIZE="3"></p>
    <blockquote>
      <blockquote>
        <p>void InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE );</p>
      </blockquote>
    </blockquote>
    <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></font><font SIZE="3">如果</font><font FACE="Times New Roman" SIZE="3">pHint</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">OnDraw</font><font SIZE="3">中,根据无效矩形绘制图形,而不是重绘全部笔划,见清单</font><font FACE="Times New Roman" SIZE="3">8.6</font><font SIZE="3">。<b></p>
    <p>清单</font><font FACE="Times New Roman" SIZE="3">8.6 </font><font SIZE="3">根据无效矩形绘制图形的</font><font FACE="Times New Roman" SIZE="3">OnDraw</font><font SIZE="3">成员函数</font></b><font FACE="Times New Roman" SIZE="3"></p>
    <p>void CDrawView::OnDraw(CDC* pDC)</p>
    <p>{</p>
    <p>CDrawDoc* pDoc = GetDocument();</p>
    <p>ASSERT_VALID(pDoc);</p>
    <p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
    <p>// Get the invalidated rectangle of the view, or in the case</p>
    <p>// of printing, the clipping region of the printer dc.</p>
    <p>CRect rectClip;</p>
    <p>CRect rectStroke;</p>
    <p>pDC-&gt;GetClipBox(&amp;rectClip);</p>
    <p>pDC-&gt;LPtoDP(&amp;rectClip);</p>
    <p>rectClip.InflateRect(1, 1); // avoid rounding to nothing</p>
    <p>// Note: CScrollView::OnPaint() will have already adjusted the</p>
    <p>// viewport origin before calling OnDraw(), to reflect the</p>
    <p>// currently scrolled position.</p>
    <p>// The view delegates the drawing of individual strokes to</p>
    <p>// CStroke::DrawStroke().</p>
    <p>CTypedPtrList&lt;CObList,CStroke*&gt;&amp; strokeList = pDoc-&gt;m_strokeList;</p>
    <p>POSITION pos = strokeList.GetHeadPosition();</p>
    <p>while (pos != NULL)</p>
    <p>{</p>
    <p>CStroke* pStroke = strokeList.GetNext(pos);</p>
    <p>rectStroke = pStroke-&gt;GetBoundingRect();</p>
    <p>pDC-&gt;LPtoDP(&amp;rectStroke);</p>
    <p>rectStroke.InflateRect(1, 1); // avoid rounding to nothing</p>
    <p>if (!rectStroke.IntersectRect(&amp;rectStroke, &amp;rectClip))</p>
    <p>continue;</p>
    <p>pStroke-&gt;DrawStroke(pDC);</p>
    <p>}</b></p>
    <p>// TODO: add draw code for native data here</p>
    <p>}</p>
    <p>OnDraw</font><font SIZE="3">首先调用</font><font FACE="Times New Roman" SIZE="3">GetClipBox</font><font SIZE="3">取得当前被剪裁区域(无效矩形区域),它把矩形复制导</font><font FACE="Times New Roman" SIZE="3">GetClipBox</font><font SIZE="3">的参数</font><font FACE="Times New Roman" SIZE="3">rectClip</font><font SIZE="3">中。然后将</font><font FACE="Times New Roman" SIZE="3">rectClip</font><font SIZE="3">的坐标由逻辑坐标转换为设备坐标。为了防止该矩形太小而无法包围其他内容,上下各放大一个单位。然后</font><font FACE="Times New Roman" SIZE="3">OnDraw</font><font SIZE="3">遍历笔划链表中的所有笔划,获取它们的最小矩形,用</font><font FACE="Times New Roman" SIZE="3">IntersectRect</font><font SIZE="3">看它是否与无效矩形相交。如果相交,说明笔划的部分或全部落在无效矩形中,此时调用笔划的</font><font FACE="Times New Roman" SIZE="3">DrawStroke</font><font SIZE="3">方法画出该笔划。</font><font FACE="Times New Roman" SIZE="3"></p>
    <p></font><b><font SIZE="3">图</font><font FACE="Times New Roman" SIZE="3">8-6</b> </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"><b></p>
    <p></b></font><font SIZE="3">为了获得笔划的最小包围矩形,需要在结束笔划时计算出包围笔划的最小矩形。因此为笔划提供两个方法:一个是</font><font FACE="Times New Roman" SIZE="3">FinishStroke()</font><font SIZE="3">,用于在笔划结束时计算最小矩形,见清单</font><font FACE="Times New Roman" SIZE="3">8.7</font><font SIZE="3">。<b></p>
    <p>清单</font><font FACE="Times New Roman" SIZE="3">8.7 CStroke::FinishStroke()</font><font SIZE="3">成员函数</font></b><font FACE="Times New Roman" SIZE="3"></p>
    <p>void CStroke::FinishStroke()</p>
    <p>{</p>
    <p>// Calculate the bounding rectangle. It's needed for smart</p>
    <p>// repainting.</p>
    <p>if (m_pointArray.GetSize()==0)</p>
    <p>{</p>
    <p>m_rectBounding.SetRectEmpty();</p>
    <p>return;</p>
    <p>}</p>
    <p>CPoint pt = m_pointArray[0];</p>
    <p>m_rectBounding = CRect(pt.x, pt.y, pt.x, pt.y);</p>
    <p>for (int i=1; i &lt; m_pointArray.GetSize(); i++)</p>
    <p>{</p>
    <p>// If the point lies outside of the accumulated bounding</p>
    <p>// rectangle, then inflate the bounding rect to include it.</p>
    <p>pt = m_pointArray[i];</p>
    <p>m_rectBounding.left = min(m_rectBounding.left, pt.x);</p>
    <p>m_rectBounding.right = max(m_rectBounding.right, pt.x);</p>
    <p>m_rectBounding.top = max(m_rectBounding.top, pt.y);</p>
    <p>m_rectBounding.bottom = min(m_rectBounding.bottom, pt.y);</p>
    <p>}</p>
    <p>// Add the pen width to the bounding rectangle. This is necessary</p>
    <p>// to account for the width of the stroke when invalidating</p>
    <p>// the screen.</p>
    <p>m_rectBounding.InflateRect(CSize(m_nPenWidth, -(int)m_nPenWidth));</p>
    <p>return;</p>
    <p>}</p>
    <p></font><font SIZE="3">另一个是</font><font FACE="Times New Roman" SIZE="3">DrawStroke()</font><font SIZE="3">,用于绘制笔划:</font><font FACE="Times New Roman" SIZE="3"></p>
    <p>BOOL CStroke::DrawStroke(CDC* pDC)</p>

⌨️ 快捷键说明

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