A felhőalapú technológiákra váltás egy izgalmas kihívás - ha tudod miként kezdj neki! Ebben a cikkben szeretném megosztani...
MVVM Light Toolkit – 1. rész
Gulyás GáborAmi egy posztnak indult, abból egy egész szép sorozatunk lett mára. Immáron negyedik bejegyzésemet írom MVVM témában, mely nagy örömömre sokak érdeklődését megragadta és hasznosnak bizonyult. Az MVVM Light Toolkit-re történő váltással most már egy kicsit a mélyebb víz felé sodródunk és az MVVM minta kicsit komolyabb szintű felhasználása a cél. De miért is lesz jó ez nekünk, miért van rá szükségünk?
Az MVVM mintát – hasonlóképp mint az összes többit – az idők során tervezték meg a szakértők, hogy egy már bevált sémát használva minél kevesebb problémába ütközzünk, ne kelljen egy már több ezerszer kijárt ösvényt nekünk is végigjárnunk. Az MVVM azonban sokkal többet tartogat számunkra annál, mint hogy szétválasztottuk a kódot részekre és nesze nekünk, mindenféle felületi elemet összeköthetünk a háttérkódban található dolgokkal. Az MVVM Light Toolkit az egyike ennek az extrának.
Mi az az MVVM Light Toolkit?
Az MVVM Light Toolkit sokkal inkább egy eszközkészlet, mint sem egy keretrendszer, lehetőség szerint ne is nevezzük annak. Célja, hogy felgyorsítsa számunkra az MVVM alkalmazások fejlesztését a WPF, Silveright, Windows Store (RT) és Windows Phone platformokon.
Amellett, hogy egyszerűbbé teszi a háttérkód elválasztását a felülettől, a bővíthetőség és karbantarthatóság elérésében is nagy segítséget nyújt a strukturáltság révén – de ehhez mi magunk is kellünk, hogy kövessük ezen elveket. A felületi réteget elvékonyodásával az automatizált tesztelés is gördülékenyebbé válik.
Mindezek mellett még a teljes Blend támogatottságot is kiélvezhetjük, így nem kell vakon tervezgetnünk a felületet egy fehér képernyő előtt, hanem azt statikus adatokkal is feltölthetjük tervezési időben.
Mielőtt továbblépnénk…
Az MVVM Light Toolkit nem egy új mintát hoz el nekünk, nem valami új SDK. Az MVVM mintában tanultak könnyebb implementálását oldja meg számunkra, ehhez pedig ismerni kell a Model-View-ViewModel minta alapjait. Ha ezeket még nem sajátítottad el, még nem ismered, érdemes előbb a sorozat előző részeit áttekinteni, kezdve a “Mi fán terem az MVVM?” című bejegyzéssel.
Ha ezzel megvagyunk, akkor neki is kezdhetünk!
Az MVVM Light Toolkit telepítése
Az MVVM Light Toolkit telepítésére két lehetőségünk van. Ezek közül az egyik, hogy azt a Visual Studio fejlesztőkörnyezetben egy bővítményként telepítjük. Ezzel új projekt sablonokat is kapni fogunk – ezt választva már alapból felépül egy könyvtárstruktúra, valamint kapni fogunk egy példakódot is.
Második lehetőségünk a projektbe történő letöltés, NuGet Package formájában. Én inkább ezt a megoldást szoktam választani, így ha valamilyen verziókezelőből letöltjük a kódot egy másik gépre, akkor a NuGet automatikusan letölti nekünk az MVVM Light Toolkit-et is, nekünk nem kell vacakolnunk semmivel.
Ha a NuGet Package Manager-ben rákeresünk az MVVM Light Toolkit-re, két opciót kapunk. Válasszuk az MvvmLightLibs opciót, ellenkezőleg újfent megkapjuk a példakódokat, sablon ViewModel-t, stb.
A kódpéldában egy Univerzális Windows 10 alkalmazást fogunk létrehozni, a kódot a GitHub-on lehet letölteni, melynek alapjául a korábbi MVVM-Binding-Simple alkalmazás fog szolgálni. Az alábbi cikkben csak a kódkiegészítések és az újdonságok lesznek bemutatva, így javaslom a teljes forrás letöltését. Ha nincs letöltve a Windows 10 SDK, akkor a kódot hasonlóképpen WPF, WinRT és a többi támogatott platformon másolás & beillesztés módszerrel ugyanilyen módon ki lehet próbálni – némi átírással persze, mint például a névterek esetében.
Érdemes megjegyezni – mint az a fenti képernyőmentésből is látszik -, hogy az MVVM Light Toolkit támogatja a Xamarin-t is, így a Xamarin Platformon írt Android és iOS alkalmazásainkban is használhatjuk!
INotifyPropertyChanged – Helyett…
Korábban már találkozhattunk az INotifyPropertyChanged interfésszel. Neki – és a vele együtt implementált eseménynek – köszönhetően tudtuk jelezni a felületünk számára, hogy változott az adattag értéke valamelyik adatkötésünkben, ezáltal ezt le tudta kezelni a felületen történő frissítéssel. A legnagyobb probléma ezzel a megoldással, hogy minden egyes osztályban meg kellett írnunk az ehhez tartozó kódsort.
[ecko_code_highlight language=”c#”]public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}[/ecko_code_highlight]
Ez rengeteg felesleges plusz kódot eredményezett és rengeteg időt is vesz el az életünkből. Erre egy roppant egyszerű válasza van az MVVM Light Toolkit-nek: ObservableObject & ViewModelBase.
Előbbit Model-ben, utóbbit pedig a ViewModel-ben használjuk, pontosabban ezekből fogjuk származtatni osztályainkat. Hogy milyen pluszt fog ez adni nekünk? Bevezet egy új, RaisePropertyChanged és RaisePropertyChanging függvényt, mellyel megspórolhatjuk az előbbi interfész implementálását és egyetlen sorral letudhatjuk a felület felé történő visszajelzést.
A függvény két módon hívható meg:
[ecko_code_highlight language=”c#”]// Megadva a változó nevét stringként
RaisePropertyChanged(“Name”);
// A változóra történő hivatkozással, Lambda kifejezéssel
RaisePropertyChanged(() => this.Name);[/ecko_code_highlight]
Mindkettő hívással ugyanaz lesz az eredmény, azonban inkább az utóbbi a javasolt. Hogy miért? A későbbiekben, ha változtatni fogjuk a változó nevét, akkor egy Refactor segítségével már ezen hívásokat is lecseréli automatán a Visual Studio, nem kell kézzel megváltoztatnunk őket.
RelayCommand – Click event? ViewModel-be vele!
Korábbi kódunkban erre volt is egy példa, mikor a gombot megnyomtuk a felületen. Ilyenkor a Click (vagy Tap) eventben megadott függvény futott le, ami a ViewModel-ben végzett el egy hívást, az a hívás pedig valamilyen munkát elvégzett a háttérben – számunkra ez már nem fontos. A probléma ezzel az, hogy amennyiben két különböző csapat dolgozik a felületen és a kódon, akkor ha változik a függvény neve a ViewModel-ben, már írhatjuk is át a felületen a Click Event kódját, vica versa.
Az MVVM esetében erre a válasz az ICommand interfész lett, melyet egy újonnan létrehozott osztály implementál, ezeket az objektumokat pedig már könnyen köthetjük egy felületi elemhez – például egy gombhoz. Az interfész implementálásakor meg kellett adnunk, hogy a parancs futtatásakor mi történjen (Execute), valamint milyen esetekben legyen futtatható a parancs (CanExecute). A CanExecute metódusnak köszönhetően azzal sem kell vesződnünk, hogy a felületi elemet kézzel tiltsuk le/engedélyezzük, mivel az ezáltal visszaadott érték alapján a felületi elem állapota is változik. A gond ezzel a megoldással az, hogy minden egyes parancshoz újabb és újabb osztályt kellene létrehoznunk, így hatalmas méretűre duzzadna a kódunk és rengetegszer írnánk le ugyanazt.
Az MVVM Light Toolkit bevezetett egy RelayCommand osztályt, mely ezt a műveletet egyszerűsíti le számunkra. Implementáljunk hát egy ilyen parancsot az előző kódpélda alapján, ahol a gomb megnyomására felülírjuk a felhasználó nevét. Ehhez létre kell hoznunk a parancsot, majd megadni a hozzá tartozó műveletet, ami a meghívásakor fut le:
[ecko_code_highlight language=”c#”]public RelayCommand<string> ChangeNameCommand { get; private set; }
private void ChangeName(string newName)
{
User.Name = newName;
}[/ecko_code_highlight]
A parancs típusa után észrevehetjük, hogy van egy Template paraméterünk is (RelayCommand<string>). Ez adja meg, hogy a parancsunk egy string típusú paramétert fog várni. Ha lehagyjuk ezt, akkor a parancsunk nem fog várni semmilyen paramétert – a paraméternek a lefuttatandó függvényben lesz majd szerepe, ott kell meghatároznunk azt.
A ViewModel konstruktorában példányosítsuk is a parancsunkat, a hozzá tartozó Execute függvénnyel együtt:
[ecko_code_highlight language=”c#”] ChangeNameCommand = new RelayCommand<string>(ChangeName);[/ecko_code_highlight]
A felületünk kódja ezután a következőképpen fog megváltozni:
[ecko_code_highlight language=”html”]<Button x:Name=”btnChangeName”
Content=”Név módosítása”
Margin=”10″ HorizontalAlignment=”Center”
Command=”{Binding ChangeNameCommand}”
CommandParameter=”{Binding ElementName=txtNewName, Path=Text}” />[/ecko_code_highlight]
Érdemes megfigyelni a felületen található gombot, ami most már nem egy Click eseményt hív meg a kattintásakor, hanem egy parancsot kötöttünk hozzá, emellett pedig a felületen lévő szövegdoboz Text adattagjának értékét adjuk át a parancs paraméterként – szintén adatkötéssel! Ezzel a módosítással a gombhoz tartozó kattintási eseményt kivehetjük a felület kódjából, egyelőre azonban más változást nem figyelhetünk meg, minden ugyanúgy működik ahogyan eddig.
A kódot azonban bővítsük ki a RelayCommand CanExecute paraméterével. Ez a paraméter azt a célt szolgálja, hogy egy értékvizsgálat alapján eldönti, az adott parancsot most lefuttathatjuk-e, vagy sem – ennek függvényében pedig engedélyezi, vagy éppen letiltja a felületi elemet. Ahhoz, hogy ez működjön, először hozzunk létre egy új bool típusú változót, ami azt adja majd meg, változtathatjuk-e most a kódot. Emellett módosítani fogunk kicsit az Execute-kor lefutó parancson is, valamint a példányosítás módján is. A kód a következő lesz:
[ecko_code_highlight language=”c#”]public User User { get; set; }
public MainViewModel()
{
User = new User() { Name = “Cortana” };
ChangeNameCommand = new RelayCommand<string>(ChangeName, val => this.AllowChangeName == true);
}
private bool _AllowChangeName = true;
public bool AllowChangeName
{
get
{
return _AllowChangeName;
}
set
{
_AllowChangeName = value;
RaisePropertyChanged(() => this.AllowChangeName);
ChangeNameCommand.RaiseCanExecuteChanged();
}
}
public RelayCommand<string> ChangeNameCommand { get; private set; }
private async void ChangeName(string newName)
{
// Letiltjuk a névváltást – Ennek megváltoztatásával a gomb is letiltásra kerül
AllowChangeName = false;
// Várunk 1 másodpercet a név megváltoztatása előtt
await Task.Delay(1000);
// Megváltoztatjuk a nevet
User.Name = newName;
// Újra engedélyezzük a névváltást
AllowChangeName = true;
}[/ecko_code_highlight]
Fontos látni, hogy az AllowChangeName változó módosításakor egy új hívást is megadtunk. Ha az adattagunk értéke alapján változik egy parancs futtathatósága, akkor újra ki kell értékelnünk az ezt vizsgáló függvényt, melyet a parancson meghívható RaiseCanExecuteChanged metódussal teszünk meg. Ha ezt nem végezzük el, úgy járunk mint az adatkötés során, amikor nem használjuk a RaisePropertyChanged hívást – nem fog megváltozni semmi. A RelayCommand példányosításakor második paraméterként, Lambda kifejezésként adtuk meg a CanExecute függvényt – de ezt ugyanúgy deklarálhatjuk egy különálló függvényként.
Ha ilyen módon futtatjuk a kódot, akkor a gombra történő kattintás után 1 másodpercig letiltásra kerül a gomb, megváltozik a név, majd újra kattinthatóvá válik.
Összegezve tehát, a RelayCommand segítségével nem csak hogy leválaszthatjuk a felületről a gombok megnyomásához hasonló események kódját, de lehetőségünk van arra is, hogy egy értékvizsgálatokat használva a felületi elemet letiltsuk és újraengedélyezzük, bármiféle trükközés, események beiktatása, vagy a felületre történő visszanyúlás nélkül.
Összegzés
A példákban bemutatott kódok és a teljes alkalmazás a következő GitHub címen érhető el: https://github.com/Bhawk90/SampleCodes/tree/master/MvvmLightToolkit.
Mivel a cikkben csak a korábbi kódhoz képest történt változásokat írtam le, javasolt a teljes alkalmazás áttekintése.
A cikk következő részében az MVVM Light Toolkit további képességeit fogom bemutatni nektek.
Addig is mit gondoltok, fogjátok használni az MVVM Light Toolkit-et a jövőben?