Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Tipp.
Ez a tartalom egy részlet az eBook, Enterprise Application Patterns Using .NET MAUI, elérhető a .NET Docs vagy egy ingyenesen letölthető PDF, hogy lehet olvasni offline.
A .NET MAUI fejlesztői felülete általában egy felhasználói felület létrehozását foglalja magában az XAML-ben, majd a felhasználói felületen működő kód mögé való hozzáadást. Összetett karbantartási problémák merülhetnek fel az alkalmazások módosításakor és méretének és hatókörének növekedésében. Ezek a problémák közé tartozik a felhasználói felület vezérlői és az üzleti logika szoros összekapcsolása, ami növeli a felhasználói felület módosításának költségeit, valamint az ilyen kód egységtesztelési nehézségeit.
Az MVVM-minta segít az alkalmazás üzleti és megjelenítési logikájának tisztán elválasztásában a felhasználói felülettől (UI). Az alkalmazáslogika és a felhasználói felület tiszta elkülönítésének fenntartása számos fejlesztési probléma megoldásában segít, és megkönnyíti az alkalmazások tesztelését, karbantartását és fejlesztését. Emellett jelentősen javíthatja a kód újrahasználati lehetőségeit, és lehetővé teszi, hogy a fejlesztők és a felhasználói felület tervezői könnyebben működjenek együtt az alkalmazás megfelelő részeinek fejlesztésekor.
Az MVVM-minta
Az MVVM-mintában három alapvető összetevő található: a modell, a nézet és a nézetmodell. Mindegyik külön célt szolgál. Az alábbi ábrán a három összetevő közötti kapcsolatok láthatók.
Az egyes összetevők felelősségi körének megértése mellett fontos tisztában lenni azzal is, hogyan működnek együtt. Magas szinten a nézet "tud" a nézetmodellről, a nézetmodell pedig "tud" a modellről, de a modell nem ismeri a nézetmodellt, és a nézetmodell nem tud a nézetről. Ezért a nézetmodell elkülöníti a nézetet a modelltől, és lehetővé teszi, hogy a modell a nézettől függetlenül fejlődjön.
Az MVVM-minta használatának előnyei a következők:
- Ha egy meglévő modell implementációja magában foglalja a meglévő üzleti logikát, annak módosítása nehéz vagy kockázatos lehet. Ebben a forgatókönyvben a nézetmodell a modellosztályok adaptereként működik, és megakadályozza, hogy jelentős módosításokat hajt végre a modellkódon.
- A fejlesztők a nézet használata nélkül is létrehozhatnak egységteszteket a nézetmodellhez és a modellhez. A nézetmodell egységtesztjei pontosan ugyanazt a funkciót tudják használni, mint amelyet a nézet használ.
- Az alkalmazás felhasználói felülete a nézetmodell és a modellkód érintése nélkül újratervezhető, feltéve, hogy a nézet teljes mértékben XAML-ben vagy C#-ban van implementálva. Ezért a nézet új verziójának a meglévő nézetmodellel kell működnie.
- A tervezők és fejlesztők a fejlesztés során egymástól függetlenül és egyidejűleg dolgozhatnak az összetevőiken. A tervezők a nézetre összpontosíthatnak, míg a fejlesztők a nézetmodellen és a modell összetevőin dolgozhatnak.
Az MVVM hatékony használatának kulcsa annak megértése, hogyan lehet az alkalmazáskódokat a megfelelő osztályokba befoglalni, és hogyan működnek együtt az osztályok. Az alábbi szakaszok az MVVM-minta egyes osztályainak feladatait ismertetik.
Nézet
A nézet feladata annak meghatározása, hogy a felhasználó milyen struktúrát, elrendezést és megjelenést lát a képernyőn. Ideális esetben minden nézet az XAML-ben van definiálva, korlátozott mögöttes kóddal, amely nem tartalmaz üzleti logikát. Bizonyos esetekben azonban a mögöttes kód tartalmazhat olyan felhasználói felületi logikát, amely az XAML-ben nehezen kifejezendő vizuális viselkedést valósít meg, például animációkat.
A .NET-alkalmazásokban MAUI a nézet általában ContentPage
-származtatott vagy ContentView
-származtatott osztály. A nézeteket azonban egy adatsablon is képviselheti, amely meghatározza azokat a felhasználói felületi elemeket, amelyek vizuálisan ábrázolják az objektumot a megjelenítéskor. Az adatsablonok nézetként nem rendelkeznek kóddal, és egy adott nézetmodell-típushoz való kötésre szolgálnak.
Tipp.
Ne engedélyezze és tiltsa le a felhasználói felület elemeit a mögöttes kódban.
Győződjön meg arról, hogy a nézetmodellek felelősek a nézet bizonyos aspektusait érintő logikai állapotváltozások meghatározásáért, például hogy elérhető-e parancs, vagy hogy egy művelet függőben van-e. Ezért engedélyezze és tiltsa le a felhasználói felület elemeit kötéssel a modelltulajdonságok megtekintéséhez ahelyett, hogy engedélyezi és letiltja őket a kód mögötti kódban.
A nézetmodellen több lehetőség is van a kódot a nézet interakcióira válaszul, például gombkattintással vagy elemkijelöléssel. Ha egy vezérlő támogatja a parancsokat, a vezérlő Command tulajdonsága adathoz kötött lehet a nézetmodell ICommand tulajdonságához. A vezérlő parancsának meghívásakor a rendszer végrehajtja a nézetmodellben lévő kódot. A parancsokon kívül viselkedések csatolhatók egy objektumhoz a nézetben, és meghallgathatják a meghívandó parancsokat vagy az eseményt. Válaszul a viselkedés ezután meghívhat egy ICommand-et a nézetmodellen vagy egy metódust a nézetmodellen.
Nézetmodell
A nézetmodell olyan tulajdonságokat és parancsokat implementál, amelyekhez a nézet adatkötést végezhet, és a változásértesítési eseményeken keresztül értesíti az állapotváltozások nézetét. A nézetmodell által biztosított tulajdonságok és parancsok határozzák meg a felhasználói felület által kínált funkciókat, de a nézet határozza meg a funkció megjelenítésének módját.
Tipp.
A felhasználói felület aszinkron műveletekkel rugalmasan kezelhető.
A többplatformos alkalmazásoknak blokkolva kell tartaniuk a felhasználói felületi szálat, hogy javuljon a felhasználó teljesítményfelfogása. Ezért a nézetmodellben aszinkron metódusokat használjon az I/O-műveletekhez, és eseményeket állítsunk elő, hogy aszinkron módon értesítsük a nézeteket a tulajdonságváltozásokról.
A nézetmodell felelős a nézet és a szükséges modellosztályok közötti interakciók összehangolásáért is. Általában egy-a-többhöz kapcsolat van a nézetmodell és a modellosztályok között. A nézetmodell dönthet úgy, hogy közvetlenül a nézet számára teszi elérhetővé a modellosztályokat, hogy a nézetben lévő vezérlők közvetlenül hozzájuk kössenek adatokat. Ebben az esetben a modellosztályokat úgy kell megtervezni, hogy támogassák az adatkötést és az értesítési események módosítását.
Minden nézetmodell olyan formában biztosít adatokat egy modellből, amelyet a nézet könnyen felhasználhat. Ennek érdekében a nézetmodell néha adatkonvertálást hajt végre. Az adatkonvertálás elhelyezése a nézetmodellben jó ötlet, mert olyan tulajdonságokat biztosít, amelyekhez a nézet kapcsolódhat. Előfordulhat például, hogy a nézetmodell két tulajdonság értékeit kombinálja, hogy megkönnyítse a nézet általi megjelenítést.
Tipp.
Adatkonvertálás központosítása konverziós rétegben.
A konvertereket külön adatkonvertálási rétegként is használhatja, amely a nézetmodell és a nézet között helyezkedik el. Erre például akkor lehet szükség, ha az adatok speciális formázást igényelnek, amelyet a nézetmodell nem biztosít.
Ahhoz, hogy a nézetmodell kétirányú adatkötésben vegyen részt a nézettel, a tulajdonságainak növelnie kell az eseményt PropertyChanged
. A modellek megtekintése megfelel ennek a követelménynek az INotifyPropertyChanged
interfész implementálásával, és az PropertyChanged
esemény növelése egy tulajdonság módosításakor.
Gyűjtemények esetén a nézetbarát ObservableCollection<T>
lehetőség biztosított. Ez a gyűjtemény a gyűjtemény módosított értesítését valósítja meg, így a fejlesztőnek nem kell implementálnia a felületet a INotifyCollectionChanged
gyűjteményeken.
Modell
A modellosztályok nem vizuális osztályok, amelyek beágyazják az alkalmazás adatait. Ezért a modell az alkalmazás tartománymodelljét jelképezheti, amely általában az üzleti és érvényesítési logika mellett egy adatmodellt is tartalmaz. A modellobjektumok közé tartoznak például az adatátviteli objektumok (DTO-k), az egyszerű régi CLR-objektumok (POCO-k), valamint a létrehozott entitás- és proxyobjektumok.
A modellosztályokat általában olyan szolgáltatásokkal vagy adattárakkal együtt használják, amelyek adathozzáférést és gyorsítótárazást foglalnak magában.
Nézetmodellek csatlakoztatása nézetekhez
A nézetmodellek a .NET MAUIadatkötési képességeinek használatával csatlakoztathatók a nézetekhez. Számos megközelítés használható nézetek létrehozására és modellek megtekintésére és futtatókörnyezetben való társítására. Ezek a megközelítések két kategóriába sorolhatók, az úgynevezett első nézetösszeállításra, és a modell első összetételének megtekintésére. A nézet első összetétele és a modell első összetétele közötti választás a preferencia és az összetettség kérdése. Minden megközelítésnek ugyanaz a célja, vagyis a nézetnek a BindingContext tulajdonságához hozzárendelt nézetmodellnek kell rendelkeznie.
A nézet első összetételével az alkalmazás elméletileg olyan nézetekből áll, amelyek az általuk használt nézetmodellhez csatlakoznak. Ennek a megközelítésnek az az elsődleges előnye, hogy megkönnyíti a lazán összekapcsolt, egységben tesztelhető alkalmazások összeállítását, mivel a nézetmodellek nem függenek maguktól a nézetektől. Az alkalmazás struktúráját is könnyen megértheti a vizualizáció szerkezetének követésével, ahelyett, hogy nyomon kellene követnie a kódvégrehajtást az osztályok létrehozásának és társításának megértéséhez. Emellett a nézet első felépítése igazodik a Microsoft Maui navigációs rendszeréhez, amely a navigáció során lapok létrehozásáért felelős, így a nézetmodell első összeállítása összetettebbé válik, és nem felel meg a platformnak.
A nézetmodell első összetételével az alkalmazás elméletileg nézetmodellekből áll, és egy szolgáltatás felelős a nézetmodell nézetének helyéért. A modell első összeállítása természetesebbnek tűnik egyes fejlesztők számára, mivel a nézetlétrehozás absztrakciós jellegű, így az alkalmazás logikai nem felhasználói felületi struktúrájára összpontosíthatnak. Emellett lehetővé teszi, hogy a nézetmodelleket más nézetmodellek is létrehozva legyenek. Ez a megközelítés azonban gyakran összetett, és megnehezítheti az alkalmazás különböző részeinek létrehozásának és társításának megértését.
Tipp.
A nézetmodellek és nézetek függetlenek maradnak.
A nézetek adatforrásbeli tulajdonsághoz való kötésének a nézet fő függőségének kell lennie a megfelelő nézetmodellhez. A nézetmodellekből ne hivatkozzon a nézettípusokra, például a Buttonra és a ListView-ra. Az itt ismertetett alapelvek követésével a modellek külön vizsgálhatók, így a hatókör korlátozásával csökkenthető a szoftverhibák valószínűsége.
A következő szakaszok a nézetmodellek nézetekhez való csatlakoztatásának fő megközelítéseit ismertetik.
Nézetmodell deklaratív létrehozása
A legegyszerűbb módszer, hogy a nézet deklaratív módon példányosíthassa a megfelelő nézetmodellt az XAML-ben. A nézet létrehozásakor a megfelelő nézetmodell-objektum is létre lesz hozva. Ezt a megközelítést a következő kód példában szemlélteti:
<ContentPage xmlns:local="clr-namespace:eShop">
<ContentPage.BindingContext>
<local:LoginViewModel />
</ContentPage.BindingContext>
<!-- Omitted for brevity... -->
</ContentPage>
A létrehozáskor a ContentPage
rendszer automatikusan létrehozza és LoginViewModel
beállítja a nézet BindingContext
egy példányát.
A nézetmodell deklaratív felépítése és hozzárendelése a nézethez azzal az előnnyel jár, hogy egyszerű, de hátránya, hogy alapértelmezett (paraméter nélküli) konstruktort igényel a nézetmodellben.
Nézetmodell programozott létrehozása
Egy nézet kóddal rendelkezhet a kód mögötti fájlban, így a nézetmodell hozzá lesz rendelve a tulajdonságához BindingContext
. Ez gyakran a nézet konstruktorában történik, ahogy az alábbi kód példában is látható:
public LoginView()
{
InitializeComponent();
BindingContext = new LoginViewModel(navigationService);
}
A nézetmodell programozott felépítése és hozzárendelése a nézet mögötti kód mögött azzal az előnnyel jár, hogy egyszerű. Ennek a megközelítésnek a fő hátránya azonban az, hogy a nézetnek minden szükséges függőséget meg kell adnia a nézetmodellnek. A függőségi injektálási tároló használata segíthet fenntartani a nézet és a nézetmodell közötti laza kapcsolatot. További információ: Függőséginjektálás.
Nézetek frissítése az alapul szolgáló nézetmodell vagy modell változásaira válaszul
A nézethez elérhető összes nézetmodellnek és modellosztálynak implementálnia kell a INotifyPropertyChanged felületet. Ennek az interfésznek a nézetmodellben vagy modellosztályban való implementálása lehetővé teszi, hogy az osztály változásértesítéseket biztosítson a nézet adathoz kötött vezérlőinek, amikor az alapul szolgáló tulajdonság értéke megváltozik.
Az alkalmazásokat a tulajdonságváltozásról szóló értesítés megfelelő használatára kell kikövetelni az alábbi követelmények teljesítésével:
- Mindig emeljen fel egy eseményt
PropertyChanged
, ha egy nyilvános tulajdonság értéke megváltozik. Ne feltételezze, hogy azPropertyChanged
esemény emelése figyelmen kívül hagyható az XAML-kötés működésének ismerete miatt. - Mindig eseményt
PropertyChanged
hoz létre minden olyan számított tulajdonsághoz, amelynek értékeit a nézetmodellben vagy modellben más tulajdonságok használják. - Mindig emelje fel az
PropertyChanged
eseményt a tulajdonságmódosítást okozó metódus végén, vagy amikor az objektum biztonságos állapotban van. Az esemény emelése az esemény kezelőinek szinkron meghívásával megszakítja a műveletet. Ha ez egy művelet közepén történik, előfordulhat, hogy az objektumot visszahívási függvények számára teszi elérhetővé, ha nem biztonságos, részben frissített állapotban van. Emellett előfordulhat, hogy a kaszkádolt módosításokat események aktiváljákPropertyChanged
. A kaszkádolt módosítások általában megkövetelik a frissítések befejezését, mielőtt a kaszkádolt módosítás biztonságosan végrehajtható lenne. - Soha ne emeljen eseményt
PropertyChanged
, ha a tulajdonság nem változik. Ez azt jelenti, hogy az esemény növelése előtt össze kell hasonlítania a régi és azPropertyChanged
új értékeket. - Soha ne emelje fel az
PropertyChanged
eseményt a nézetmodell konstruktorában, ha egy tulajdonságot inicializál. A nézetben az adathoz kötött vezérlők jelenleg nem fognak előfizetni a változásértesítések fogadására. - Soha ne emeljen fel egynél több eseményt
PropertyChanged
ugyanazzal a tulajdonságnévargumentummal egy osztály nyilvános metódusának egyetlen szinkron meghívásán belül. Ha például egyNumberOfItems
olyan tulajdonság, amelynek háttértárolója a_numberOfItems
mező, ha egy metódus ötvenszeresére nő_numberOfItems
egy ciklus végrehajtása során, akkor csak egyszer, az összes munka befejezése után kell tulajdonságmódosítási értesítést kapnia a tulajdonságrólNumberOfItems
. Aszinkron metódusok esetén az aszinkron folytatási lánc minden szinkron szegmensében adja megPropertyChanged
egy adott tulajdonságnév eseményét.
Ennek a funkciónak egy egyszerű módja az osztály bővítményének BindableObject
létrehozása. Ebben a példában az ExtendedBindableObject
osztály változásértesítéseket biztosít, amelyek az alábbi kód példában láthatók:
public abstract class ExtendedBindableObject : BindableObject
{
public void RaisePropertyChanged<T>(Expression<Func<T>> property)
{
var name = GetMemberInfo(property).Name;
OnPropertyChanged(name);
}
private MemberInfo GetMemberInfo(Expression expression)
{
// Omitted for brevity ...
}
}
A .NET MAUIosztálya BindableObject
implementálja az INotifyPropertyChanged
interfészt, és egy metódust OnPropertyChanged
biztosít. Az ExtendedBindableObject
osztály megadja a RaisePropertyChanged
tulajdonságváltozásról szóló értesítés meghívásának módját, és ennek során az osztály által BindableObject
biztosított funkciókat használja.
A modellosztályok megtekintése ezután az ExtendedBindableObject
osztályból származhat. Ezért minden nézetmodell-osztály az RaisePropertyChanged
osztály metódusát használja a ExtendedBindableObject
tulajdonságváltozásról szóló értesítés megadásához. Az alábbi példakód bemutatja, hogyan hívja meg az eShop többplatformos alkalmazás a tulajdonságmódosítási értesítést lambda kifejezéssel:
public bool IsLogin
{
get => _isLogin;
set
{
_isLogin = value;
RaisePropertyChanged(() => IsLogin);
}
}
A lambda kifejezés ily módon történő használata kisebb teljesítményköltséget jelent, mivel a lambda kifejezést minden híváshoz ki kell értékelni. Bár a teljesítményköltségek kicsik, és általában nem befolyásolják az alkalmazásokat, a költségek akkor merülhetnek fel, ha sok változásértesítés van. Ennek a megközelítésnek azonban az az előnye, hogy fordítási idő típusú biztonságot és újrabontási támogatást nyújt a tulajdonságok átnevezésekor.
MVVM-keretrendszerek
Az MVVM-minta jól bevált a .NET-ben, és a közösség számos keretrendszert hozott létre, amelyek megkönnyítik ezt a fejlesztést. Mindegyik keretrendszer különböző funkciókkal rendelkezik, de szabványos, hogy egy közös nézetmodellt biztosítsanak az INotifyPropertyChanged
interfész implementálásával. Az MVVM-keretrendszerek további funkciói közé tartoznak az egyéni parancsok, a navigációs segédek, a függőséginjektálás/szolgáltatáskereső összetevők és a felhasználói felületi platform integrációja. Bár nem szükséges ezeket a keretrendszereket használni, felgyorsíthatják és egységesíthetik a fejlesztést. Az eShop többplatformos alkalmazás a .NET Community MVVM Toolkitet használja. A keretrendszer kiválasztásakor figyelembe kell vennie az alkalmazás igényeit és a csapat erősségeit. Az alábbi lista tartalmazza a .NET-hez MAUIkészült MVVM-keretrendszerek némelyikét.
Felhasználói felületi interakció parancsokkal és viselkedésekkel
A többplatformos alkalmazásokban a műveletek általában egy felhasználói műveletre( például egy gombkattintásra) reagálva lesznek meghívva, amely egy eseménykezelő létrehozásával valósítható meg a kód mögötti fájlban. Az MVVM-mintában azonban a művelet végrehajtásának felelőssége a nézetmodellé, és a kód mögötti kód elhelyezését el kell kerülni.
A parancsok kényelmes módot biztosítanak a felhasználói felületen lévő vezérlőkhöz kötött műveletek ábrázolására. Belefoglalják a műveletet megvalósító kódot, és segítenek abban, hogy a nézetben el legyen választva a vizualizációtól. Így a nézetmodellek hordozhatóbbak lesznek az új platformokon, mivel nem függenek közvetlenül a platform felhasználói felületi keretrendszere által biztosított eseményekhez. A .NET MAUI olyan vezérlőket tartalmaz, amelyek deklaratív módon csatlakoztathatók egy parancshoz, és ezek a vezérlők meghívják a parancsot, amikor a felhasználó interakcióba lép a vezérlővel.
A viselkedések azt is lehetővé teszik, hogy a vezérlők deklaratív módon csatlakozhassanak egy parancshoz. A viselkedések azonban olyan műveletek meghívására használhatók, amelyek egy vezérlő által létrehozott eseménytartományhoz kapcsolódnak. Ezért a viselkedések számos olyan forgatókönyvet kezelnek, mint a parancsokkal kompatibilis vezérlők, ugyanakkor nagyobb fokú rugalmasságot és vezérlést biztosítanak. Emellett a viselkedésekkel parancsobjektumokat vagy metódusokat is társíthat olyan vezérlőkhöz, amelyeket nem kifejezetten parancsok kezelésére terveztek.
Parancsok implementálása
A modellek megtekintése általában nyilvános tulajdonságokat tesz elérhetővé a nézetből való kötéshez, amely megvalósítja az interfészt ICommand
. Számos .NET-vezérlő MAUI és kézmozdulat biztosít egy tulajdonságot Command
, amely a nézetmodell által biztosított objektumhoz ICommand
kötött adat lehet. A gombvezérlő az egyik leggyakrabban használt vezérlő, amely egy parancstulajdonságot biztosít, amely a gombra kattintáskor fut.
Feljegyzés
Bár a nézetmodell által használt felület tényleges implementációját ICommand
is közzéteheti (például Command<T>
RelayCommand
), a parancsokat nyilvánosan is közzéteheti.ICommand
Így, ha később módosítania kell a megvalósítást, könnyen felcserélhető.
Az ICommand
interfész meghatároz egy metódust Execute
, amely magában foglalja a műveletet, egy metódust CanExecute
, amely azt jelzi, hogy a parancs meghívható-e, és egy CanExecuteChanged
olyan eseményt, amely olyan változások esetén következik be, amelyek befolyásolják, hogy a parancsot végre kell-e hajtani. A legtöbb esetben csak a parancsok metódusát Execute
fogjuk megadni. A részletes áttekintésért ICommand
tekintse meg a dokumentációját.
A .NET-ben MAUI az Command
illesztőt megvalósító Command<T>
osztályok és ICommand
osztályok találhatók, ahol T
az argumentumok Execute
típusa és CanExecute
a .
Command
alapvető Command<T>
implementációk, amelyek a felülethez szükséges minimális funkcionalitást ICommand
biztosítják.
Feljegyzés
Számos MVVM-keretrendszer több funkciógazdag implementációt kínál a ICommand
felülethez.
A Command
vagy Command<T>
konstruktorhoz szükség van egy műveleti visszahívási objektumra, amelyet a ICommand.Execute
metódus meghívásakor hívunk meg. A CanExecute
metódus egy opcionális konstruktorparaméter, és egy Func, amely egy bool értéket ad vissza.
Az eShop többplatformos alkalmazás a RelayCommand és az AsyncRelayCommand alkalmazást használja. A modern alkalmazások elsődleges előnye, hogy jobb funkciókat biztosítanak az AsyncRelayCommand
aszinkron műveletekhez.
Az alábbi kód bemutatja, hogyan jön létre egy Command
regisztrációs parancsot képviselő példány a Register nézetmodell metódusának delegáltjának megadásával:
public ICommand RegisterCommand { get; }
A parancs egy olyan tulajdonságon keresztül jelenik meg a nézet számára, amely egy .ICommand
Amikor a Execute
metódust meghívják az Command
objektumon, egyszerűen továbbítja a hívást a nézetmodell metódusának a Command
konstruktorban megadott meghatalmazotton keresztül. Az aszinkron metódusok az aszinkron paranccsal hívhatók meg, és a parancs delegáltjának Execute
megadásakor kulcsszavakra várnak. Ez azt jelzi, hogy a visszahívás egy Task
, és várni kell. Az alábbi kód például bemutatja, hogyan jön létre egy ICommand
bejelentkezési parancsot jelképező példány a nézetmodell metódusának SignInAsync
delegáltjának megadásával:
public ICommand SignInCommand { get; }
...
SignInCommand = new AsyncRelayCommand(async () => await SignInAsync());
A paraméterek átadhatók a Execute
CanExecute
parancs példányosításához az AsyncRelayCommand<T>
osztály használatával a műveleteknek és a műveleteknek. A következő kód például azt mutatja be, hogy a AsyncRelayCommand<T>
példányok hogyan jelzik, hogy a NavigateAsync
metódushoz sztring típusú argumentum szükséges:
public ICommand NavigateCommand { get; }
...
NavigateCommand = new AsyncRelayCommand<string>(NavigateAsync);
Az egyes konstruktorokban és RelayCommand
RelayCommand<T>
osztályokban a metódus delegálása CanExecute
nem kötelező. Ha nincs megadva meghatalmazott, akkor a Command
függvény igaz CanExecute
értéket ad vissza. A nézetmodell azonban az objektum metódusának meghívásával CanExecute
jelezheti a parancs állapotának ChangeCanExecute
változásátCommand
. Ez okozza az CanExecuteChanged
eseményt. A parancshoz kötött felhasználói felületi vezérlők ezután frissítik az engedélyezett állapotukat, hogy tükrözzék az adathoz kötött parancs rendelkezésre állását.
Parancsok meghívása nézetből
Az alábbi példakód azt mutatja be, hogyan köti egy Grid
adott osztály egy példányával az LoginView
RegisterCommand
osztály egy adott példányátLoginViewModel
:TapGestureRecognizer
<Grid Grid.Column="1" HorizontalOptions="Center">
<Label Text="REGISTER" TextColor="Gray"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
</Grid>
A parancsparaméter opcionálisan definiálható a CommandParameter
tulajdonság használatával is. A várt argumentum típusa a célmetelyekben Execute
CanExecute
van megadva. A TapGestureRecognizer
rendszer automatikusan meghívja a célparancsot, amikor a felhasználó interakcióba lép a csatlakoztatott vezérlővel. Ha CommandParameter
meg van adva, a rendszer argumentumként átadja a parancs végrehajtási meghatalmazottjának.
Viselkedések implementálása
A viselkedések lehetővé teszik a funkciók hozzáadását a felhasználói felület vezérlőihez anélkül, hogy alosztályba kellene őket sorolniuk. Ehelyett a funkció egy viselkedési osztályban van implementálva, és úgy csatlakozik a vezérlőhöz, mintha maga a vezérlő része lenne. A viselkedések lehetővé teszik olyan kód implementálását, amelyet általában kód mögött kell írnia, mivel az közvetlenül kommunikál a vezérlő API-jával úgy, hogy tömören csatolható legyen a vezérlőhöz, és több nézetben vagy alkalmazásban újra felhasználható legyen. Az MVVM kontextusában a viselkedés hasznos módszer a vezérlők parancsokhoz való csatlakoztatásához.
A vezérlőelemhez csatolt tulajdonságokon keresztül csatolt viselkedést csatolt viselkedésnek nevezzük. A viselkedés ezután annak az elemnek a közzétett API-ját használhatja, amelyhez hozzá van kapcsolva, hogy funkciókat adjon hozzá a vezérlőhöz vagy más vezérlőkhöz a nézet vizualizációs fájában.
A .NET-viselkedés MAUI olyan osztály, amely az vagy Behavior
az Behavior<T>
osztályból származik, ahol a T annak a vezérlőnek a típusa, amelyre a viselkedésnek vonatkoznia kell. Ezek az osztályok biztosítanak OnAttachedTo
és OnDetachingFrom
metódusokat, amelyeket felül kell bírálni, hogy olyan logikát biztosítson, amely akkor lesz végrehajtva, amikor a viselkedést a vezérlőkhöz csatolják, és leválasztják a vezérlőkről.
Az eShop többplatformos alkalmazásban az BindableBehavior<T>
osztály az Behavior<T>
osztályból származik. Az osztály célja BindableBehavior<T>
egy alaposztály biztosítása olyan .NET-viselkedésekhez MAUI , amelyekhez a BindingContext
viselkedést be kell állítani a csatlakoztatott vezérlőre.
Az BindableBehavior<T>
osztály egy felülbírírozható OnAttachedTo
metódust biztosít, amely beállítja a BindingContext
viselkedést, és egy felülírható OnDetachingFrom
metódust, amely megtisztítja a BindingContext
.
Az eShop többplatformos alkalmazás tartalmaz egy EventToCommandBehavior osztályt, amelyet a MAUI közösségi eszközkészlet biztosít.
EventToCommandBehavior
parancsot hajt végre egy eseményre válaszul. Ez az osztály az BaseBehavior<View>
osztályból származik, így a viselkedés a viselkedés felhasználásakor egy tulajdonság által ICommand
megadotthoz kötődhet és végrehajthatóCommand
. Az alábbi példakód az osztályt EventToCommandBehavior
mutatja be:
/// <summary>
/// The <see cref="EventToCommandBehavior"/> is a behavior that allows the user to invoke a <see cref="ICommand"/> through an event. It is designed to associate Commands to events exposed by controls that were not designed to support Commands. It allows you to map any arbitrary event on a control to a Command.
/// </summary>
public class EventToCommandBehavior : BaseBehavior<VisualElement>
{
// Omitted for brevity...
/// <inheritdoc/>
protected override void OnAttachedTo(VisualElement bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent();
}
/// <inheritdoc/>
protected override void OnDetachingFrom(VisualElement bindable)
{
UnregisterEvent();
base.OnDetachingFrom(bindable);
}
static void OnEventNamePropertyChanged(BindableObject bindable, object oldValue, object newValue)
=> ((EventToCommandBehavior)bindable).RegisterEvent();
void RegisterEvent()
{
UnregisterEvent();
var eventName = EventName;
if (View is null || string.IsNullOrWhiteSpace(eventName))
{
return;
}
eventInfo = View.GetType()?.GetRuntimeEvent(eventName) ??
throw new ArgumentException($"{nameof(EventToCommandBehavior)}: Couldn't resolve the event.", nameof(EventName));
ArgumentNullException.ThrowIfNull(eventInfo.EventHandlerType);
ArgumentNullException.ThrowIfNull(eventHandlerMethodInfo);
eventHandler = eventHandlerMethodInfo.CreateDelegate(eventInfo.EventHandlerType, this) ??
throw new ArgumentException($"{nameof(EventToCommandBehavior)}: Couldn't create event handler.", nameof(EventName));
eventInfo.AddEventHandler(View, eventHandler);
}
void UnregisterEvent()
{
if (eventInfo is not null && eventHandler is not null)
{
eventInfo.RemoveEventHandler(View, eventHandler);
}
eventInfo = null;
eventHandler = null;
}
/// <summary>
/// Virtual method that executes when a Command is invoked
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
protected virtual void OnTriggerHandled(object? sender = null, object? eventArgs = null)
{
var parameter = CommandParameter
?? EventArgsConverter?.Convert(eventArgs, typeof(object), null, null);
var command = Command;
if (command?.CanExecute(parameter) ?? false)
{
command.Execute(parameter);
}
}
}
A OnAttachedTo
metódusokkal regisztrálhat és OnDetachingFrom
törölhet egy eseménykezelőt a EventName
tulajdonságban meghatározott eseményhez. Ezután, amikor az esemény aktiválódik, a rendszer meghívja a OnTriggerHandled
metódust, amely végrehajtja a parancsot.
A parancsok eseménytüzek esetén történő végrehajtásának előnye EventToCommandBehavior
, hogy a parancsok olyan vezérlőkkel társíthatók, amelyek nem a parancsokkal való interakcióra lettek tervezve. Emellett áthelyezi az eseménykezelési kódot a modellek megtekintéséhez, ahol egységtesztelhető.
Viselkedések meghívása nézetből
Ez EventToCommandBehavior
különösen akkor hasznos, ha parancsokat csatol egy olyan vezérlőhöz, amely nem támogatja a parancsokat. A LoginView például az EventToCommandBehavior
alábbi kódban látható módon hajtja végre azt ValidateCommand
, amikor a felhasználó módosítja a jelszó értékét:
<Entry
IsPassword="True"
Text="{Binding Password.Value, Mode=TwoWay}">
<!-- Omitted for brevity... -->
<Entry.Behaviors>
<mct:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateCommand}" />
</Entry.Behaviors>
<!-- Omitted for brevity... -->
</Entry>
Futásidőben a rendszer válaszol a EventToCommandBehavior
Entry
. Amikor egy felhasználó betér a Entry
mezőbe, az TextChanged
esemény aktiválódik, amely végrehajtja a ValidateCommand
következőt: LoginViewModel
. Alapértelmezés szerint az esemény eseményargumentumait a rendszer átadja a parancsnak. Szükség esetén a EventArgsConverter
tulajdonság segítségével az EventArgs
esemény által biztosított értéket olyan értékké alakíthatja, amelyet a parancs bemenetként vár el.
A viselkedésekről további információt a .NET fejlesztői központban található ViselkedésekMAUI.
Összegzés
A Model-View-ViewModel (MVVM) minta segít az alkalmazás üzleti és megjelenítési logikájának tisztán elválasztásában a felhasználói felülettől (UI). Az alkalmazáslogika és a felhasználói felület tiszta elkülönítésének fenntartása számos fejlesztési probléma megoldásában segít, és megkönnyíti az alkalmazások tesztelését, karbantartását és fejlesztését. Emellett jelentősen javíthatja a kód újrahasználati lehetőségeit, és lehetővé teszi, hogy a fejlesztők és a felhasználói felület tervezői könnyebben működjenek együtt az alkalmazás megfelelő részeinek fejlesztésekor.
Az MVVM-mintát használva az alkalmazás felhasználói felülete, valamint a mögöttes bemutató és üzleti logika három külön osztályba van osztva: a nézet, amely magában foglalja a felhasználói felületet és a felhasználói felület logikáját; a megjelenítési logikát és állapotot magában foglaló nézetmodell; és a modellt, amely az alkalmazás üzleti logikáját és adatait foglalja magában.