八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第128部分

深入浅出MFC第2版(PDF格式)-第128部分

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






     我们还需要一个函数,用以计算「线条之最小外包四方形」,这件事情当然是在线条完 



     成后进行之,所以此一函数命名为FinishStroke。每当一笔画结束(鼠标左键放开,产生 



     WM_LBUTTONUP ),OnLButtonUp 就调用FinishStroke 让它计算边界。计算方法很直 



     接,取出线条中的坐标点,比大小而已: 



     // in SCRIBDOC。CPP 

     void CStroke::FinishStroke() 

     { 



               计算外围四方形。为了灵巧而高效率的重绘动作,这是必要的。 

            //  

             if  (m_pointArray。GetSize()==0) 

             { 

                     m_rectBounding。SetRectEmpty(); 

                     return; 

             } 

             CPoint pt = m_pointArray'0'; 

             m_rectBounding = CRect(pt。x; pt。y; pt。x; pt。y); 



             for (int i=1; i 《 m_pointArray。GetSize(); i++) 

             { 



                    //  

                       如果点在四方形之外,那么就将四方形膨胀,以包含该点。 

                     pt = m_pointArray'i'; 

                     m_rectBounding。left     = min(m_rectBounding。left; pt。x); 

                     m_rectBounding。right    = max(m_rectBounding。right; pt。x); 

                     m_rectBounding。top      = min(m_rectBounding。top; pt。y); 

                     m_rectBounding。bottom   = max(m_rectBounding。bottom; pt。y); 

             } 



            // 在四方形之外再加上笔的宽度。 

             m_rectBounding。InflateRect(CSize(m_nPenWidth; m_nPenWidth)); 

             return; 

     } 



把hint 传给OnUpdate 



     下一步骤是想办法把hint 交给UpdateAllViews。让我们想想什么时候Scribble 的资料 



     开始产生改变?答案是鼠标左键按下时!或许你会以为要在OnLButtonDown 中调用 



     CDocument::UpdateAllViews。这个猜测的论点可以成立但是结果错误,因为左键按下后还 



                                                                                          635 


…………………………………………………………Page 698……………………………………………………………

                   第篇    深入  MFC  程式設計 



                   有一连串的鼠标轨迹移动,每次移动都导至资料改变,新的点不断被加上去。如果我们 



                   等左键放开,线条完成,再来调用UpdateAllViews,事情会比较单纯。因此Scribble Step4 



                   是在OnButtonUp 中调用UpdateAllViews。当然我们现在就可以预想得到,一笔画完成 



                   之前,同一Document  的其它Views 没有办法实时显示最新资料。 



                   下面是OnButtonUp 的修改内容: 



                   void CScribbleView::OnLButtonUp(UINT; CPoint point) 

                    { 

                           。。。 

                           m_pStrokeCur…》m_pointArray。Add(point); 



                           // 已完成加点的动作,现在可以计算外围四方形了 

                           m_pStrokeCur…》FinishStroke(); 



                           // 通知其它的views,使它们得以修改它们的图形。 

                           pDoc…》UpdateAllViews(this; 0L; m_pStrokeCur); 

                           。。。 

                           return; 

                    } 



                   程序逻辑至为简单,唯一需要说明的是UpdateAllViews 的第三个参数(hint ),原本我 



                   们只需设此参数为m_rectBounding ,即可满足需求,但MFC 规定,第三参数必须是一个 



                   CObject 指针,而CRect 并不衍生自CObject,所以我们干脆就把目前正作用中的整个 



                   线条(也就是m_pStrokeCur )传过去算了。CStroke 的确是衍生自CObject ! 



                   // in SCRIBBLEVIEW。H 

                   class CScribbleView : public CScrollView 

                    { 

                   protected: 

                           CStroke*    m_pStrokeCur;   // the stroke in progress 

                           。。。 

                    }; 



                   // in SCRIBBLEVIEW。CPP 

                   void CScribbleView::OnLButtonDown(UINT; CPoint point) 

                    { 

                           。。。 

                           m_pStrokeCur = GetDocument()…》NewStroke(); 

                           m_pStrokeCur…》m_pointArray。Add(point); 



636 


…………………………………………………………Page 699……………………………………………………………

                                               第 11 章    View 功能之加強與重繪效率之提昇 



              。。。 

      } 

      void CScribbleView::OnMouseMove(UINT; CPoint point) 

      { 

              。。。 

              m_pStrokeCur…》m_pointArray。Add(point); 

              。。。 

      } 

      void CScribbleView::OnLButtonUp(UINT; CPoint point) 

      { 

              。。。 

              m_pStrokeCur…》m_pointArray。Add(point); 

              m_pStrokeCur…》FinishStroke(); 

              pDoc…》UpdateAllViews(this; 0L; m_pStrokeCur); 

              。。。 

      } 



     UpdateAllViews 会巡访CScribbleDoc 所连接的每一个Views       (始作俑者那个View  除 



     外),调用它们的OnUpdate 函数,并把hint 做为参数之一传递过去。 



利用 hint 增加重绘效率 



     预设情况下,OnUpdate 所收到的无效区(也就是重绘区),是Document Frame 窗口的 



     整个内部。但谁都知道,原已存在而且没有变化的图形再重绘也只是浪费时间而已。上 



     一节你已看到Scribble 每加上一整个线条, 就在OnLButtonUp 函数中调用 



     UpdateAllViews 函数,并且把整个线条(内含其四方边界)传过去,因此我们可以想办法 



     在OnUpdate 中重绘这个四方形小区域就好。 



     话说回来,如何能够只重绘一个小区域就好呢?我们可以一一取出Document  中每一线 



     条的四方边界,与新线条的四方边界比较,若有交点就重绘该线条。CRect 有一个 



     IntersectRect  函数正适合用来计算四方形交集。 



     但是有一点必须注意,绘图动作不是集中在OnDraw 吗?因此OnUpdate 和OnDraw 之 



     间的分工有必要厘清。前面数个版本中这两个函数的动作是: 



                                                                                       637 


…………………………………………………………Page 700……………………………………………………………

                   第篇    深入  MFC  程式設計 



                     OnUpdate 啥也没做。事实上CScribbleView 原本根本没有改写此一函数。 



                     OnDraw 迭代取得Document  中的每一线条,并调用CStroke::DrawStroke 将线条 



                     绘出。 



                     Scribble Step4 之中,这两个函数的动作如下: 



                      OnUpdate 判断Framework 传来的hint 是否为CStroke 对象。如果是,设定 



                   无效区域(重绘区域)为该线条的外围四方形;如果不是,设定无效区域为整 



                     个窗口区域。「设定无效区域」( 也就是调用CWnd::InvalidateRect ) 会引发 



                     WM_PAINT ,于是引发OnDraw 。 



                      OnDraw 迭代取得Document  中的每一线条,并调用CStroke::GetBoundingRect 



                   取线条之外围四方形,如果与「无效区域」有交集,就调用CStroke::DrawStroke 



                     绘出整个线条。如果没有交集,就跳过不画。 



                     以下是新增的OnUpdate 函数: 



                   // in SCRIBVW。CPP 

                   void CScribbleView::OnUpdate(CView* /* pSender */; LPARAM /* lHint */; 

                           CObject* pHint) 

                   { 

                       // Document 通知View 说,某些资料已经改变了 



                       if (pHint != NULL) 

                       { 

                           if  (pHint…》IsKindOf(RUNTIME_CLASS(CStroke))) 

                      { 

                               // hint提示我们哪一线条被加入(或被修改),所以我们要把该线条的 

                               // 外围四方形设为无效区。 

                               CStroke* pStroke =  (CStroke*)pHint; 

                               CClientDC dc(this); 

                               OnPrepareDC(&dc); 

                               CRect rectInvalid = pStroke…》GetBoundingRect(); 

                               dc。LPtoDP(&rectInvalid); 

                               InvalidateRect(&rectInvalid); 

                               return; 

                           } 

                       } 

                       //如果我们不能解释hint 内容(也就是说它不是我们所预期的 



638 


…………………………………………………………Page 701……………………………………………………………

                                            第 11 章    View 功能之加強與重繪效率之提昇 



               对象),那就让整个窗口重绘吧(把整个窗口设为无效区)。 

   // CStroke  

     Invalidate(TRUE); 

     return; 

 } 



为什么OnUpdate 之中要调用OnPrepareDC?这关系到滚动条,我将在介绍分裂窗口时再 



说明。另,GetBoundingRect 动作如下: 



 CRect& GetBoundingRect() { return m_rectBounding; } 



 OnDraw 函数也为了高效能重绘动作之故,做了以下修改。阴影部份是与Scribble Step3 



 不同之处: 



 // SCRIBVW。CPP 

 void CScribbleView::OnDraw(CDC* pDC) 

 { 

     CScribbleDoc* pDoc = GetDocument(); 

     ASSERT_VALID(pDoc); 



     // 取得窗口的无效区。如果是在打印状态情况下,则取 

     // printer DC 的截割区(clipping region)。 

     CRect rectClip; 

     CRect rectStroke; 

     pDC…》GetClipBox(&rectClip); 



     // 

     // 注意:CScrollView::OnPrepare 已经在OnDraw 被调用之前先一步 

     // 调整了DC 原点,用以反应出目前的卷动位置。关于CScrollView, 

     // 下一节就会提到。 



     //调用CStroke::DrawStroke 完成无效区中各线条的绘图动作 

     CTypedPtrList& strokeList = pDoc…》m_strokeList; 

     POSITION pos = strokeList。GetHeadPosition(); 

     while (pos != NULL) 

     { 

         CStroke* pStroke = strokeList。GetNext(pos); 

         rectStroke = pStroke…》GetBoundingRect(); 

         if (!rectStroke。IntersectRect(&rectStroke; &rectClip)) 

             continue; 

         pStroke…》DrawStroke(pDC); 

     } 

 } 



                                                                                       639 


…………………………………………………………Page 702……………………………………………………………

               第篇    深入  MFC  程式設計 



          可卷动的窗口:CScrollView 



                到目前为止我们还没有办法观察一张比窗口还大的图,因为我们没有滚动条。 



                一个View 窗口没有滚动条,是很糟糕的事,因为通常Docume

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的