WTL按鈕自繪機制

這幾天想自己寫一個WTL的SkinButton,找了好長時間的資料才搞明白。

在搜索資料在過程中發現,大家都是知道怎麼實現,貼出了一大段代碼,但是很多人並不明白實現窗體自繪的原理。下面就如何實現窗體自繪我給出自己的解法:

1、第一步就是控件的子類化,這個是用來讓自己寫的類接受window消息的。這個就不具體講解了,

可以參考:

http://www.cnblogs.com/wdhust/archive/2010/09/18/1830097.html

http://lordtang.javaeye.com/blog/658489

http://fengshao1020.spaces.live.com/blog/cns!58764EF0C1622309!338.entry

2、在網上大家都說要繼承COwnerDraw或CCustomDraw,兩者的區別說的很明白,但是對於其原理就沒有說明白。

最初我想的很簡單,既然實現窗體自繪只需要我寫的按鈕類,處理WM_DRAWITEM消息就可以了,但是

查了一下MSDN才知道,當子窗體需要自繪的時候,子窗口會發送WM_DRAWITEM消息給父窗口,也就是說,這個消息是子窗口自動發送給父窗口讓父窗口來處理的。這樣我們寫的按鈕類就接收不到這個消息了,也就無法自繪了。

互聯網上的資料說通過在父窗口的消息映射最後添加  REFLECT_NOTIFICATIONS()
就可以了,查了一下這個宏定義的源碼才發現,他是將一些特定的消息原封不動的在傳遞給子窗口。也就是說當父窗口受到WM_DRAWITEM消息時,會將這個消息反向傳遞給發送方,即誰發給父窗口,父窗口在原封不動的發送給誰。

在查看COwnerDraw的源碼發現,其消息映射中有下面一個映射,

 MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)

顯然父窗口反向傳遞消息時候,消息的參數沒有變,只不過是消息標誌變了,原來是WM_現在是OCM_

下面是COwnerDraw類的一些代碼

 LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
 {
  T* pT = static_cast<T*>(this);
  pT->SetMsgHandled(TRUE);
  pT->DrawItem((LPDRAWITEMSTRUCT)lParam);
  bHandled = pT->IsMsgHandled();
  return (LRESULT)TRUE;
 }

。。。。。。。。。

// Overrideables
 void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
 {
  // must be implemented
  ATLASSERT(FALSE);
 }

研究才發現,如果繼承了這個類,就必須重載 DrawItem,否則就會出現編譯錯,講到這裏大家就應該明白窗體自繪的原理了吧。好了我們梳理一下思路,窗體自繪原理如下:

 

(1)想實現控件自繪,控件就必須有處理WM_DRAWITEM消息的能力,但是這個消息是子控件用來通知父窗體的,所以必須有某種機制讓父窗體將這個消息反向發送給子控件。這個可以用  REFLECT_NOTIFICATIONS()宏來實現,其實也就是調用SendMessage而已。

(2)父窗體反向傳遞回來的消息是以OCM開頭的,所以子控件應該處理OCM_DRAWITEM消息,這樣子控件的消息映射中應該添加MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)
當然映射的末尾最好添加DEFAULT_REFLECTION_HANDLER()

(3)在函數

 LRESULT
  OnDrawItem(
  UINT uMsg,
  WPARAM wParam,
  LPARAM lParam,
  BOOL& bHandled);

中實現控件的自繪。

(4)至此一個完整的控件自繪的功能就實現了,而且我們也並沒有繼承複雜的COwnerDraw這個類,當然在更復雜的控件自繪中也可以繼承這個類,這樣可以簡化編碼。

 

 

 

 

 

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