4. Vytvß°enφ udßlostφ

Udßlosti jsou velmi d∙le╛itou Φßstφ komponent. Jsou propojenφm mezi v²skytem v systΘmu (jako je nap°. akce u╛ivatele nebo zm∞na zaost°enφ), na kter² komponenta m∙╛e reagovat a Φßstφ k≤du, kter² reaguje na tento v²skyt. Reagujφcφ k≤d je obsluha udßlosti a je v∞t╣inou zapisovßna u╛ivatelem komponenty. Pomocφ udßlostφ, v²vojß° aplikace m∙╛e p°izp∙sobit chovßnφ komponenty a to bez nutnosti zm∞ny samotnΘ t°φdy. Jako tv∙rce komponenty, pou╛ijeme udßlosti k povolenφ v²vojß°i aplikace p°izp∙sobit chovßnφ komponenty.
Udßlosti pro mnoho akcφ u╛ivatele (nap°. akce my╣i) jsou zabudovßny ve v╣ech standardnφch komponentßch C++ Builderu, ale m∙╛eme takΘ definovat novΘ udßlosti. K vytvß°enφ udßlostφ v komponent∞, se musφme seznßmit s:

C++ Builder implementuje udßlosti jako vlastnosti.

Co jsou udßlosti?

Obecn∞ °eΦeno, udßlost je mechanismus, kter² propojuje v²skyt s urΦit²m k≤dem. Vφce specificky, udßlost je zßv∞r (ukazatel), kter² ukazuje na urΦitou metodu v urΦitΘ instanci t°φdy.
Z perspektivy u╛ivatele komponenty, udßlost je jmΘno svßzanΘ s udßlostφ systΘmu, jako je nap°. OnClick, kterΘmu u╛ivatel m∙╛e p°i°adit specifick² k≤d. Nap°. stisknutφ tlaΦφtka nazvanΘho Button1 mß metodu OnClick. Implicitn∞ C++ Builder generuje obsluhu udßlosti nazvanou Button1Click na formulß°i, kter² obsahuje tlaΦφtko a p°i°adφ ji k OnClick. Kdy╛ se vyskytne udßlost kliknutφ na tlaΦφtku, tlaΦφtko volß metodu p°i°azenou OnClick, v tomto p°φpad∞ Button1Click. K zßpisu udßlosti musφme pochopit toto:

Udßlosti jsou zßv∞ry

C++ Builder pou╛φvß k implementaci udßlostφ zßv∞ry. Zßv∞r je specißlnφ typ ukazatele, kter² ukazuje na urΦitou metodu v urΦitΘ instanci objektu. Jako tv∙rce komponenty m∙╛eme pou╛φvat zßv∞r jako adresu mφsta. Nß╣ k≤d detekuje v²skyt udßlosti a je volßna metoda (je-li) specifikovanß u╛ivatelem pro tuto udßlost.
Zßv∞r obhospoda°uje skryt² ukazatel na instanci t°φdy. Kdy╛ u╛ivatel p°i°adφ obsluhu k udßlosti komponenty, nep°i°adφ metodu jistΘho jmΘna, ale jistou metodu jistΘ instance objektu. Tato instance je obvykle formulß° obsahujφcφ komponentu, ale nemusφ jim b²t.
V╣echny ovladaΦe nap°. d∞dφ virtußlnφ metodu nazvanou Click pro zpracovßnφ udßlosti kliknutφ:
virtual void __fastcall Click(void);
Implementace Click volß u╛ivatelovu obsluhu udßlosti kliknutφ, pokud existuje. Jestli╛e u╛ivatel mß p°i°azenou obsluhu k udßlosti OnClick ovladaΦe, pak v²skytem kliknutφ na ovladaΦi je volßnφ p°i°azenΘ obsluhy. Jestli╛e obsluha nenφ p°i°azena, neprovßdφ se nic.

Udßlosti jsou vlastnosti

Komponenty pou╛φvajφ k implementaci sv²ch udßlostφ vlastnosti. Narozdφl od jin²ch vlastnostφ, udßlosti nemohou pou╛φt metody k implementovßnφ Φßstφ read a write. Je zde nutno pou╛φt soukromou polo╛ku t°φdy a to stejnΘho typu jako je vlastnost.
Podle konvencφ, jmΘno polo╛ky je stejnΘ jako jmΘno vlastnosti, ale na zaΦßtek je p°idßno pφsmeno F. Nap°. zßv∞r (ukazatel) metody OnClick je ulo╛en v polo╛ce nazvanΘ FOnClick typu TNotifyEvent a deklarace vlastnosti udßlosti OnClick je tato:
class TControl : public TComponent
{
private:
  TNotifyEvent FOnClick;
  ...
protected:
  __property TNotifyEvent OnClick = {read=FOnClick, write=FOnClick};
...
};
Stejn∞ jako u jin²ch vlastnostφ, m∙╛eme nastavovat nebo m∞nit hodnotu udßlosti p°i b∞hu aplikace a pomocφ Inspektora objekt∙ m∙╛e u╛ivatel komponent p°i°azovat obsluhu p°i nßvrhu.

Typy udßlostφ jsou typu zßv∞r

Proto╛e udßlost je ukazatel na obsluhu udßlosti, typ vlastnosti udßlosti musφ b²t zßv∞rem. Podobn∞, libovoln² k≤d pou╛it² jako obsluha udßlosti musφ b²t odpovφdajφcφm typem metody t°φdy. V╣echny metody obsluh udßlostφ jsou funkce typu void. Jsou kompatibilnφ s udßlostφ danΘho typu, metoda obsluhy udßlostφ musφ mφt stejn² poΦet a typy parametr∙ a musφ b²t p°edßny ve stejnΘm po°adφ.
C++ Builder definuje typy metod pro v╣echny svΘ standardnφ udßlosti. Kdy╛ vytvß°φme svou vlastnφ udßlost, m∙╛eme pou╛φt existujφcφ typ (pokud vyhovuje) nebo definovat sv∙j vlastnφ.

Obsluhy udßlostφ majφ nßvratov² typ void

P°esto╛e obsluha udßlosti je funkce, nesmφme hodnotu funkce nikdy pou╛φt p°i zpracovßnφ udßlosti (funkce musφ b²t typu void). Prßzdnß funkce vracφ nedefinovan² v²sledek, prßzdnß obsluha udßlosti, kterß by vracela hodnotu, by byla chybnß. Jeliko╛ obsluha udßlosti nem∙╛e vracet hodnotu, musφme zφskßvat informace zp∞t z u╛ivatelova k≤du prost°ednictvφm parametr∙ volan²ch odkazem.
P°φkladem p°edßvßnφ parametr∙ volan²ch odkazem obsluze udßlosti je udßlost stisku klßvesy, kterß je typu TKeyPressEvent. TKeyPressEvent definuje dva parametry, prvnφ, indikujφcφ, kter² objekt generuje udßlost a druh² indikujφcφ, kterß klßvesa byla stisknuta:
typedef void __fastcall(__closure *TKeyPressEvent)(TObject *Sender,Char &Key);
Normßln∞, parametr Key obsahuje znak stisknut² u╛ivatelem. V n∞kter²ch situacφch m∙╛e u╛ivatel komponenty chtφt tento znak zm∞nit. Nap°. m∙╛e chtφt p°evΘst znaky mal²ch pφsmen na odpovφdajφcφ pφsmena velkß. V tomto p°φpad∞ m∙╛e u╛ivatel definovat nßsledujφcφ obsluhu udßlosti pro editaΦnφ ovladaΦ:
void __fastcall TForm1::Edit1KeyPress(TObject *Sender,Char &Key)
{
  Key = UpCase(Key);
}
M∙╛eme takΘ pou╛φvat parametry p°edanΘ odkazem k umo╛n∞nφ u╛ivateli p°epsat implicitnφ zpracovßnφ.

Obsluhy udßlosti jsou nepovinnΘ

P°i vytvß°enφ udßlostφ komponenty musφme pamatovat na to, ╛e u╛ivatel na╣ich komponent nemusφ p°ipojit obsluhu k udßlosti. To znamenß, ╛e na╣e komponenta negeneruje chybu, pokud u╛ivatel komponenty nep°ipojφ obsluhu k jistΘ udßlosti.
Udßlosti ve Windowsovsk²ch aplikacφ vznikajφ stßle. P°i p°esunu my╣i nad viditelnou komponentou, Windows zasφlß °adu zprßv pohybu my╣i, kterΘ komponenta p°evßdφ na udßlosti OnMouseMove. V∞t╣inou v²vojß° nechce zpracovßvat pohyby my╣i a nebude tedy pro tuto udßlost vytvß°et obsluhu. Vytvo°enΘ komponenty nesmφ vy╛adovat obsluhu sv²ch udßlostφ.
V²vojß° aplikace ale m∙╛e zapsat libovoln² k≤d do obsluhy udßlosti. Komponenty ve VCL majφ udßlosti zapsanΘ zp∙sobem minimalizujφcφm mo╛nost generovßnφ chyb obsluhou udßlosti. I kdy╛ nelze zabrßnit p°ed zßpisem logick²ch chyb v k≤du aplikace, lze zajistit inicializaci datov²ch struktur p°ed volßnφm udßlosti a tak v²vojß° aplikace nem∙╛e zφskat chybnß data.

Implementovßnφ standardnφch udßlostφ

V╣echny ovladaΦe v C++ Builderu d∞dφ udßlosti nejd∙le╛it∞j╣φch udßlostφ Windows. Tyto udßlosti naz²vßme standardnφ udßlosti. V╣echny tyto udßlosti zabudovanΘ v abstraktnφch ovladaΦφch, jsou implicitn∞ chrßn∞nΘ, co╛ znamenß, ╛e koncov² u╛ivatel k nim nem∙╛e p°ipojit obsluhu. Kdy╛ vytvo°φme ovladaΦ, m∙╛eme zvolit, zda bude udßlost viditelnß pro u╛ivatele na╣eho ovladaΦe.
Jsou t°i v∞ci, kterΘ je nutno provΘst, kdy╛ pou╛φvßme standardnφ udßlosti v na╣ich ovladaΦφch:

Identifikace standardnφ udßlosti

Jsou dv∞ kategorie standardnφch udßlostφ: udßlosti definovanΘ pro v╣echny ovladaΦe a udßlosti definovanΘ pouze pro standardnφ okennφ ovladaΦe. Nejzßkladn∞j╣φ udßlosti jsou definovßny v typu objektu TControl. V╣echny ovladaΦe (okennφ, grafickΘ nebo u╛ivatelskΘ) d∞dφ tyto udßlosti:
OnClick     OnDragDrop   OnEndDrag   OnMouseMove   OnDblClick   OnDragOver
OnMouseDown OnMouseUp
V╣echny standardnφ udßlosti majφ chrßn∞nΘ virtußlnφ metody deklarovanΘ v TControl, jejich╛ jmΘno odpovφdß jmΘnu udßlosti. Nap°. udßlost OnClick volß metodu jmΘna Click a OnEndDrag volß metodu nazvanou DoEndDrag.
Mimo udßlostφ spoleΦn²ch pro v╣echny ovladaΦe, majφ ovladaΦe odvozenΘ od TWinControl dal╣φ udßlosti:
OnEnter     OnKeyDown   OnKeyPress   OnKeyUp       OnExit
Podobn∞ jako standardnφ udßlosti v TControl, i udßlosti okennφch ovladaΦ∙ majφ odpovφdajφcφ metody.

Zviditeln∞nφ udßlosti

Deklarace standardnφch udßlosti jsou chrßn∞nΘ a chrßn∞nΘ jsou i metody, kterΘ jim odpovφdajφ. Jestli╛e chceme tyto vlastnosti zp°φstupnit u╛ivateli p°i b∞hu programu nebo p°i nßvrhu, musφme op∞tovn∞ deklarovat vlastnost udßlosti jako ve°ejnou nebo zve°ej≥ovanou.
Op∞tovnß deklarace vlastnosti bez specifikace jejφ implementace zachovßvß implementovanou metodu, ale zm∞nφ ·rove≥ p°φstupu. Tφm m∙╛eme zviditelnit standardnφ udßlosti.
Kdy╛ vytvß°φme komponentu a chceme nap°. zp°φstupnit udßlost OnClick b∞hem nßvrhu, pak p°idßme do deklarace typu komponenty:
class PACKAGE TMujOvladac : public TCustomControl
{
__published:
  __property OnClick;    //zviditelnφ OnClick v Inspektoru objekt∙
};

Zm∞na zpracovßnφ standardnφch udßlostφ

Jestli╛e chceme zm∞nit zp∙sob reakce komponenty na jistou t°φdu udßlostφ, zapφ╣eme p°φslu╣n² k≤d a p°i°adφme jej udßlosti. Pro u╛ivatele komponenty je to p°esn∞ to co chce. NicmΘn∞, kdy╛ vytvß°φme komponentu, nenφ to co chceme, proto╛e musφme udr╛et udßlost p°φstupnou pro u╛ivatele komponenty.
Mß to v²znam pro chrßn∞nΘ implementace metod p°i°azen²ch ke ka╛dΘ standardnφ udßlosti. P°edefinovßnφm implementace metody, m∙╛eme modifikovat vnit°nφ obsluhu udßlosti a volßnφm zd∞d∞nΘ metody m∙╛eme obslou╛it standardnφ zpracovßnφ, vΦetn∞ u╛ivatelova k≤du.
Je d∙le╛itΘ ╛e volßme zd∞d∞nou metodu. ObecnΘ pravidlo je volat zd∞d∞nou metodu nejd°φve, a pou╛φt k≤d p∙vodnφ obsluhy udßlosti d°φve ne╛ sv∙j p°izp∙soben². NicmΘn∞, n∞kdy chceme provΘst sv∙j k≤d p°ed volßnφm zd∞d∞nΘ metody. Nap°. jestli╛e zd∞d∞n² k≤d je n∞jak zßvisl² na stavu komponenty a nß╣ k≤d m∞nφ tento stav, pak chceme zm∞nit stav a nechat u╛ivatel∙v k≤d reagovat na zm∞n∞n² stav.
P°edpoklßdejme nap°. ╛e zapisujeme komponentu a chceme modifikovat zp∙sob reakce novΘ komponenty na kliknutφ. Namφsto p°i°azenφ obsluhy k udßlosti OnClick, jak by to provedl u╛ivatel komponenty, p°edefinujeme chrßn∞nou metodu Click:
void __fastcall TMujOvladac::Click() {
  TWinControl::Click();
  // na╣e p°izp∙sobenφ vlo╛φme sem
}

Definovßnφ sv²ch vlastnφch udßlostφ

Definovßnφ nov²ch udßlostφ je relativn∞ vzßcnΘ. Mnohem Φast∞ji provßdφme p°edefinovßnφ ji╛ existujφcφch udßlostφ. NicmΘn∞ n∞kdy, kdy╛ chovßnφ komponenty je znaΦn∞ odli╣nΘ, pak je vhodnΘ definovat pro ni udßlost.
Definovßnφ udßlosti probφhß ve Φty°ech krocφch:

Generovßnφ udßlosti

Prvnφ co provedeme, kdy╛ definujeme svou vlastnφ udßlost, kterß neodpovφdß ╛ßdnΘ standardnφ udßlosti, je urΦenφ co udßlost spustφ. Pro n∞kterΘ udßlosti je odpov∞∩ z°ejmß. Nap°. kdy╛ u╛ivatel stiskne levΘ tlaΦφtko my╣i, Windows zasφlß zprßvu WM_LBUTTONDOWN aplikaci. Po p°φjmu tΘto zprßvy komponenta volß svou metodu MouseDown, kterß dßle volß n∞jak² k≤d, kter² u╛ivatel mß p°ipojen k udßlosti OnMouseDown.
Ale u n∞kter²ch udßlostφ je obtφ╛n∞j╣φ urΦit, co specifikuje externφ v²skyt. Nap°. posuvnφk mß udßlost OnChange, spou╣t∞nou n∞kolika typy v²skyt∙, vΦetn∞ klßvesnice, kliknutφ my╣i nebo zm∞nou v jinΘm ovladaΦi. Kdy╛ definujeme svou udßlost, musφme zajistit, ╛e v╣echny mo╛nΘ v²skyty spustφ na╣i udßlost.
Jsou dva typy v²skyt∙, pro kterΘ musφme udßlosti o╣et°it: zm∞na stavu a akce u╛ivatele. Mechanismus zpracovßnφ je stejn², ale li╣φ se sΘmanticky. Udßlost akce u╛ivatele je tΘm∞° v╛dy spou╣t∞na zprßvou Windows, indikujφcφ, ╛e u╛ivatel provedl n∞co na co na╣e komponenta mß reagovat. Udßlost zm∞ny stavu je takΘ svßzßna se zprßvou od Windows (nap°. zm∞na zaost°enφ nebo povolenφ n∞Φeho), ale m∙╛e takΘ vzniknou na zßklad∞ zm∞ny vlastnosti nebo jinΘho k≤du. Musφme definovat, ╛e to v╣e m∙╛e spustit udßlost.

Definovßnφ typu obsluhy

Kdy╛ urΦφme jak na╣e udßlost vznikne, musφme definovat jak ji chceme obslou╛it. To znamenß urΦit typ obsluhy udßlosti. V mnoha p°φpadech, obsluhy pro udßlosti definujeme sami jednoduch²m oznßmenφm nebo typem specifick²m udßlosti. To takΘ umo╛≥uje zφskat informace zp∞t z obsluhy.
Jednoduchß oznßmenφ
Oznamovacφ udßlost je udßlost, kterß pouze °φkß, se jistß udßlost nastala a nespecifikuje ╛ßdnΘ informace o nφ. Oznßmenφ pou╛φvß typ TNotifyEvent, kter² mß pouze jeden parametr (odesilatel udßlosti). Tedy v╣echny obsluhy pro oznßmenφ znajφ o udßlosti pouze to, o jakou t°φdu udßlosti se jednß a kterß komponenta udßlost zp∙sobila. Nap°. udßlosti kliknutφ jsou oznßmenφ. Kdy╛ zapisujeme obsluhu pro udßlost kliknutφ, v╣e co znßme je to, ╛e kliknutφ nastalo a na kterΘ komponent∞ bylo kliknuto.
Oznßmenφ jsou jednosm∞rn² proces. Nenφ mechanismus k poskytnutφ zp∞tnΘ vazby nebo k zabrßn∞nφ budoucφ obsluhy oznßmenφ.
Obsluha specifickß pro udßlost
V n∞kter²ch p°φpadech nßm ale tyto informace nestaΦφ. Nap°. jestli╛e udßlost je udßlost stisku klßvesy, je pravd∞podobnΘ, ╛e chceme takΘ znßt, kterß klßvesa byla stisknuta. V t∞chto p°φpadech po╛adujeme typ obsluhy, kter² obsahuje parametry s n∞jak²mi nezbytn²mi informacemi o udßlosti. Jestli╛e na╣e udßlost je generovßna v reakci na zprßvu, je pravd∞podobnΘ, ╛e parametry p°edßvanΘ obsluze udßlosti zφskßme p°φmo z parametr∙ zprßvy.
Nßvrat informacφ z obsluhy
Proto╛e v╣echny obsluhy udßlostφ jsou funkce vracejφcφ void, jedin² zp∙sob k p°edßnφ informacφ zp∞t z obsluhy je pomocφ parametru volanΘho odkazem. Na╣e komponenta m∙╛e pou╛φt tyto informace k urΦenφ jak a co udßlost ud∞lß po provedenφ obsluhy u╛ivatele. Nap°. v╣echny udßlosti klßvesnice (OnKeyDown, OnKeyUp a OnKeyPress) p°edßvajφ hodnotu stisknutΘ klßvesy v parametru volanΘm odkazem jmΘna Key. Obsluha udßlosti m∙╛e zm∞nit Key a tak aplikace vidφ jinou klßvesu ne╛ kterß zp∙sobila udßlost. To je nap°. zp∙sob jak zm∞nit zapsan² znak na velkß pφsmena.

Deklarovßnφ udßlosti

Kdy╛ jsme urΦili typ na╣φ obsluhy udßlosti, m∙╛eme deklarovat zßv∞r a vlastnost pro udßlost. Udßlosti dßme smysluplnΘ a popisnΘ jmΘno tak, aby u╛ivatel pochopil, co udßlost d∞lß. Je vhodnΘ, aby bylo konzistentnφ s jmΘny podobn²ch udßlostφ v jin²ch komponentßch.
JmΘna v╣ech standardnφch udßlostφ v C++ Builderu zaΦφnajφ "On". Je to pouze konvence, p°ekladaΦ nevy╛aduje jejφ dodr╛ovßnφ. Inspektor objekt∙ urΦuje ╛e vlastnost je udßlost podle typu vlastnosti: v╣echny vlastnosti zßv∞r∙ jsou pova╛ovßny za udßlosti a jsou zobrazeny na strßnce udßlostφ.

Volßnφ udßlosti

Obecn∞ je vhodnΘ centralizovat volßnφ udßlosti, tj. vytvo°it virtußlnφ metodu v na╣i komponent∞, kterß volß u╛ivatelskou obsluhu udßlosti (pokud ji u╛ivatel p°i°adφ) a provßdφ n∞jakΘ implicitnφ zpracovßnφ.
Umφst∞nφ v╣ech volßnφ udßlostφ na jednom mφst∞ zajistφ, ╛e n∞jakß odvozenß novß komponenta od na╣φ komponenty m∙╛e p°izp∙sobit obsluhu udßlosti p°edefinovßnφm jen jednΘ metody, namφsto hledßnφm v na╣em k≤du mφsta, kde udßlost je volßna.
Nesmφme nikdy vytvo°it situaci ve kterΘ prßzdnß obsluha udßlosti zp∙sobφ chybu, tj. vlastnφ funkΦnost na╣ich komponent nesmφ zßviset na jistΘ reakci z k≤du obsluhy udßlosti. Z tohoto d∙vodu, prßzdnß obsluha musφ produkovat stejn² v²sledek jako neobslou╛enß udßlost. Komponenty nikdy nesm∞jφ po╛adovat aby u╛ivatel je pou╛il jist²m zp∙sobem. D∙le╛it²m aspektem je princip, ╛e u╛ivatel komponent nemß ╛ßdnΘ omezenφ na to, co m∙╛e s nimi d∞lat v obsluze udßlosti.
Jeliko╛ prßzdnß obsluha se mß chovat stejn∞ jako ╛ßdnß obsluha, k≤d pro volßnφ u╛ivatelskΘ obsluhy mß vypadat takto:
if (OnClick) OnClick(this);
// provedenφ implicitnφho zpracovßnφ
Nikdy nesmφme pou╛φt tento zp∙sob:
if (OnClick) OnClick(this);
else
// provedenφ implicitnφho zpracovßnφ
Pro n∞kterΘ typy udßlostφ, u╛ivatel m∙╛e chtφt nahradit implicitnφ zpracovßnφ. K umo╛n∞nφ aby to mohl ud∞lat, musφme p°idat parametr volan² odkazem k obsluze a testovat jej na jistou hodnotu p°i nßvratu z obsluhy. Kdy╛ nap°. zpracovßvßme udßlost stisku klßvesy, u╛ivatel m∙╛e po╛adovat implicitnφ zpracovßnφ nastavenφm parametru Key na nulov² znak (viz nßsledujφcφ p°φklad):
if (OnKeyPress) OnKeyPress(this, &Key);
if (Key != NULL)  // provedenφ implicitnφho zpracovßnφ
SkuteΦn² k≤d se nepatrn∞ li╣φ od zde uvedenΘho (spolupracuje se zprßvou Windows), ale logika je stejnß. Implicitn∞ komponenta volß n∞jakou u╛ivatelem p°i°azenou obsluhu a pak provede svΘ standardnφ zpracovßnφ. Jestli╛e u╛ivatelova obsluha nastavφ Key na nulov² znak komponenta p°eskoΦφ implicitnφ zpracovßnφ.


S praktick²m pou╛φvßnφm udßlostφ se podrobn∞ji seznßmφme v nßsledujφcφch kapitolßch.
 
4. Vytvß°enφ udßlostφ