|
7. Správy vo Windows |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Na prednáške:
Odporúčam pozerať sa do win32.hlp alebo navštíviť http://msdn.microsoft.com/library.
Vo Windows sú programy riadené udalosťami (pohyb myši, zmena
veľkosti okna a pod.). Po vzniku udalosti sa vygeneruje správa pre určené okno a
táto správa sa buď posiela priamo oknu alebo
sa zaradí do frontu. Správa vo fronte má
nasledujúce, pre nás zaujímavé, položky:
Handle: HWND;
// číslo okna
Message: Cardinal; //
číslo správy - určuje jej význam
WParam: Longint;
// parameter W
LParam: Longint;
// parameter L
Každá správa má svoj význam a podľa toho aj nastavené jednotlivé položky WParam a LParam:
Ako prebieha posielanie správ:
procedure
TForm1.ZatvorOknoAClick(Sender: TObject); begin SendMessage(Handle, WM_CLOSE, 0, 0); end; procedure TForm1.ZatvorOknoBClick(Sender: TObject); begin Perform(WM_CLOSE, 0, 0); end; procedure TForm1.ZatvorOknoCClick(Sender: TObject); begin PostMessage(Handle, WM_CLOSE, 0, 0); end; |
Správy, ktoré boli zaradené do frontu, sa spracovávajú tak, že aplikácia kontroluje stav svojho frontu a postupne z neho vyberá jednotlivé správy:
Potom, ako sa správa z frontu vybrala, distribuuje sa oknu (čo môže byť formulár, tlačidlo alebo komponent odvodený od TWinControl), ktorému je správa určená tak, že pre príslušný objekt sa zavolá metóda:
procedure TWinControl.MainWndProc(var Msg: TMessage);
TMessage je nasledujúci typ:
TMessage = packed record
číslo správy
Msg: Cardinal; //
case Integer of
0: (
WParam: Longint; // parameter
LParam: Longint; // parameter
Result: Longint); // výsledok
1: (
WParamLo: Word;
WParamHi: Word;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;
Metóda MainWndProc zavolá WindowProc:
Pre rôzne správy sa zvykne parameter
Msg pretypovať. Napríklad, pri pohybe
myši:
TWMMouse = packed
record
Msg: Cardinal;
Keys: Longint;
// zodpovedá časti
WParam
case Integer of
0: (
XPos: Smallint; //
zodpovedá časti
LParamLo
YPos: Smallint); //
zodpovedá časti
LParamHi
1: (
Pos: TSmallPoint; //
zodpovedá časti
LParam
Result: Longint);
end;
V nasledujúcom príklade vytvoríme vlastnú metódu, ktorá odfiltruje správu WM_MOUSEMOVE, ak je x-ová súradnica sa myši menšia ako 100:
type TForm1=class(TForm) procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); private OldProc: TWndMethod; procedure NewProc(var Msg: TMessage); public constructor Create(Owner: TComponent); override; destructor Destroy; override; end; var Form1: TForm1; implementation {$R *.DFM} constructor TForm1.Create(Owner: TComponent); begin inherited; OldProc:=WindowProc; WindowProc:=NewProc; end; destructor TForm1.Destroy; begin WindowProc:=OldProc; inherited; end; procedure TForm1.NewProc(var Msg: TMessage); begin if (Msg.Msg=WM_MOUSEMOVE) and (TWMMouse(Msg).XPos<100) then Msg.Result:=0 else OldProc(Msg); end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin Canvas.Pixels[X, Y]:=clRed; end; |
Za normálnych okolností je vlastnosť
WindowProc nastavená tak, že sa volá virtuálna metóda:
procedure WndProc(var
Message: TMessage);
V programoch sa skoro nikdy nezvykne meniť vlastnosť
WindowProc, ale radšej sa prepisuje metóda
WndProc:
type TForm1 = class(TForm) procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer); protected procedure WndProc(var Msg: TMessage); override; public end; procedure TForm1.WndProc(var Msg: TMessage); begin if (Msg.Msg=WM_MOUSEMOVE) and (TWMMouse(Msg).XPos<100) then Msg.Result:=0 else inherited; end; |
Ak by sme potrebovali spracovať viacero správ, pravdepodobne by
WndProc vyzerala oveľa komplikovanejšie - obsahovala by niekoľko príkazov
if, prípadne jeden obrovský príkaz
case:
case Msg.Message of
WM_MOUSEMOVE: ...
WM_
LBUTTONDOWN: ...
atď.
Príkaz case môže byť
veľmi rozsiahly, preto sa ani WndProc
nezvykne prepisovať. Pôvodná WndProc
zabezpečí, že pre správu sa zavolá špeciálna dynamická metóda - tzv.
message handler:
type TForm1 = class(TForm) procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); protected procedure WMMouseMove(var Msg: TWMMouseMove); message WM_MOUSEMOVE; end; procedure TForm1.WMMouseMove(var Msg: TWMMouseMove); begin if Msg.XPos<100 then Msg.Result:=0 else inherited; end; |
Viaceré message handlere sú naprogramované tak, že vyvolávajú rôzne udalosti - WM_MOUSEMOVE vyvoláva udalosť OnMouseMove a pod.
Príklad vlastných message handlerov, ktoré vyvolávajú vlastné udalosti:
unit CoolButton; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, ExtCtrls; type TCoolButton=class(TShape) private FOnMouseEnter: TNotifyEvent; FOnMouseLeave: TNotifyEvent; protected procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE; procedure CMColorChanged(var Msg: TMessage); message CM_COLORCHANGED; procedure DoMouseEnter; virtual; procedure DoMouseLeave; virtual; published property OnMouseEnter: TNotifyEvent read FonMouseEnter write FOnMouseEnter; property OnMouseLeave: TNotifyEvent read FonMouseLeave write FOnMouseLeave; property Color; end; procedure Register; implementation procedure TCoolButton.CMMouseEnter(var Msg: TMessage); begin DoMouseEnter; inherited; end; procedure TCoolButton.CMMouseLeave(var Msg: TMessage); begin DoMouseLEave; inherited; end; procedure TCoolButton.CMColorChanged(var Msg: TMessage); begin inherited; Brush.Color:=Color; end; procedure TCoolButton.DoMouseEnter; begin if Assigned(OnMouseEnter) then OnMouseEnter(Self); end; procedure TCoolButton.DoMouseLeave; begin if Assigned(OnMouseLeave) then OnMouseLeave(Self); end; procedure Register; begin RegisterComponents('Test', [TCoolButton]); end; end. |
Príklad použitia:
procedure
TForm1.CoolButton1MouseEnter(Sender: TObject); begin TCoolButton(Sender).Brush.Color:=clRed; end; procedure TForm1.CoolButton1MouseLeave(Sender: TObject); begin TCoolButton(Sender).Brush.Color:=TCoolButton(Sender).Color; end; |
V prípade, že objekt nemá napísaný message handler pre určitú
správu, zavolá sa metóda:
procedure DefaultHandler(var
Msg);
Ak objekt nedokáže spracovať správu ani v tejto metóde, zavolá sa funkcia
Windows - DefWindowProc. Tá sa postará
o spracovanie správy, prípadne neznámu správu ignoruje.
Používateľom definované správy
Môžeme používať vlastné správy z rozsahu $0400 ... $7FFF. Väčšinou sa odvolávame na konštantu WM_USER=$0400;
const WM_CHANGESIZE=WM_USER+1; type TWMChangeSize=packed record Msg: Cardinal; M: Longint; D: Longint; Result: Longint; end; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; CoolButton1: TCoolButton; Label1: TLabel; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); protected procedure WMChangeSize(var Msg: TWMChangeSize); message WM_CHANGESIZE; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.WMChangeSize(var Msg: TWMChangeSize); var Index: Integer; begin SetBounds( Left, Top, MulDiv(ClientWidth, Msg.M, Msg.D)+Width-ClientWidth, MulDiv(ClientHeight, Msg.M, Msg.D)+Height-ClientHeight)); Font.Height:=MulDiv(Font.Height, Msg.M, Msg.D); for Index:=0 to ControlCount-1 do Controls[Index].Perform(WM_CHANGESIZE, Msg.M, Msg.D); end; procedure TForm1.Button1Click(Sender: TObject); begin SendPerform(WM_CHANGESIZE, 2, 1); end; procedure TForm1.Button2Click(Sender: TObject); begin Perform(WM_CHANGESIZE, 1, 2); end; |
Namiesto cyklu, ktorým rozposielame správy, môžeme použiť metódu Broadcast:
procedure
TForm1.WMChangeSize(var Msg: TWMChangeSize);Iný komponent, ktorý reaguje na správu WM_CHANGESIZE:
type TMyLabel=class(TLabel) protected procedure WMChangeSize(var Msg: TWMChangeSize); message WM_CHANGESIZE; end; procedure TMyLabel.WMChangeSize(var Msg: TWMChangeSize); begin SetBounds( MulDiv(Left, Msg.M, Msg.D), MulDiv(Top, Msg.M, Msg.D), Width, Height); Msg.Result:=1; end; |
Pre komponenty, ktoré na správu nereagovali, upravíme metódu TForm1.WMChangeSize:
procedure TForm1.WMChangeSize(var Msg: TWMChangeSize); var Control: TControl; Index: Integer; begin SetBounds( Left, Top, MulDiv(ClientWidth, Msg.M, Msg.D)+Width-ClientWidth, MulDiv(ClientHeight, Msg.M, Msg.D)+Height-ClientHeight)); Font.Height:=MulDiv(Font.Height, Msg.M, Msg.D); for Index:=0 to ControlCount-1 do begin Control:=Controls[Index]; if Control.Perform(WM_CHANGESIZE, Msg.M, Msg.D)=0 then Control.SetBounds( MulDiv(Control.Left, Msg.M, Msg.D), MulDiv(Control.Top, Msg.M, Msg.D), MulDiv(Control.Width, Msg.M, Msg.D), MulDiv(Control.Height, Msg.M, Msg.D)); end; end; |
© 2003 Ľubomír SALANCI