題外話
很多朋友看了我的前兩篇文章後,紛紛來信說能不能介紹一些元件入門的基礎知識,因為他們根本找不到相關資料,並詢問我是如何知道這些知識的。誠然,網上確實沒有這方面的介紹資料,更何況大家是學BCB的,對於Delphi的源代碼學習起來更是困難,對於作者來說也不比大家知道多少,我認為最好的方式就是看VCL源代碼和去Borland的新聞組提問,至少我是這樣解決問題的,希望你也可以。
這裡是Borland新聞組地址,如果你英文夠好,他們基本是有問必答的:
forums.borland.com
對於那些想學習基礎元件知識的朋友,我會在這系列文章的最後部分專門安排2篇文章作為禮物送給你們,一篇是我會實際分析一個專業級元件,來個源代碼解剖,把所有細節展示給大家,第二篇是我會實際編寫一個簡單使用的組件,並介紹全過程,希望大家喜歡。
更多消息處理
已經寫了2篇文章了,怎麼還是消息處理?是的,編寫元件就是處理消息和表露事件,對於一般的消息處理,前面2篇文章介紹的內容已經足夠用了,但是很多時候這還是不夠的,比如如果在設計時期你更改了元件的Font屬性,而你又想根據字體重新繪制。很明顯傳統的Windows消息處理其不到絲毫作用,這樣的消息通常是WM_XXXX的形式。如果你研究過VCL源代碼,你會發現很多CN_XXXX和CM_XXXX這樣的消息,如果你要完成我上面提到的消息處理,這些消息可以幫助完成任務。
其實,VCL存在一些非API消息以供其內部使用,為什麼要這樣做呢?這要從WM_COMMAND & WM_NOTIFY消息說起,我們說WM_COMMAND消息並不是直接發給實際產生消息的窗體,而是發送到它的父窗體。但是父窗體幾乎不可能用通常方法處理這些根本不知道如何處理的消息,於是父窗體把這個消息加上CN_BASE在分發到實際的子窗體中,然後由實際的子窗體處理。
比如TBitBtn元件為了在按鈕表面繪制圖象,處理了CN_DRAWITEM消息,這個消息處理函數是這樣寫的:
FCanvas.Handle := DrawItemStruct.hDC;
R := ClientRect;
… //省略一部分
if IsDown then
OffsetRect(R, 1, 1);
TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout, FMargin,
FSpacing, State, False, DrawTextBiDiModeFlags(0));
if IsFocused and IsDefault then
begin
R := ClientRect;
InflateRect(R, -4, -4);
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Brush.Color := clBtnFace;
DrawFocusRect(FCanvas.Handle, R);
end;
FCanvas.Handle := 0;
可以看出這和通常處理Paint的方法差不多,其實都是在HDC上作圖。如果你學習過SDK的話,其實我們可以自己處理WM_NOTIFY消息來處理那些由控件產生的消息,只不過VCL替我們封裝了一下而已。
還有一些消息是VCL內部控件而產生的,這類消息通常是CM_XXXX的格式,比如CM_FONTCHANGED這個消息就是當字體改變的時候觸發,詳細的定義你可以在Controls.pas文件中找到,這裡就不再詳細介紹了
對於上面的CM_FONTCHANGED消息,通常是這樣處理的:
procedure TBitBtn.CMFontChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
通過上面的討論,得出一個結論,所有CN/CM消息都可以自己處理,但是他們沒有對應的虛函數,所以我們只好用老方法,所以消息映射宏在這裡是最好得解決方案,比如像這樣:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(CN_DRAWITEM, TWMDrawItem, CNDrawItem)
END_MESSAGE_MAP(TCustomControl)
Void __fastcall CNDrawItem(TWMDrawItem Msg);
定義自己的消息
在上篇文章的結束,我示范了一段元件代碼,如果你還記憶猶新的話:
typedef void __fastcall (__closure *THoverShapeEvent)(TObject* Sender,int Index);
typedef void __fastcall (__closure *TShapeSelectedEvent)(TObject* Sender,int Index);
是否還記得上面的代碼?