Použití příkazů ve WPF aplikaci s MVVM
Z předchozích článků již víme, jakým způsobem zapojit do naší aplikace prezentační návrhový vzor Model-View-ViewModel a také jsem ukázal a popsal, jak se pracuje s příkazy ve WPF aplikaci. V dnešním článku bych rád demonstroval zapojení příkazů (dědících od rozhraní ICommand) do WPF aplikace a jak rozšířit ViewModel o poskytování takových příkazů.
Již minule jsem ukazoval, jak je možné příkaz zapsat v jazyce XAML a jak takový příkaz obsloužit, dobře nám k tomu posloužila třída CommandBinding, která propojila příkaz s jeho obsluhou. Demonstrovali jsme si to na příkladu, který využívá jeden z připravených typů příkazů. Jak bychom však postupovali, když chceme zapojit nějaký náš vlastní příkaz?
Definice vlastních příkazů
Při definici vlastního příkazu vycházíme z toho, že ve WPF všechny příkazy jsou poděděny od třídy RoutedCommand, která zajišťuje potřebnou infrastrukturu pro probublání příkazu a zároveň správné napojení na CommandManager.
Vytvořím tedy statickou třídu, která bude uchovávat jednotlivé definice příkazů, které použijeme v naší aplikaci. Samotné zapsání příkazu je pak již snadné, provede se vytvořením instance třídy RoutedCommand.
public static RoutedCommand Subscribe = new RoutedCommand();
Tím mám připraven tento příkaz k použití, ať už v xaml deklaraci nebo v codebehind okna aplikace. Jak tedy mohu propojit tento příkaz s jeho obsluhou? Opět je to na jeden řádek kódu, kdy využiji třídy CommandBinding a v konstruktoru okna zapíši následující část kódu:
CommandBindings.Add(new CommandBinding(MeasureCommands.Subscribe, SubscribeExecuted, CanSubscribeExecute));
Metody SubscribeExecuted a CanSubscribeExecute jsou obslužnými metodami, ve kterých provedu potřebnou implementaci pro obsluhu příkazu.
Tímto bych mohl skončit, protože již umíme vyvolat jak předdefinované příkazy, tak i příkazy vlastní. Vzhledem k tomu, že i tento článek patří do seriálu o použití vzoru Model-View-ViewModel rád bych ukázal, jak zapojit příkazy do aplikace psané pomocí vzoru MVVM.
Zapojení příkazů do MVVM
Z předchozích článků již víte, že ViewModel poskytuje data pro View pomocí veřejných vlastností, které jsou následně bindovány na jednotlivé elementy uživatelského rozhraní. Nejinak tomu bude i v případě použití příkazů, které stejným způsobem zpřístupníme na toto rozhraní.
Hned na úvod musím podotknout, že bych i v tomto případě mohl použít třídu RoutedCommand a využít všech jejích předností, které nabízí pro zpracování a obsluhu příkazů. Jelikož se však chci držet jistých nepsaných pravidel, která se postupně tvoří s tím, jak se tento vzor čím dál častěji používá, ukáži vytvoření a napojení příkazu pomocí rozhraní ICommand, které také ostatně implementuje třída RoutedCommand.
Použití rozhraní s sebou nese některé kladné, ale také stinné stránky a tak se ve většině frameworcích, které usnadňují vývoj na základě MVVM vzoru setkáme s nějakou třídou, která již implementuje rozhraní ICommand a zajistí pro nás potřebnou infrastrukturu obdobně, jako to činí třída RoutedCommand. Otázkou, kterou si tedy někteří z vás nyní položí je, proč rovnou nepoužijeme třídu RoutedCommand? Je to především z důvodu testování, kdy se proti rozhraní snadněji vytvářejí tzv. mock objekty.
Definice příkazu pro ViewModel
Je nejvyšší čast ukázat si, jakým způsobem vytvoříme příkaz a jak jej zpřístupníme ve ViewModelu. Z předchozího odstavce víme, že budeme potřebovat naimplmentovat rozhraní ICommand. Toto rozhraní obsahuje dvě metody s názvy Execute a CanExecute a jednu událost CanExecuteChanged. Abychom mohli zapsat obsluhu metod, měli bychom nějakým způsobem získat odkaz na vytvořený ViewModel, což provedeme v konstruktoru tohoto příkazu. Náš kód pro příkaz tak může vypadat nějak takto:
public class SubscribeCommand: ICommand { private readonly MeasureViewModel _viewModel; public SubscribeCommand(MeasureViewModel viewModel) { _viewModel = viewModel; } public void Execute(object parameter) { _viewModel.Subscribe(); } public bool CanExecute(object parameter) { return _viewModel.CanSubscribe(); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } }
Myslím si, že asi není co k vysvětlování, v konstruktoru si uložíme referenci na náš ViewModel a v jednotlivých metodách potom voláme metody nad tímto VM, které nám zajišťují samotnou logiku obsluhy události. Za povšimnutí snad stojí především obsluha události, na kterou jsem upozorňoval v minulém článku (předposlední odstavec), kdy je třeba explicitně obsloužit událost RequerySuggested na třídě CommandManageru. Tím budeme mít zajištěno, že napojený element ve View bude správně reagovat na změny a bude se tak správně volat metoda CanExecute tohoto příkazu.
Vypublikování takovéhoto příkazu ze třídy ViewModelu je poté již poměrně snadné, někde, nejlépe však v konstruktoru třídy, je třeba vytvořit tento příkaz a předat mu instanci VM
_subscribeCommand = new SubscribeCommand(this);
(field _subscribeCommand je deklarován takto na úrovni třídy)
private ICommand _subscribeCommand;
a poté definovat veřejně přístupnou vlastnost, kterou zpřístupníme tento příkaz pro View
public ICommand SubscribeCommand { get { return _subscribeCommand; } }
Tímto je naše práce ve ViewModelu hotová a jediné co nám zbývá je napojit tento příkaz na některý ovládací prvek v okně naší aplikace.
Přiřazení příkazu pomocí data bindingu ve View
Propojení View a ViewModelu se děje pomocí tzv. data bindingu, kdy se využívá markup extension {Binding} pro napojení vlastností VM do vlastností elementů ve View. Tohoto data bindingu využijeme i v případě přiřazení příkazu k elementu, v tomto konkrétním případě k elementu Button.
Když se podíváme na jednotlivé vlasnosti třídy Button zjistíme, že obsahuje vlastnost Command, která je typu ICommand a na kterou tak můžeme navázat náš příkaz. Toto provázání provedeme následující deklarací:
<Button Command="{Binding SubscribeCommand}"
a můžeme směle prohlásit, že máme hotovo. Ano skutečně, to je vše co potřebujeme k tomu, aby naše aplikace správně reagovala na akce, které provede uživatel. Takto provázané tlačítko bude správně reagovat a měnit svůj stav podle toho, zda má být možné vykonat příkaz, nebo zda tuto akci uživateli zakážeme (vrátíme hodnotu false v metodě CanExecute).
Závěrem
Tímto článkem by se dalo skončit a prohlásit, že vše o tom, jak vyvíjet pomocí prezentačního návrhového vzoru, bylo napsáno. Již umíme vytvořit ViewModel, napojit jej na View a stejně tak umíme komunikovat s Modelem naší aplikace. Naše aplikace pak bude interaktivní ve vzahu ke koncovému uživateli, který ji dokáže ovládat a aplikace umí reagovat správně na jeho požadavky. Záměrně jsem však vynechal slůvko využívat, neboť to co jsem doteď napsal byl základ, který je třeba znát, abychom dosáhli přínosu při aplikaci tohoto návrhového vzoru. O tom jak je využít budou příští články.
Komentáře