Zprávy

Objekty mezi sebou komunikují posíláním zpráv (message). Zpráva se typicky skládá z

  1. adresy neboli označení příjemce zprávy (může být i obecné; v takovém případě může být zpráva přijata více objekty a mluvíme o broadcasting=rozhlasové vysílání),
  2. selektoru zprávy vyjadřuje, co se má dělat - je to např. jméno metody, která se má vyvolat,
  3. argumentů - jsou to parametry pro vyvolanou metodu.

POZOR !

Povšimněte si zásadního rozdílu oproti klasickému programování: vlastní provedení operace zde není zavoláno přímo, ale nepřímo tím, že se vyšle příslušná zpráva. Vždy se tedy nejprve hledá příjemce zprávy, tzn. dohledává se, která metoda se aplikuje (díky dědičnosti to může být i velmi složitý proces). Teprve na příjemci zprávy záleží, jak bude zprávu interpretovat.

Zprávy ve Windows

Je třeba vědět, že za realizaci zpráv, jejich předávání a práci s nimi je zodpovědný operační systém, v tomto případě Windows. Zprávy jsou také jedním ze stěžejních (a také nejvíce nespolehlivých) částí Windows. Protože ale práce se zprávami prostřednictvím služeb Windows je složitá a má četná úskalí, tvůrci Delphi zapouzdřili windowsovský systém zpráv do objektů v Delphi, se kterými se pracuje jednodušeji. V Delphi se jim říká události.

Popišme si nejprve zprávy tak, jak je realizují windows. Ve windows je zpráva record, který má následující části:

Pro vysvětlení práce se zprávami Windows si musíme vysvětlit 3 základní pojmy:

Obsluha windowsovských zpráv

Obsluha zpráv v Delphi je daleko jednodušší než ve Windows: místo jedné procedury window procedure, která obsluhuje všechny zprávy, má v Delphi každá zpráva svou vlastní proceduru. Aby procedura mohla být obsluhou zprávy, musí splňovat 3 podmínky:

  1. musí být metodou objektu,
  2. musí mít jeden var parametr typu TMessage (nebo některého specifického typu zprávy),
  3. musí mít direktivy message za kterou je číslo zprávy.

Příklad:

ve Form1:

procedure Prekresli (var Msg: TwmPaint); message wm_Paint;

za begin:

procedure TForm1.Prekresli (var Msg: TwmPaint);

begin

MessageBeep(0);

inherited;

end;

Pozor, obsluha zpráv není bez závazků! Kdybychon v proceduře Prekresli vynechali inherited, nezavolala by se procedura pro obsluhu zprávy wm_Paint z rodiče, tzn. z formuláře TForm. To by mělo fatální důsledky: Windows by nedostaly požadovanou návratovou hodnotu, a proto by aplikace zamrzla (nebo celý počítač).

Posílání windowsovských zpráv

V Delphi existují 3 způsoby, jak lze poslat windowsovskou zprávu:

  1. procedura Perform( ); Pracuje nezávisle na Windows, posílá zprávu jakémukoliv formuláři nebo jeho prvku, pokud známe instanci toho objektu v Delphi.
  2. procedura SendMessage( ); Dovoluje poslat zprávu i oknu, které není v Delphi, pošle zprávu přímo window proceduře zvoleného okna. Čeká, dokud zpráva není doručena.
  3. procedura PostMessage( ); Jako SendMessage, ale nečeká na doručení.

Uživatelem definované zprávy

Uvnitř jedné aplikace není problém poslat zprávu; číslo zprávy si libovolně vybereme z dovoleného rozmezí wm_User+100 až $7FFF (což je rozsah, který Windows rezervují pro uživatelem definované zprávy).

Pokud potřebujeme posílat zprávy mezi aplikacemi (na jednom počítači!), musí se číslo zprávy zaregistrovat ve Windows. V každé aplikaci, mezi kterými si chceme posílat zprávy, zavoláme proceduru RegisterWindowsMessage s parametrem - libovolným (ale u všech aplikací stejným) řetězcem. Windows zprávě přidělí číslo v rozsahu $C000 až $FFFF.

Hook

 

Zprávy v Delphi a události

Delphi zapouzdřují zprávy do recordu Tmessage:

Tmessage = record

Msg: word;

case integer of

0: (

wParam: word;

lParam: LongInt;

Result: LongInt);

1: (

wParamLo, wParamHi: byte;

lParamLo, lParamHi: word;

ResultLo, ResultHi: word);

end;

 

Povšimněte si, že Tmessage má méně údajů, než bylo v původní TMsg; to proto, že Delphi některé údaje zpracovávají interně a nemusíme se o ně starat. Do Result se napíše výsledek v případě, že Windows požadují návratovou hodnotu - o doručení se už postarají Delphi.

Vedle typu TMessage definují Delphi ještě specifické typy zpráv, které obsahují úplnější specifickou informaci. Typicky TwmMouse obsahuje Msg:TmsgParam, Keys:word, Xpos a Ypos: integer a Result:longint.

Obsluha zpráv v Delphi

V Delphi má každý objekt proceduru WndProc, což je vlastně window procedura. Ta se postará o to, aby zpráva byla rozeslána do oken, kterých se týká. Uvnitř okna nastane obsluha zprávy a poté, co je zpráva obsloužena, tak se označí jako vyřízená. Všechny zprávy, které nejsou v objektech obslouženy, se sbírají a přivedou se do procedury DefaultHandler.

O tyto podrobnosti se běžný programátor v Delphi nemusí nijak starat. Pro něj obsluha událostí začíná a končí zjištěním, že Delphi zprávu doručí do správného objektu a uvnitř tohoto objektu že zpráva vyvolá obsluhu zprávy.

Události

Zprávy, které jsou zapouzdřeny a zpracovány systémem Delphi a které jsou doručeny do správných objektů, se nazývají události, anglicky event. Protože události jsou opracované zprávy, nikoho asi nepřekvapí, že každé zprávě Windows odpovídá jedna událost v Delphi:

OnActivate

wm_Activate

OnClick

wm_xButtonClick

OnCreate

wm_Create

OnDblClick

wm_xButtonDblClick

OnKeyDown

wm_KeyDown

onKeyUp

wm_KeyUp

OnKeyPress

wm_Char

OnPaint

wm_Paint

OnResize

wm_Size

OnTimer

wm_Timer

 

Obsluha událostí - handler

Smysl a funkci událostí jsme popsali v předcházejícím odstavci. Nyní si ukážeme, jak jsou události v Delphi implementovány.

  1. Události jsou pointery na metody.Pokud je hodnota pointeru nil, tzn. dokud jsme nezařadili vlastní obsluhu události, žádná obsluha se nevyvolá anebo se vyvolá jen defaultní obsluha.
  2. Události jsou vlastnosti - property. Proto se s nimi pracuje jako s vlastnostmi, například takto.
  3. Obsluha události jsou procedury. Anglicky se jim říká handler. Příklad handleru je zde.

Některé handlery jsou v Delphi předdefinovány, např. handler pro ošetření události Click. Pokud chceme či potřebujeme standardní chování modifikovat, můžeme. Protože handler je metoda jako každá jiná, můžeme ji ve zděděných třídách zmodifikovat - třeba override, přeřídit. Je třeba dodržet 2 podmínky, bez kterých by obsluha událostí byla nespolehlivá:

  1. prázdný (nepřiřazený) handler se musí chovat, jako kdyby tam žádný handler nebyl.
  2. uživatel musí mít možnost přeřídit defaultní chování handleru.

 

Zprávy klávesnice a KeyPreview

Při stisknutí klávesy vznikne událost OnKeyDown, při jejím uvolnění OnKeyUp. Pokud dojde ke stisknutí a uvolnění klávesy, vygeneruje se událost OnKeyPress. Bohužel, OnKeyPress funguje jen na "normální" klávesy, jako jsou písmena, tlačítka apod. Příklad je zde.

Kdybychom potřebovali zpracovat stisknutí některé řídicí klávesy, jako je Home, End, šipky a podobně, museli bychom napsat handler pro událost OnKeyDown.

Někdy je velmi omezující, že událost z klávesnice je směrována přímo k objektu, kterého se týká; kdybychom například v předcházejících příkladech nenastavili u objektu Memo1 vlastnost Enabled:=False, tak bychom se chování OnKeyDown nedočkali. Důvod je prostý - OnKeyDown jsme nastavili ve Form1, ale událost z klávesnice se tam vůbec nedostane, protože Delphi ji oděšlou přímo na Memo1, kam patří. Chceme-li se tomu vyhnout, použijeme KeyPreview.

KeyPreview ve formuláři nastavíme na True. Tím se zajistí, že než událost dojde k objektu pro který je určena, tak se ještě předtím zpracuje ve Form.OnKeyPress. Na jiné události než na ty, na které reaguje OnKeyPress, tato vlastnost nefunguje.

Ošetření výjimek a chyb

 

Vygenerování výjimky