久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

MFC 自繪

 3D建模仿真 2013-05-21

WM_DRAWITEM與DrawItem()的討論

     我在學習中經(jīng)常遇到要重寫DrawItem()的情況,,但又有一個WM_DRAWITEM消息,它們是什么樣的關(guān)系呢,。

如果我們要重寫一個CButton取名為CMyButton,,我們可以重寫CMyButton的DrawItem()函數(shù)來實現(xiàn)我們的

需求,但CMyButton::DrawItem()是在什么時候調(diào)用呢,?它是在它的宿主類的OnDrawItem()中被調(diào)用,,

OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct )正是對WM_DRAWiTEM的相應函數(shù),。

宿主類可以根據(jù)nIDCtl來判定是哪個子控件。其實我們可以在OnDrawItem函數(shù)里對子控件進行繪制,,但是有很多

的子控件看起來不好,,所以我們應該在子類的DrawItem對子類繪制,例如CMyButton::DrawItem,。所以可以

這樣理解,,OnDrawItem是畫窗口中的子控件的,因為它的入口參數(shù)LPDRAWITEMSTRUCT帶入不同子控件的相

關(guān)參數(shù),,而且,,你得把字控件設(shè)置成“自畫”類型,才會調(diào)用到OnDrawItem,。

    當自繪按鈕(owner-draw button),,下拉列表框(combo box),列表框(list box)視覺屬性,,或者菜單發(fā)生變化時,,

框架為他們的owner調(diào)用OnDrawItem(發(fā)送WM_DRAWITEM),在宿主類調(diào)用子類的DrawItem(發(fā)送WM_DRAWITEM消息),。

我們可以重載子類的DrawItem可以繪制自己需要的控件,,不是所有設(shè)置成自畫類型的控件都會調(diào)用父窗口的OnDrawItem,

例如ListBox的自畫,,你就必須重載CListBox的DrawItem方法和MeasureItem方法才可以,,但象菜單,按鈕等的自畫則會調(diào)用

OnDrawItem,。在SDK中,,子類是不可能受到WM_DRAWITEM,在MFC中可以,,這是類的設(shè)計者設(shè)計的(反射),,這的確不錯。

    在學習中還有一個消息也是由宿主類被調(diào)用的,,它就是WM_CTRCOLOR,。這個消息是在子控件將要繪畫時,向宿主

類發(fā)送,,宿主類利用發(fā)射機制讓子類自己又一個處理的機會,。OnCtlColor (CDC* pDC, CWnd* pWnd,  UINT nCtlColor)

pDC,pWnd都是于子類相關(guān)的,在這里可以設(shè)置,,前景顏色,,背景顏色,畫刷類型,字體等等,,但不能改變元素的界面框架,,

這是DrawItem 所能干的。

   如果同時有DrawItem(子類),,OnDrawItem(宿主類),,OnCtlColor(宿主類),它們的調(diào)用順序是:

OnCtlColor,,OnDrawItem,,DrawItem。

    如果我們同時又相應的子類的WM_PAINT消息,,這也許OnPaint在內(nèi)部進行了一些處理,,判斷是否自繪來決定是否向宿主類

發(fā)送WM_DRAWITEM,所以如果響應了WM_PAINT子類就不會向宿主類發(fā)送WM_DRAWITEM消息,,你要完成子類的全部繪

制工作,,如果子類是一個列表框,就很麻煩,。這時調(diào)用順序是OnCtlColor,OnPaint。

  在發(fā)送一個WM_PAINT消息前,,總會先發(fā)送一個WM_ERASEBACK消息,,我們在這里在一個背景圖片。

 

   對于我們平時對控件的繪制,,上面介紹的差不多了,,還有一個CView的問題,也就是OnPaint和Ondraw的關(guān)系,,

其實這個很簡單,,CView::OnPaint()的源碼如下:

  

  1. void CView::OnPaint()  
  2. {  
  3.      CPaintDC dc(this);        
  4.      OnPrepareDC(&dc);        
  5.      OnDraw(&dc)  
  6. }  

從代碼中可以清楚的看出他們的關(guān)系。


MFC中OnDraw與OnPaint的區(qū)別

OnPaint是WM_PAINT消息的消息處理函數(shù),,在OnPaint中調(diào)用OnDraw,,一般來說,用戶自己的繪圖代碼應放在OnDraw中,。

OnPaint()是CWnd的類成員,,負責響應WM_PAINT消息。OnDraw()是CVIEW的成員函數(shù),,沒有響應消息的功能.當視圖變得無效時(包括大小的改變,,移動,被遮蓋等等),,Windows發(fā)送WM_PAINT消息,。該視圖的OnPaint 處理函數(shù)通過創(chuàng)建CPaintDC類的DC對象來響應該消息并調(diào)用視圖的OnDraw成員函數(shù).OnPaint最后也要調(diào)用OnDraw,因此一般在OnDraw函數(shù)中進行繪制。


The WM_PAINT message is sent when the UpdateWindow or RedrawWindow member function is called.

在OnPaint中,將調(diào)用BeginPaint,,用來獲得客戶區(qū)的顯示設(shè)備環(huán)境,,并以此調(diào)用GDI函數(shù)執(zhí)行繪圖操作。在繪圖操作完成后,,將調(diào)用EndPaint以釋放顯示設(shè)備環(huán)境,。而OnDraw在BeginPaint與EndPaint間被調(diào)用。

1) 在mfc結(jié)構(gòu)里OnPaint是CWnd的成員函數(shù). OnDraw是CView的成員函數(shù).
2) OnPaint()調(diào)用OnDraw(),,OnPrint也會調(diào)用OnDraw(),,所以O(shè)nDraw()是顯示和打印的共同操作。

OnPaint是WM_PAINT消息引發(fā)的重繪消息處理函數(shù),,在OnPaint中會調(diào)用OnDraw來進行繪圖,。OnPaint中首先構(gòu)造一個CPaintDC類得實例,然后一這個實例為參數(shù)來調(diào)用虛函數(shù)OnPrepareDC來進行一些繪制前的一些處理,,比設(shè)置映射模式,,最后調(diào)用OnDraw。而OnDraw和OnPrepareDC不是消息處理函數(shù),。所以在不是因為重繪消息所引發(fā)的OnPaint導致OnDraw被調(diào)用時,,比如在OnLButtonDown等消息處理函數(shù)中繪圖時,要先自己調(diào)用OnPrepareDC,。
至于CPaintDC和CClientDC根本是兩回事情 CPaintDC是一個設(shè)備環(huán)境類,,在OnPaint中作為參數(shù)傳遞給OnPrepareDC來作設(shè)備環(huán)境的設(shè)置。真正和CClientDC具有可比性的是CWindowDC,,他們一個是描述客戶區(qū)域,,一個是描述整個屏幕。
如果是對CVIEW或從CVIEW類派生的窗口繪圖時應該用OnDraw,。

OnDraw()和OnPaint()有什么區(qū)別呢,?
首先:我們先要明確CView類派生自CWnd類。而OnPaint()是CWnd的類成員,,同時負責響應WM_PAINT消息,。OnDraw()是CVIEW的成員函數(shù),并且沒有響應消息的功能,。這就是為什么你用VC成的程序代碼時,,在視圖類只有OnDraw沒有OnPaint的原因。而在基于對話框的程序中,,只有OnPaint,。
其次:我們在第《每天跟我學MFC》3的開始部分已經(jīng)說到了。要想在屏幕上繪圖或顯示圖形,,首先需要建立設(shè)備環(huán)境DC,。其實DC是一個數(shù)據(jù)結(jié)構(gòu),,它包含輸出設(shè)備(不單指你17寸的純屏顯示器,還包括打印機之類的輸出設(shè)備)的繪圖屬性的描述,。MFC提供了CPaintDC類和CWindwoDC類來實時的響應,,而CPaintDC支持重畫。當視圖變得無效時(包括大小的改變,,移動,,被遮蓋等等),Windows 將 WM_PAINT 消息發(fā)送給它,。該視圖的OnPaint 處理函數(shù)通過創(chuàng)建 CPaintDC 類的DC對象來響應該消息并調(diào)用視圖的 OnDraw 成員函數(shù),。通常我們不必編寫重寫的 OnPaint 處理成員函數(shù)。
///CView默認的標準的重畫函數(shù)
void CView::OnPaint() //見VIEWCORE.CPP
{

CPaintDC dc(this);
OnPrepareDC(&dc),;
OnDraw(&dc); //調(diào)用了OnDraw
}
///CView默認的標準的OnPrint函數(shù)
void CView::OnPrint(CDC* pDC, CPrintInfo*)
{
ASSERT_VALID(pDC);
OnDraw(pDC); // Call Draw
}

既然OnPaint最后也要調(diào)用OnDraw,因此我們一般會在OnDraw函數(shù)中進行繪制,。下面是一個典型的程序。
///視圖中的繪圖代碼首先檢索指向文檔的指針,,然后通過DC進行繪圖調(diào)用,。
void CMyView::OnDraw( CDC* pDC )
{

CMyDoc* pDoc = GetDocument();
CString s = pDoc->GetData();
GetClientRect( &rect ); // Returns a CString CRect rect;
pDC->SetTextAlign( TA_BASELINE | TA_CENTER );
pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );
}
最后:現(xiàn)在大家明白這哥倆之間的關(guān)系了吧。因此我們一般用OnPaint維護窗口的客戶區(qū)(例如我們的窗口客戶區(qū)加一個背景圖片),,用OnDraw維護視圖的客戶區(qū)(例如我們通過鼠標在視圖中畫圖),。當然你也可以不按照上面規(guī)律來,只要達到目的并且沒有問題,,怎么干都成,。補充:我們還可以利用Invalidate(),ValidateRgn(),ValidateRect()函數(shù)強制的重畫窗口,具體的請參考MSDN吧,。

OnDraw中可以繪制用戶區(qū)域。OnPaint中只是當窗口無效時重繪不會保留CClientDC繪制的內(nèi)容,。

這兩個函數(shù)有區(qū)別也有聯(lián)系:

1,、區(qū)別:OnDraw是一個純虛函數(shù),定義為virtual void OnDraw( CDC* pDC ) = 0; 而OnPaint是一個消息響應函數(shù),,它響應了WM_PANIT消息,,也是是窗口重繪消息。

2,、聯(lián)系:我們一般在視類中作圖的時候,,往往不直接響應WM_PANIT消息,而是重載OnDraw純虛函數(shù),,這是因為在CVIEW類中的WM_PANIT消息響應函數(shù)中調(diào)用了OnDraw函數(shù),,如果在CMYVIEW類中響應了WM_PAINT消息,不顯式地調(diào)用OnDraw函數(shù)的話,,是不會在窗口重繪的時候調(diào)用OnDraw函數(shù)的,。

應用程序中幾乎所有的繪圖都在視圖的 OnDraw 成員函數(shù)中發(fā)生,必須在視圖類中重寫該成員函數(shù)。(鼠標繪圖是個特例,,這在通過視圖解釋用戶輸入中討論,。)


OnDraw 重寫:
通過調(diào)用您提供的文檔成員函數(shù)獲取數(shù)據(jù)。
通過調(diào)用框架傳遞給 OnDraw 的設(shè)備上下文對象的成員函數(shù)來顯示數(shù)據(jù),。
當文檔的數(shù)據(jù)以某種方式更改后,,必須重繪視圖以反映該更改。默認的 OnUpdate 實現(xiàn)使視圖的整個工作區(qū)無效,。當視圖變得無效時,,Windows 將 WM_PAINT 消息發(fā)送給它。該視圖的 OnPaint 處理函數(shù)通過創(chuàng)建 CPaintDC 類的設(shè)備上下文對象來響應該消息并調(diào)用視圖的 OnDraw 成員函數(shù),。

當沒有添加WM_PAINT消息處理時,窗口重繪時,由OnDraw來進行消息響應...當添加WM_PAINT消息處理時,窗口重繪時,WM_PAINT消息被投遞,由OnPaint來進行消息響應.這時就不能隱式調(diào)用OnDraw了.必須顯式調(diào)用( CDC *pDC=GetDC(); OnDraw(pDC); )..
隱式調(diào)用:當由OnPaint來進行消息響應時,系統(tǒng)自動調(diào)用CView::OnDraw(&pDC).


想象一下,,窗口顯示的內(nèi)容和打印的內(nèi)容是差不多的,所以,,一般情況下,,統(tǒng)一由OnDraw來畫。窗口前景需要刷新時,,系統(tǒng)會會調(diào)用到OnPaint,,而OnPaint一般情況下是對DC作一些初始化操作后,調(diào)用OnDraw(),。


OnEraseBkGnd(),,是窗口背景需要刷新時由系統(tǒng)調(diào)用的。明顯的一個例子是設(shè)置窗口的背景顏色(你可以把這放在OnPaint中去做,,但是會使產(chǎn)生閃爍的現(xiàn)象),。  
至于怎么界定背景和前景,那要具體問題具體分析了,,一般情況下,,你還是很容易區(qū)別的吧。

的確,,OnPaint()用來響應WM_PAINT消息,,視類的OnPaint()內(nèi)部根據(jù)是打印還是屏幕繪制分別以不同的參數(shù)調(diào)用OnDraw()虛函數(shù)。所以在OnDraw()里你可以區(qū)別對待打印和屏幕繪制,。
其實,,MFC在進行打印前后還做了很多工作,調(diào)用了很多虛函數(shù),,比如OnPreparePrint()等,。


消息反射機制

深度剖析消息反射機制
作者:hustli
    摘要:在前面我們分析了控件通知消息WM_NOTIFY,和WM_NOTIFY緊密聯(lián)系的還有一個MFC新特性:消息反射,。本文中,,我想就這個問題作一個全面的論述,,如果有錯誤,還望各路大蝦批評指正,。
    什么是消息反射,?
    在windows里面,子控件經(jīng)常向父控件發(fā)送消息,,例如很多子控件要繪制自己的背景,,就可能向父窗口發(fā)送消息WM_CTLCOLOR。對于從子控件發(fā)來的消息,,父控件有可能在處理之前,,把消息返還給子控件處理,這樣消息看起來就想是從父窗口反射回來一樣,,故此得名:消息反射,。
    消息反射的由來
    在windows和MFC4.0版本一下,父窗口(通常是一個對話框)會對這些消息進行處理,,換句話說,,自控件的這些消息處理必須在父窗口類體內(nèi),每當我們添加子控件的時候,,就要在父窗口類中復制這些代碼,,我們可以想象這是多么的復雜,代碼是多么的臃腫,!
    我們可以想象,,如果這些消息都讓父窗口類去做,父窗口就成了一個萬能的神,,一個臃腫不堪的代碼機,,無論如何消息的處理都集中在父窗口類中,會使父窗口繁重無比,,但是子控件卻無事可做,,并且代碼也無法重用,這對于一個程序員來講是多么痛苦的一件事,?!
    在老版本的MFC中,,設(shè)計者也意識到了這個問題,,他們對一些消息采用了虛擬機制,例如:WM_DRAWITEM,,這樣子控件就有機會控制自己的動作,,代碼的可重用性有了一定的提高,但是這還沒有達到大部分人的要求,,所以在高版本的MFC中,,提出了一種更方便的機制:消息反射,。
    通過消息反射機制,子控件窗口便能夠自行處理與自身相關(guān)的一些消息,,增強了封裝性,,同時也提高了子控件窗口類的可重用性。不過需要注意的是:消息反射是MFC實現(xiàn)的,,不是windows實現(xiàn)的,;要讓你的消息反射機制工作,你得類必須從CWnd類派生,。
    Message-Map中的處理
    如果想要處理消息反射,,必須了解相應的Message-Map宏和函數(shù)原型。一般來講,,Message-Map是有一定的規(guī)律的,,通常她在消息的前面加上一個ON_ ,然后再消息的最后加上 _REFLECT,。例如我們前面提到的WM_CTLCOLOR 經(jīng)過處理后變成了ON_WM_CTLCOLOR_REFLECT,;WM_MEASUREITEM則變成了ON_WM_MEASUREITEM_REFLECT。
    凡事總會有例外,,這里也是這樣,,這里面有3個例外:
    (1) WM_COMMAND 轉(zhuǎn)換成 ON_CONTROL_REFLECT;
    (2) WM_NOTIFY  轉(zhuǎn)換成 ON_NOTIFY_REFLECT,;
    (3) ON_UPDATE_COMMAND_UI 轉(zhuǎn)換成 ON_UPDATE_COMMAND_UI_REFLECT,;
    對于函數(shù)原型,也必須是以 afx_msg 開頭,。
    利用ClassWizard添加消息反射
    (1)在ClassWizard中,,打開選擇項Message Maps;
    (2)在下拉列表Class name中選擇你要控制的類,;
    (3)在Object IDs中,,選中相應的類名;
    (4)在Messages一欄中找到前面帶有=標記的消息,,那就是反射消息,;
    (5)雙擊鼠標或者單擊添加按鈕,然后OK!
    消息處理的過程
  (1)子窗口向父窗口發(fā)送通知消息,,激發(fā)父窗口去調(diào)用它的虛函數(shù)CWnd::OnNotify,。大致的結(jié)構(gòu)如下
       BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
       {
          if (ReflectLastMsg(hWndCtrl, pResult)) 
file://hWndCtrl
,為發(fā)送窗口
                                    return TRUE; file://如果子窗口已處理了此消息,返回
          AFX_NOTIFY notify;
          notify.pResult = pResult;
          notify.pNMHDR = pNMHDR;
          return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY)? notify:NULL);
      }
  (2)ReflectLastMsg聲明如下:static BOOL PASCAL ReflectLastMsg(HWND hWndChild, LRESULT* pResult = NULL);
     它的主要任務就是調(diào)用發(fā)送窗口的SendChildNotifyLastMsg,。 
  (3)SendChildNotifyLastMsg聲明如下:BOOL SendChildNotifyLastMsg(LRESULT* pResult = NULL);
     調(diào)用發(fā)送窗口的虛函數(shù)OnChildNotify函數(shù),,進行處理。 如果發(fā)送窗口沒有進行重載處理,,則調(diào)用ReflectChildNotify(...)函數(shù)進行標準的反射消息的消息映射處理,。
    使用的一個例子
    這里面我們舉一個簡單的例子,,希望大家能夠更清晰的掌握消息反射機制。
    (1)創(chuàng)建一個基于對話框的工程,。
    (2)利用向?qū)?chuàng)建一個新的類:CMyEdit,,基類是CEdit。
    (3)在CMyEdit頭文件中加入3個成員變量:
         COLORREF m_clrText ;
 COLORREF m_clrBkgnd ;
 CBrush   m_brBkgnd;
    (4)利用向?qū)г谄渲屑尤隬M_CTLCOLOR(看到了么,,前面是不是有一個=,?),并且將它的函數(shù)體改為:
         HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
        {
  pDC->SetTextColor( m_clrText );    // text
  pDC->SetBkColor( m_clrBkgnd );    // text bkgnd
  return m_brBkgnd;                // ctl bkgnd
        }
        同時我們在.cpp文件中會看到ON_WM_CTLCOLOR_REFLECT(),,這就是我們所說的經(jīng)過處理的宏,,是不是很符合規(guī)則?
    (5)在對話框中加入一個Edit,,增加一個關(guān)聯(lián)的變量,,選擇Control屬性,類別為CMyEdit,。
    (6)在對話框.cpp文件中加入#include "MyEdit.h",,運行,看到了什么,?呵呵,。


PreTranslateMessage和TranslateMessage區(qū)別

 PreTranslateMessage是消息在送給TranslateMessage函數(shù)之前被調(diào)用的,絕大多數(shù)本窗口的消息都要通過這里,,比較常用,,當需要在MFC之前處理某些消息時,常常要在這里添加代碼.  
       

       MFC 消息控制流最具特色的地方是CWnd類的虛擬函數(shù)PreTranslateMessage(),,通過重載這個函數(shù),,可以改變MFC的消息控制流程,甚至可 以作一個全新的控制流出來,。只有穿過消息隊列的消息才受PreTranslateMessage()影響,,采用SendMessage()或其他類似的方 式向窗口直接發(fā)送的而不經(jīng)過消息隊列的消息根本不會理睬PreTranslateMessage()的存在。  

       是否調(diào)用TranslateMessage()和DispatchMessage()是由一個名稱為PreTranslateMessage()函數(shù)的返回值決定的,,如果該函數(shù)返回TRUE,,則不會把該消息分發(fā)給窗口函數(shù)處理。 

傳給PreTranslateMessage()的消息是未經(jīng)翻譯過的消息,,它沒有經(jīng)過TranslateMessage()處理,。可以在該函數(shù)中使用(pMsg->wParam==VK_RETURN)來攔截回車鍵,。wParam中存放的是鍵盤上字符的虛擬碼。 

PeekMessage和GetMessage的區(qū)別: 

GetMessage在沒有消息的時候等待消息,,cpu當然低 

PeekMessage沒有消息的時候立刻返回,,所以cpu占用率高,。 

因為游戲不能靠windows消息驅(qū)動,所以要用PeekMessage(); 

     PretranslateMessage 的實現(xiàn),,不得不談到MFC消息循環(huán)的實現(xiàn),。MFC通過CWinApp類中的Pumpmessage函數(shù)實現(xiàn)消息循環(huán),但是實際的消息循環(huán)代碼位于 CWinThread中,,CWinApp只是從CWinThread繼承過來,。其簡化后的代碼大概如下: 
  BOOL CWinThread::PumpMessage() 
  { 
  _AFX_THREAD_STATE *pState = AfxGetThreadState(); 
   
  ::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)) 
   
  if (!AfxPreTranslateMessage(&(pState->m_msgCur))) 
  { 
  ::TranslateMessage(&(pState->m_msgCur)); 
  ::DispatchMessage(&(pState->m_msgCur)); 
  } 
  return TRUE; 
  } 
   可以看到,PumpMessage在實際的TranslateMessage和DispatchMessage發(fā)生之前會調(diào)用 AfxPreTranslateMessage,,AfxPreTranslateMessage又會調(diào)用 CWnd::WalkPreTranslateTree(雖然也會調(diào)用其他函數(shù),,但是這個最為關(guān)鍵),其代碼如下: 
  BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg) 
  { 
  ASSERT(hWndStop == NULL || ::IsWindow(hWndStop)); 
  ASSERT(pMsg != NULL); 
   
  // walk from the target window up to the hWndStop window checking 
  // if any window wants to translate this message 
   
  for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) 
  { 
  CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); 
  if (pWnd != NULL) 
  { 
  // target window is a C window 
  if (pWnd->PreTranslateMessage(pMsg)) 
  return TRUE; // trapped by target window (eg: accelerators) 
  } 
   
  // got to hWndStop window without interest 
  if (hWnd == hWndStop) 
  break; 
  } 
  return FALSE; // no special processing 
  } 
   
  可以看到,,代碼還是很直接的,。從接受到消息的窗口層層往上遍歷,并調(diào)用PretranslateMessage看是否返回TRUE,,是則結(jié)束,,否則繼續(xù)。 
  這里有一個地方非常關(guān)鍵:CWnd *pWnd = CWnd::FromHandlePermanent(hWnd) 這一句代碼從當前AfxModuleThreadState拿到Permanent句柄表,,從而找到hWnd對應的CWnd 


MFC 中PreTranslateMessage是GetMessage(...)函數(shù)的下一級操作,,即GetMessage(...)從消息隊列中獲取消息 后,交由PreTranslateMessage()處理,,若其返回FALSE則再交給TranslateMessage和 DispatchMessage處理(進入WindowProc),;   
如果用SendMessage,   則消息直接交到WindowProc處理,所以GetMessage不會取得SendMessage的消息,,當然PreTranslateMessage也就不會被調(diào)用,。   [Page]
如果用PostMessage,則消息進入消息隊列,由GetMessage取得,,PreTranslateMessage就有機會進行處理,。


WindowProc和DefWindowProc的區(qū)別

1.
WindowProc是你給自己的窗口定義的窗口處理函數(shù)
DefWindowProc是windows平臺提供的默認窗口處理函數(shù)
如果某些消息你不需要做特別的處理,調(diào)用DefWindowProc進行處理就可以了,,不需要你自己再去些那些windows的"標準動作"

2.
根據(jù)1,,顯然,你只能定義WindowProc而不能定義DefWindowProc

看看MFC的CWnd源碼就一目了然了
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult)) // 如果消息是需要自己處理的,,
  //處理后就不必讓windows系統(tǒng)進行默認處理了
lResult = DefWindowProc(message, wParam, lParam); // 自己沒有處理的就交給windows去做

return lResult;
}


CWnd中PreCreateWindow,、PreSubclassWindow、SubclassWindow的區(qū)別

http://blog.csdn.net/swimmer2000/archive/2007/10/30/1856213.aspx

MFC(VC6.0)的CWnd及其子類中,,有如下三個函數(shù):

    // From VS Install PathVC98MFCIncludeAFXWIN.H
    class CWnd : public CCmdTarget
    {
        ...
    public:
        ...
        virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
        virtual void PreSubclassWindow();
        BOOL SubclassWindow(HWND hWnd);
        ...
    }
;
    

    
讓人很不容易區(qū)分,,不知道它們究竟干了些什么,在什么情況下要改寫哪個函數(shù),?

想知道改寫函數(shù),?讓我先告訴你哪個不能改寫,,那就是SubclassWindow。Scott Meyers的杰作<<Effective C++>>的第36條是這樣的:Differentiate between inheritance of interface and inheritance of implementation. 看了后你馬上就知道,,父類中的非虛擬函數(shù)是設(shè)計成不被子類改寫的,。根據(jù)有無virtual關(guān)鍵字,我們在排除了SubclassWindow后,,也就知道PreCreateWindow和PreSubClassWindow是被設(shè)計成可改寫的,。接著的問題便是該在什么時候該寫了。要知道什么時候該寫,,必須知道函數(shù)是在什么時候被調(diào)用,,還有執(zhí)行函數(shù)的想要達到的目的。我們先看看對這三個函數(shù),,MSDN給的解釋:
    
    PreCreateWindow:
    
        Called by the framework before the creation of the Windows window 
        attached to this CWnd object.
        (譯:在窗口被創(chuàng)建并attach到this指針所指的CWnd對象之前,,被framework調(diào)用)
        
    PreSubclassWindow:
    
        This member function is called by the framework to allow other necessary 
        subclassing to occur before the window is subclassed.
        (譯:在window被subclassed之前被framework調(diào)用,用來允許其它必要的
            subclassing發(fā)生)
            
雖然我已有譯文,,但還是讓我對CWnd的attach和窗口的subclass作簡單的解釋吧,!要理解attach,我們必須要知道一個C++的CWnd對象和窗口(window)的區(qū)別:window就是實在的窗口,,而CWnd就是MFC用類對window所進行C++封裝,。attach,就是把窗口附加到CWnd對象上操作,。附加(attach)完成后,,CWnd對象才和窗口發(fā)生了聯(lián)系。窗口的subclass是指修改窗口過程的操作,,而不是面向?qū)ο笾械呐缮宇悺?/p>

好了,,PreCreateWindow由framework在窗口創(chuàng)建前被調(diào)用,函數(shù)名也說明了這一點,,Pre應該是previous的縮寫,,PreSubclassWindow由framework在subclass窗口前調(diào)用。 這段話說了等于沒說,,你可能還是不知道,,什么時候該改寫哪個函數(shù)。羅羅嗦嗦的作者,,還是用代碼說話吧,!源碼之前,了無秘密(候捷語),。我們就看看MFC中的這三個函數(shù)都是這樣實現(xiàn)的吧,! 

    // From VS Install PathVC98MFCSRCWINCORE.CPP
    BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
 LPCTSTR lpszWindowName, DWORD dwStyle,
 int x, int y, int nWidth, int nHeight,
 HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
    {
     // allow modification of several common create parameters
     CREATESTRUCT cs;
     cs.dwExStyle = dwExStyle;
     cs.lpszClass = lpszClassName;
     cs.lpszName = lpszWindowName;
     cs.style = dwStyle;
     cs.x = x;
     cs.y = y;
     cs.cx = nWidth;
     cs.cy = nHeight;
     cs.hwndParent = hWndParent;
     cs.hMenu = nIDorHMenu;
     cs.hInstance = AfxGetInstanceHandle();
     cs.lpCreateParams = lpParam;
    
     if (!PreCreateWindow(cs))
     {
      PostNcDestroy();
      return FALSE;
     }

    
     AfxHookWindowCreate(this);
     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
       cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
       cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
    
        ...
     return TRUE;
    }

    
    // for child windows
    BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
    {
     if (cs.lpszClass == NULL)
     {
      // make sure the default window class is registered
      VERIFY(AfxDeferRegisterClass(AFX_WND_REG));
    
      // no WNDCLASS provided - use child window default
      ASSERT(cs.style & WS_CHILD);
      cs.lpszClass = _afxWnd;
     }

     return TRUE;
    }


CWnd::CreateEx先設(shè)定cs(CREATESTRUCT),在調(diào)用真正的窗口創(chuàng)建函數(shù)::CreateWindowEx之前,調(diào)用了CWnd::PreCreateWindow函數(shù),,并把參數(shù)cs以引用的方式傳遞了進去,。而CWnd的PreCreateWindow函數(shù)也只是給cs.lpszClass賦值而已。畢竟,,窗口創(chuàng)建函數(shù)CWnd::CreateEx的諸多參數(shù)中,并沒有哪個指定了所要創(chuàng)建窗口的窗口類,,而這又是不可缺少的(請參考<<windows程序設(shè)計>>第三章),。所以當你需要修改窗口的大小、風格,、窗口所屬的窗口類等cs成員變量時,,要改寫PreCreateWindow函數(shù)。 

    // From VS Install PathVC98MFCSRCWINCORE.CPP
    BOOL CWnd::SubclassWindow(HWND hWnd)
    {
     if (!Attach(hWnd))
      return FALSE;
    
     // allow any other subclassing to occur
     PreSubclassWindow();
    
     // now hook into the AFX WndProc
     WNDPROC* lplpfn = GetSuperWndProcAddr();
     WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
      (DWORD)AfxGetAfxWndProc());
     ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());
    
     if (*lplpfn == NULL)
      *lplpfn = oldWndProc;   // the first control of that type created
    #ifdef _DEBUG
     else if (*lplpfn != oldWndProc)
     {
      ...
      ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
     }

    #endif
    
     return TRUE;
    }


    void CWnd::PreSubclassWindow()
    {
     // no default processing
    }


CWnd::SubclassWindow先調(diào)用函數(shù)Attach(hWnd)讓CWnd對象和hWnd所指的窗口發(fā)生關(guān)聯(lián),。接著在用::SetWindowLong修改窗口過程(subclass)前,,調(diào)用了PreSubclassWindow。CWnd::PreSubclassWindow則是什么都沒有做,。

在CWnd的實現(xiàn)中,,除了CWnd::SubclassWindow會調(diào)用PreSubclassWindow外,還有一處,。上面所列函數(shù)CreateEx的代碼,,其中調(diào)用了一個AfxHookWindowCreate函數(shù),見下面代碼: 

    // From VS Install PathVC98MFCSRCWINCORE.CPP
    BOOL CWnd::CreateEx(...)
    {
     // allow modification of several common create parameters
        ...
    
     if (!PreCreateWindow(cs))
     {
      PostNcDestroy();
      return FALSE;
     }

    
     AfxHookWindowCreate(this); 
     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
       cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
       cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
    
        ...
     return TRUE;
    }

    
接著察看AfxHookWindowCreate的代碼:

    // From VS Install PathVC98MFCSRCWINCORE.CPP
    void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
    {
     ...
    
     if (pThreadState->m_hHookOldCbtFilter == NULL)
     {
      pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
       _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
      if (pThreadState->m_hHookOldCbtFilter == NULL)
       AfxThrowMemoryException();
     }

     ...
    }

其主要作用的::SetWindowsHookEx函數(shù)用于設(shè)置一個掛鉤函數(shù)(Hook函數(shù))_AfxCbtFilterHook,,每當Windows產(chǎn)生一個窗口時(還有許多其它類似,,請參考<<深入淺出MFC>>第9章,563頁),,就會調(diào)用你設(shè)定的Hook函數(shù),。

這樣設(shè)定完成后,回到CWnd::CreateEx函數(shù)中,,執(zhí)行::CreateWindowEx進行窗口創(chuàng)建,,窗口一產(chǎn)生,就會調(diào)用上面設(shè)定的Hook函數(shù)_AfxCbtFilterHook,。而正是在_AfxCbtFilterHook中對函數(shù)PreSubclassWindow進行了第二次調(diào)用,。見如下代碼:

    // From VS Install PathVC98MFCSRCWINCORE.CPP
    /////////////////////////////////////////////////////////////////////////////
    // Window creation hooks
    
    LRESULT CALLBACK
    _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
    {
            ...       
                 ...
        // connect the HWND to pWndInit...
      pWndInit->Attach(hWnd);
        // allow other subclassing to occur first
        pWndInit->PreSubclassWindow();
                ...
        {
                // subclass the window with standard AfxWndProc
                oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)afxWndProc);
            ASSERT(oldWndProc != NULL);
                *pOldWndProc = oldWndProc;
        }

           ...
    }


也在調(diào)用函數(shù)SetWindowLong進行窗口subclass前調(diào)用了PreSubclassWindow.

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多