Používáme MVVM - jednoduchý ViewModel
Minulým článkem jsem začal povídání o prezentačním návrhovém vzoru Model-View-ViewModel, který je hojně využíván při tvorbě WPF/Silverlight aplikací. Dnešním článkem se pak přesunu z roviny teoretické k rovině praktické a ukážeme si, jak takový jednoduchý ViewModel může vypadat.
Příprava pro ViewModel
Dřive než přistoupím k samotné tvorbě ViewModelu, je třeba si říct, jaké máme předpoklady a očekávání. Tedy, pro jaké View nám bude takový ViewModel vlastně sloužit a jaký Model bude adaptovat. Při tvorbě budu vycházet z příkladu, který jsem uváděl na své přednášce.
Jedná se o poměrně jednoduchou WPF aplikaci, kde má uživatel možnost se přihlásit k odběru údajů z měřící stanice zadáním jejího jména. Informace - naměřené údaje - jsou pak zobrazovány uživateli v seznamu měření. Aby bylo vše dobře přehledné, necháme uživateli ještě zobrazit seznam všech přihlášených měřících stanic.
Zároveň máme k dispozici službu, která poskytuje poměrně jednoduchý interface. Kdy na jedné straně přijímá název přihlašované měřící stanice a na straně druhé, pokud se to tak dá říci, poskytuje data, která jsou získána z měření. Interface této služby tak může vypadat následovně:
public interface IMeasureService { /// <summary> /// Fire event when new measure is available /// </summary> event Action<Measure> MeasureArrived; /// <summary> /// Subscribe to retrieve information from station /// </summary> /// <param name="station">Station name</param> void Subscribe(string station); }
Jak můžete vidět, pomocí eventu publikujeme informace o aktuálním měření zjištěné na stanici. Třída Measure pak obsahuje pouze vlastnosti, které jsou naplněny hodnotami z měření.
Tvorba ViewModelu
Samotné vytvoření třídy ViewModelu je pak již velice snadné. Známe jaké rozhraní nám ViewModel musí poskytovat, abychom byli schopni naplnit View a zároveň máme k dispozici rozhraní služby, se kterou budeme komunikovat.
V minulém teoretickém článku jsme si řekli, že ViewModel volá model nebo služby přes jejich rozhraní, znamená to tedy, že musíme nějakým způsobem zpřístupnit službu do ViewModelu. Asi nejjednodušší způsob je ten, že tuto službu zprostředkujeme přes konstruktor a konkrétní instanci si uložíme.
Zároveň víme, že naše služba generuje událost v okamžiku, kdy se provede nové měření na některé z přihlášených stanic. V konstruktoru se tak tedy přihlásíme k odběru této události. Tím máme obsloužen jeden směr komunikace se službou.
Další nutná komunikace je směrem volání služby a její metody Subscribe. Budeme tedy potřebovat mít přístupnou metodu Subscribe tak, aby jsme se mohli přes View přihlásit k měřící stanici.
Tím se dostáváme do situace, kdy potřebujeme zpřístupnit vlastnosti pro View, abychom s ním mohli komunikovat.
Jak již z předchozího textu víme, uživatel má možnost zapsat jméno stanice, ke které se chce přihlásit a odebírat její měření. Zpřístupníme si tak jednoduchou vlastnost, která bude pomocí Bindingu napojena na textové pole a bude přebírat hodnotu z tohoto pole.
Také jsme si řekli, že budeme uživateli zobrazovat seznam měření na přihlášené stanici. K tomu pak využijeme vlastnosti, která bude typu ObservableCollection<Measure> a tato vlastnost pak bude napojena na ItemsSource vlastnost zobrazovacího prvku ListView.
Obdobně pak budeme postupovat i se seznamem měřících stanic, které si uživatel již zaregistroval k odběru. Opět se bude jednat o veřejnou vlastnost typu ObservableCollection<string>, která bude tato data uchovávat.
Nejspíše poslední věc, která nám schází provést je obsloužit příjem události o novém měření a informace o měření vložit do kolekce.
Samozřejmě nesmíme zapomenout na samotnou metodu Subscribe() na ViewModelu, která provede přečtení si hodnoty z veřejné vlastnosti ViewModelu napojené na vstupní textové pole a do nějž uživatel zadává jméno stanice. Toto jméno stanice vloží jako parametr do metody Subscribe služby a zároveň toto jméno vloží do seznamu měřících stanic, čímž dojde k aktualizaci uživatelského rozhraní.
Celý kód ViewModelu, který adaptuje Model pro potřeby View je zde:
public class MeasureViewModel { private readonly IMeasureService _service; public MeasureViewModel() { Measures = new ObservableCollection<Measure>(); Stations = new ObservableCollection<string>(); } public MeasureViewModel(IMeasureService service): this { _service = service; _service.MeasureArrived += Service_MeasureArrived; } void Service_MeasureArrived(Measure measure) { Measures.Add(measure); } public void Subscribe() { var station = Station; _service.Subscribe(station); Stations.Add(station); } public string Station { get; set; } public ObservableCollection<Measure> Measures { get; set; } public ObservableCollection<string> Stations { get; set; } }
Závěrem
Takto připravený ViewModel je nutné napojit na View a předat mu referenci na konkrétní implementaci služby. Dále bude dobré a vhodné takovýto ViewModel upravit, abychom se vyvarovali případných výjimek při komunikaci se službou. Jak toho dosáhnout si ukážeme v dalším článku.
Ahoj, chtěl bych se zeptat jak postupovat, pokud chci používat Entity Framework ve spojení s WPF? Má teorie je taková, že Model budou entity(model), které se vygenerují z databáze a pak bude třeba udělat rozhraní, které nějakým způsobem zpřístupní vlastnosti entit. Nebo se postupuje úplně jinak? Myslíš že by jsi mohl udělat nějaký jednoduchý příklad v rámci "tutoriálu"?
Přeji pěkný den. Michal
Ahoj, zkusím na toto téma připravit nějaký článek případně i s ukázkou použití a publikovat jej co nejdříve.
Díky za námět.