孫鑫VC++視頻學習筆記之3: MFC消息映射機制和繪圖DC

轉自:http://webbery.tianyablog.com

閱讀本文前,我們假設您已經:
   1,知道如何創建一個單文檔的App Wizard
   2,知道C++ 類、函數重載等簡單知識
   3,知道如何給View類或者Doc文檔添加成員變量
   4,會用MFC的IDE調試工具最好,那麼本文的程序您可以copy去調試
  
   我們在上一節講了MFC框架App類、View類、MainFrame類和Doc類的關係,那麼,基於消息的windows MFC程序設計是如何進行消息映射的呢?
  
   在進行下一節之前,我們來複習一下。
  
   對於一個MFC APPWizard運用程序,CMainFrame和View是窗口類,並且是父子關係,Doc一般用於數據的加載和存儲,View用於圖像的顯示,App中是包括一些(窗口)初始化之類的東西。
  
   好,我們開始這一節的內容。
  
  一、 消息映射機制
  windows程序設計是種事件驅動方式的程序設計,主要基於消息的。當用戶需要完成某種功能時,需要調用OS某種支持,然後OS將用戶的需要包裝成消息,並投入到消息隊列中,最後應用程序從消息隊列中取走消息並進行響應。
  
  在左邊View類處點右鍵,在出現的菜單裏點擊“Add Windows Message Handler”,在出現的對話框裏選擇“WM_LBUTTONDOWN”,添加消息映射函數。
  
  回到原文件,我們將看到三處進行了修改:
  
  1, 在頭文件(View.h)中聲明消息響應函數原型。
  //{{AFX_MSG(CDrawView) //註釋宏
  afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  //}}AFX_MSG //註釋宏
  afx_msg宏表示聲明的是一個消息響應函數。
  
  2, 在源文件(View.cpp)中進行消息映射。
  
  BEGIN_MESSAGE_MAP(CDrawView, CView)
   //{{AFX_MSG_MAP(CDrawView)
   ON_WM_LBUTTONDOWN()
   //}}AFX_MSG_MAP
  
  在宏BEGIN_MESSAGE_MAP()與END_MESSAGE_MAP()之間進行消息映射。
  宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN與它的響應函數OnLButtonDown()相關聯。這樣一旦有消息的產生,就會自動調用相關聯的消息響應函數去處理。
  
  宏ON_WM_LBUTTONDOWN()定義如下:
   #define ON_WM_LBUTTONDOWN()
  { WM_LBUTTONDOWN, 0, 0, 0,
  AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(void
  (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
  
  3, 源文件中進行消息響應函數處理。
  void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
  {
   // TODO: Add your message handler code here and/or call default
   CView::OnLButtonDown(nFlags, point);
  }
  說明:
  
  可見當增加一個消息響應處理,在以上三處進行了修改。可在消息響應函數裏添加消息處理代碼完成對消息的響應、處理。
  
  
  
  消息響應的可能方式:
  
   1)在基類中針對每種消息做一個虛函數,當子類對消息響應時候,只要在子類中重寫這個虛函數即可。缺點:MFC類派生層次很多,如果在基類對每個消息進 行虛函數處理,那麼從基類派生的每個子類都將揹負一個龐大的虛表,這樣浪費內存,故MFC沒有采取這中方式而採取消息映射方式。
  
   2)消息映射方式:MFC在後臺維護了一個句柄和C++對象指針對照表,當收到一個消息後,通過消息結構裏資源句柄(查對照表)就可找到與它對應的一個 C++對象指針,然後把這個指針傳給基類,基類利用這個指針調用WindowProc()函數對消息進行處理,WindowProc()函數中調用 OnWndMsg()函數,真正的消息路由及處理是由OnWndMsg()函數完成的。由於WindowProc()和OnWndMsg()都是虛函數, 而且是用派生類對象指針調用的,由多態性知最總終調用子類的。在OnWndMsg()函數處理的時候,根據消息種類去查找消息映射,判斷所發的消息有沒有 響應函數,具體方式是到相關的頭文件和源文件中尋找消息響應函數聲明(從註釋宏//{{AFX_MSG(CDrawView)... //}}AFX_MSG之間尋找),消息映射(從宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之間尋 找),最終找到對應的消息處理函數。當然,如果子類中沒有對消息進行處理,則消息交由基類處理。
  
  說明:
  
  virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
  
  virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
  
  二、 繪圖DC的獲取
  說明:在View類添加WM_LBUTTONDOWN和WM_LBUTTONUP的消息處理函數,我們將用來演示各種DC的獲取,以及顯示效果。
  View類添加全局變量CPoint m_ptOrigin用來存儲左鍵按下點座標。下面集中來關注OnLButtonUp中的繪圖程序和效果。
  以下語句添加於OnLButtonUp函數中,可以查看不同的效果。
  
  1, 使用SDK獲取DC句柄:
  HDC hdc;
  hdc=::GetDc(m_hWnd);//獲取DC句柄
  MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
  LineTo(hdc,point.x,point.y);
  ::ReleaseDC(m_hWnd,hdc);//釋放DC
  
  2, 利用CDC類指針和CWin類成員函數獲取DC。
  CDC *pDC=GetDC();
  pDC->MoveTo(m_ptOrigin);
  pDC->LineTo(point);
  ReleaseDC(pDC);
  
  3, 利用CClientDC對象。(CClientDC類從CDC類派生來的)
  CClientDC dc(this);
  dc.MoveTo(m_ptOrigin);
  dc.LineTo(point);
  
  4, 利用CWindowDC對象。(CWindowDC類從CDC類派生來的)
  CWindowDC dc(this);//
  dc.MoveTo(m_ptOrigin);
  dc.LineTo(point);
  
  5, 父窗口(MainFrame框架)和屏幕指針。
  
  將上面的dc(this)分別改成GetParent()和GetDesktopWindow(),就可以得到父窗口指針和屏幕窗口指針。
  
  可以分別試驗畫線效果。
  
  6, 利用畫筆改變線條顏色和類型:
  CPen pen(PS_DOT,1,RGB(0,255,0));//構造畫筆對象
  CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//將畫筆選入DC
  dc.MoveTo(m_ptOrigin);
  dc.LineTo(point);
  dc.SelectObject(pOldPen);//恢復先前的畫筆
  
  7, 使用畫刷(通常利用畫刷去填充矩形區域):
  使用單色畫刷
  CBrush brush(RGB(255,0,0));//構造畫刷對象
  CClientDC dc(this);
  dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的畫刷去填充矩形區域
  
  使用位圖畫刷
  CBitmap bitmap;//構造位圖對象(使用前需要初試化)
  bitmap.LoadBitmap(IDB_BITMAP1);//初試化位圖對象
  CBrush brush(&bitmap);//構造位圖畫刷
  CClientDC dc(this);
  dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位圖畫刷去填充矩形區域
  
  使用透明畫刷
  CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//獲取透明畫刷對象指針
  CClientDC dc(this);
  CBrush *pOldBrush=dc.SelectObject(pBrush);//將透明畫刷選入DC
  dc.Rectangle(CRect(m_ptOrigin,point));
  dc.SelectObject(pOldBrush);//釋放透明畫刷
  
  8, 注意點:
  1)靜態方法不屬於某一個具體對象,而屬於類本身,在類加載的時候就已經爲類靜態方法分配了代碼去,故可用CBrush::FromHandle()形式調用。
  2)靜態方法中,不能引用非靜態的數據成員和方法。
  3)靜態數據成員需要在類外單獨做初始化,形式如: 變量類型 類名::變量名=初始值;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章