Příkazy a zákazy ve WPF
Dnešním článkem jen maličko odbočím od povídání o prezentačním návrhovém vzoru Model-View-ViewModel a vrhnu se do příkazů a zákazů, které dají do chodu snad každou aplikaci a pomocí nichž má možnost uživatel komunikovat s naším programem a řídit jej.
Co si můžeme pod takovými příkazy, ve WPF a Silverlight značenými jako Command, představit? Jedná se například o možnost uživatele vyjmout (Cut) nebo vložit (Paste) text či objekt z aplikace. Ve WPF aplikacích pak rozlišujeme několik různých typů těchto příkazů, které jsou rozděleny do následujících tříd ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands, a ComponentCommands. Všechny příkazy z těchto tříd však mají jedno společné a to, že vycházejí ze třídy RoutedCommand. Tato třída pak implementuje rozhraní ICommand, které si zapamatujte, neboť s tímto rozhraním se budete setkávat asi poměrně často.
Co by to však bylo za příkaz, kdybychom neměli k dispozici způsob, jak jej zavolat. A tak i WPF poskytuje několik možností, které nám umožní příkaz spustit. Mezi základní možnosti, které má uživatel většinou k dispozici patří klávesnice a myš a tak i my máme k dispozici třídy, které dokáží s těmito vstupními zařízeními komunikovat, patří do skupiny tzv. InputGesture a jsou to KeyGesture a MouseGesture. Co si vlastně představit pod takovým vstupem pomocí KeyGesture, je případ, kdy uživatel stiskne nějakou klávesovou zkratku. Může se jednat o známé zkratky jako je Ctrl+X, které jsou přiřazeny k předdefinovaným příkazům, nebo se může jednat o námi vytvořenou kombinaci, kterou přiřadíme některému příkazu.
Takové přiřazení můžeme udělat buď programově, a nebo zapsat pomocí xaml:
<Window.InputBindings> <KeyBinding Key="B" Modifiers="Control" Command="ApplicationCommands.Open" /> </Window.InputBindings>
Tady jsem přidal novou klávesovou zkratku Ctrl+B k příkazu Open.
Dobrá tedy, máme nějaký příkaz, víme také jak jej zavolat, ale ještě nevíme, jak propojit příkaz s obsluhou tohoto příkazu (handlerem). K takovémuto propojení slouží třída CommandBinding, které máme možnost předat odkaz na náš příkaz – Command - obsloužit tento příkaz nám poté pomůže zajistit jeden z přiřazených handlerů událostí. Tyto handlery můžeme přiřadit k událostem PreviewExecuted nebo Executed, které zajišťují samotné provedení příkazu. Pamatujete si jak jsem na začátku psal o tom, že se budeme věnovat i zákazům, tak právě přichází ta správná chvíle a v okamžiku, kdy využijeme událostí PreviewCanExecute a CanExecute a přiřadíme jim odpovídající handlery, můžeme zakázat vykonávání našeho příkazu, nebo jej naopak povolit.
Jak tedy může taková deklarace příkazu vypadat zapsaná pomocí jazyka xaml je uvedeno v následující ukázce:
<Window.CommandBindings> <CommandBinding Command="ApplicationCommands.Open" Executed="OpenExecuted" CanExecute="OpenCanExecute"/> </Window.CommandBindings>
Jak vidíte, deklaroval jsem předdefinovaný Command s názvem Open na úrovni celého okna. Zároveň jsem přiřadil k událostem jejich handlery.
Z ukázky je patrné, že příkazy nemusíme deklarovat tam, kde skutečně vznikají, ale využijeme toho, že ve WPF dochází k probublání příkazu stromem elementů nahoru, ke kořenovému elementu – v tomto případě tedy elementu Window. V okamžiku, kdy se narazí na obsluhu příslušného příkazu, příkaz se obslouží a pokud neurčíme jinak, tak se pokračuje dále v probublávání.
Opět asi bude vhodné si na příkladu ukázat, jak vlastně může vypadat taková jednoduchá obsluha našich příkazů:
void OpenExecuted(object target, ExecutedRoutedEventArgs e) { String commandName, targetName; commandName = ((RoutedCommand)e.Command).Name; targetName = ((FrameworkElement)target).Name; Debug.WriteLine("Příkaz " + commandName + " byl vykonán na elementu " + targetName); }
Nejspíš bude náplní obsluhy příkazu úplně něco jiného, ale pro ukázku to doufám stačí. Obdobně pak zapíšeme i obsluhu pro zjištění, zda náš příkaz může být vlastně vykonán nebo jestli není zakázán:
void OpenCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; }
Celý zákaz příkazu se řídí tím, jak naplníme vlastnost CanExecute předaného argumentu. Opět bude platit, že bude existovat složitější logika, ze které vyplyne, zda příkaz může být povolen nebo bude zakázán.
Zmiňoval jsem se o tom, že můžeme také zakázat další probublávání příkazu, to provedeme tak, že naplníme vlastnost Handled na hodnotu true:
e.Handled = true;
čímž dojde k zastavení probublávání.
CommandManager
Z předchozího popisu je asi zřejmé, že bez někoho, kdo celý tento poměrně složitý mechanismus bude řídit by to nešlo, tím dotyčným, který vše řídí a diriguje, je objekt CommandManager. Nejen, že tento objekt poskytuje několik statických metod pro přidání, případně odebrání, handlerů k některému elementu, také poskytuje možnost registrace a propojení CommandBindingu a InputBindingu. V neposlední řadě, a tady opět upozorňuji, že této schopnosti CommandManageru využijeme při použití vzoru MVVM, zajišťuje vyvolání události CanExecuteChanged, tak aby se nám správně a především ve správný okamžik aktualizoval stav, kdy je možné provést příkaz, případně bude příkaz zakázán.
Závěrem
Já jen doufám, že jsem nezpůsobil velký zmatek při výkladu příkazů a zákazů, se kterými se můžete setkat ve WPF. Problematika je poměrně široká a snažil jsem se ji shrnout a vytáhnout to, co mi přišlo nejpodstatnější. Pokud se mi to přeci jen nepovedlo, tak se určitě ptejte v komentářích. V příštím článku se na příkazy a zákazy podíváme z pohledu vývoje pro aplikaci psanou pomocí vzoru MVVM.
Komentáře