程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi TControl 類(2)消息

Delphi TControl 類(2)消息

編輯:Delphi

Delphi--TControl與Windows消息的封裝

 

TControl是從TPersistent類的子類TComponent類繼承而來的。TPersistent抽象基類具有使用流stream來存取類的屬性的能力。

TComponent類則是所有VCL組件的父類。
這就是所有的VCL組件包括您的自定義組件可以使用dfm文件存取屬性的原因(當然要是TPersistent的子類,我想您很少需要直接從TObject類來派生您的自定義組件吧)。

TControl類的重要性並不亞於它的父類們。在BCB的繼承關系中,TControl類的是所有VCL可視化組件的父類。實際上就是控件的意思吧。所謂可視化是指您可以在運行期間看到和操縱的控件。這類控件所具有的一些基本屬性和方法都在TControl類中進行定義。

TControl的實現在\Borland\CBuilder5\Source\Vcl\control.pas中可以找到。

TControl繼承但並沒有重寫TObject的Dispatch方法。反而提供了一個新的方法WndProc。一起來看看Borland的工程師們是怎麼寫的吧。
[delphi]
procedure TControl.WndProc(var Message: TMessage);  var  Form: TCustomForm;  begin  //由擁有control的窗體來處理設計期間的消息   if (csDesigning in ComponentState) then  begin  Form := GetParentForm(Self);  if (Form <> nil) and (Form.Designer <> nil) and  Form.Designer.IsDesignMsg(Self, Message) then Exit;  end  //如果需要,鍵盤消息交由擁有control的窗體來處理   else if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then  begin  Form := GetParentForm(Self);  if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;  end  //處理鼠標消息   else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then  begin  if not (csDoubleClicks in ControlStyle) then  case Message.Msg of  WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:  Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);  end;  case Message.Msg of  WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);  WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:  begin  if FDragMode = dmAutomatic then  begin  BeginAutoDrag;  Exit;  end;  Include(FControlState, csLButtonDown);  end;  WM_LBUTTONUP:  Exclude(FControlState, csLButtonDown);  end;  end    // 下面一行有點特別。如果您仔細的話會看到這個消息是CM_VISIBLECHANGED.   // 而不是我們熟悉的WM_開頭的標准Windows消息.   // 盡管Borland沒有在它的幫助中提到有這一類的CM消息存在。但很顯然這是BCB的   // 自定義消息。呵呵,如果您對此有興趣可以在VCL源碼中查找相關的內容。一定會有不小的收獲。   else if Message.Msg = CM_VISIBLECHANGED then  with Message do  SendDockNotification(Msg, WParam, LParam);  // 最後調用dispatch方法。   Dispatch(Message);  end;  procedure TControl.WndProc(var Message: TMessage);
var
Form: TCustomForm;
begin
//由擁有control的窗體來處理設計期間的消息
if (csDesigning in ComponentState) then
begin
Form := GetParentForm(Self);
if (Form <> nil) and (Form.Designer <> nil) and
Form.Designer.IsDesignMsg(Self, Message) then Exit;
end
//如果需要,鍵盤消息交由擁有control的窗體來處理
else if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
begin
Form := GetParentForm(Self);
if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
end
//處理鼠標消息
else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
begin
if not (csDoubleClicks in ControlStyle) then
case Message.Msg of
WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
end;
case Message.Msg of
WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);
WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
begin
if FDragMode = dmAutomatic then
begin
BeginAutoDrag;
Exit;
end;
Include(FControlState, csLButtonDown);
end;
WM_LBUTTONUP:
Exclude(FControlState, csLButtonDown);
end;
end

// 下面一行有點特別。如果您仔細的話會看到這個消息是CM_VISIBLECHANGED.
// 而不是我們熟悉的WM_開頭的標准Windows消息.
// 盡管Borland沒有在它的幫助中提到有這一類的CM消息存在。但很顯然這是BCB的
// 自定義消息。呵呵,如果您對此有興趣可以在VCL源碼中查找相關的內容。一定會有不小的收獲。
else if Message.Msg = CM_VISIBLECHANGED then
with Message do
SendDockNotification(Msg, WParam, LParam);
// 最後調用dispatch方法。
Dispatch(Message);
end;看完這段代碼,你會發現TControl類實際上只處理了鼠標消息,沒有處理的消息最後都轉入Dispatch()來處理。

但這裡需要強調指出的是TControl自己並沒有獲得焦點Focus的能力。TControl的子類TWinControl才具有這樣的能力。我憑什麼這樣講?呵呵,還是打開BCB的幫助。很多朋友抱怨BCB的幫助實在不如VC的MSDN。毋庸諱言,的確差遠了。而且這個幫助還經常有問題。但有總比沒有好啊。

Delphi消息的發送有三種方法:

1.Tcontrol類的Perform對象方法。可以向任何一個窗體或控件發送消息,只需要知道窗體或控件的實例。其聲明如下:

function Tcontrol.Perform(Msg:Cardinal;Wparam,Lparam:Longint):Longint

2.Windows的API函數SendMessage()和Postmessage()。其聲明如下:

function SendMessage(hWnd: HWND; Msg: UINT;wParam:WPARAM; lParam: LPARAM):LRESULT;stdcall;

function SendMessage(hWnd: HWND; Msg: UINT;wParam: WPARAM; lParam:LPARAM):LRESULT;stdcall

PostMessage函數將消息添加到應用程序的消息隊列中去。應用程序的消息循環會從消息隊列中提取登記的該消息,再發送到相應的窗口中。

TControl與Windows消息的封裝

TObject提供了最基本的消息分發和處理的機制,而VCL真正對Windows系統消息的封裝則是在TControl中完成的。

TControl將消息轉換成VCL的事件,以將系統消息融入VCL框架中。

消息分發機制在4.2節已經介紹過,那麼系統消息是如何變成事件的呢?

現在,通過觀察TControl的一個代碼片段來解答這個問題。在此只以鼠標消息變成鼠標事件的過程來解釋,其余的消息封裝基本類似。

先摘取TControl聲明中的一個片段: 
 TControl = class(TComponent)

 Private

 ……
 FOnMouseDown: TMouseEvent;
 ……

 procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;

 Shift: TShiftState);

 ……

 procedure MouseDown(Button: TMouseButton; Shift: TShiftState;

 X, Y: Integer); dynamic;

 ……

 procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;

 procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;

 procedure WMMButtonDown(var Message: TWMMButtonDown); message WM_MBUTTONDOWN;


 ……
 protected
 ……

 property OnMouseDown: TMouseEvent read FOnMouseDown write

 FOnMouseDown;

 ……

 end;
       這段代碼是TControl組件類的聲明。如果你從沒有接觸過類似的VCL組件代碼的代碼,不明白那些property、read、write的意思,那麼可以先跳轉到5.1節閱讀一下相關的基礎知識,然後再回過頭來到此處繼續。

TControl聲明了一個OnMouseDown屬性,該屬性讀寫一個稱為FOnMouseDown的事件指針。因此FOnMouseDown會指向OnMouseDown事件的用戶代碼。

TControl聲明了WMLButtonDown、WMRButtonDown、WMMButtonDown 3個消息 處理函數,它們分別處理WM_LBUTTONDOWN、WM_RBUTTONDOWN、WM _MBUTTONDOWN 3個Windows消息,對應於鼠標的左鍵按下、右鍵按下、中鍵按下3個硬件事件。

另外,還有一個DoMouseDown()方法和一個MouseDown()的dynamic方法,它們與消息處理函數之間2是什麼樣的關系呢?

現在,就來具體看一下這些函數的實現。

這裡是3個消息的處理函數:

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
 SendCancelMode(Self);
 inherited;
 if csCaptureMouse in ControlStyle then
 MouseCapture := True;
 if csClickEvents in ControlStyle then
 Include(FControlState, csClicked);
 DoMouseDown(Message, mbLeft, []);
end;

procedure TControl.WMRButtonDown(var Message: TWMRButtonDown);
begin
 inherited;
 DoMouseDown(Message, mbRight, []);
end;

procedure TControl.WMMButtonDown(var Message: TWMMButtonDown);
begin
 inherited;
 DoMouseDown(Message, mbMiddle, []);
end;

當TObject.Dispatch()將WM_LBUTTONDOWN消息、WM_RBUTTONDOWN消息或WM_MBUTTONDOWN消息分發給TControl的派生類的實例後,WMLButtonDown()、WMRButtonDown()或WMMButtonDown()被執行,然後它們都有類似這樣

DoMouseDown(Message, mbRight, []); 的代碼來調用DoMouseDown():

procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton; Shift: TShiftState);
begin
 if not (csNoStdEvents in ControlStyle) then
 with Message do
 if (Width > 32768) or (Height > 32768) then
 with CalcCursorPos do
 MouseDown(Button, KeysToShiftState(Keys) + Shift, X, Y)
 else
 MouseDown(Button, KeysToShiftState(Keys) + Shift, Message.XPos, Message.Ypos );
end;

在DoMouseDown()中進行一些必要的處理工作後(特殊情況下重新獲取鼠標位置),就會調用MouseDown():

procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
 if Assigned(FOnMouseDown) then
 FOnMouseDown(Self, Button, Shift, X, Y);
end;

在MouseDown()中,才會通過FOnMouseDown事件指針真正去執行用戶定義的OnMouseDown事件的代碼。

由此,完成了Windows系統消息到VCL事件的轉換過程。


因此,從TControl派生的類都可以擁有OnMouseDown事件,只不過該事件屬性在TControl中被定義成protected,只有其派生類可見,並且在派生類中可以自由選擇是否公布這個屬性。要公布該屬性只需要簡單地將其聲明為published即可。如:

TMyControl = class(TControl)
published
 property OnMouseDown;
end;

這些函數過程的調用關系如圖4.3所示。

DoMouseDown()
MouseDown()
程序員的OnMouseDown事件代碼
WMMouseDown()
Dispatch(WM_LBUTTONDOWN); Dispatch(WM_LBUTTONDOWN);


圖4.3 WM_LBUTTONDOWN消息到OnMouseDown事件的轉換過程

在此,只是以OnMouseDown事件為例。其實,VCL對Windows各個消息的封裝大同小異,以此一例足以說明事件模型的原理。

另外,值得注意的是,在上例中的MouseDown()函數是一個dynamic方法,因此可以通過在TControl派生類中覆蓋MouseDown()來處理自己所編寫組件的鼠標按下事件,然後通過

inherited;

語句調用TControl的MouseDown()來執行使用組件的程序員所編寫的OnMouseDown的代碼。具體內容會在第5章中展開。
至此,讀者應該已經了解了VCL事件與Windows消息的對應關系,應該知道平時為組件寫的事件代碼是如何被執行的


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved