Model-View-ViewModel (MVVM)
Tip
Tento obsah je výňatek z elektronické knihy, vzory podnikových aplikací pomocí .NET MAUI, dostupné na .NET Docs nebo jako zdarma ke stažení PDF, které lze číst offline.
Vývojářské prostředí .NET MAUI obvykle zahrnuje vytvoření uživatelského rozhraní v jazyce XAML a následné přidání kódu, který funguje v uživatelském rozhraní. Složité problémy s údržbou můžou nastat, když se aplikace upraví a zvětšují jejich velikost a rozsah. Mezi tyto problémy patří úzká párování mezi ovládacími prvky uživatelského rozhraní a obchodní logikou, což zvyšuje náklady na úpravy uživatelského rozhraní a potíže s testováním jednotek takového kódu.
Model MVVM pomáhá vyčistit obchodní a prezentační logiku aplikace od uživatelského rozhraní. Udržování čistého oddělení mezi logikou aplikace a uživatelským rozhraním pomáhá řešit řadu problémů s vývojem a usnadňuje testování, údržbu a vývoj aplikací. Může také výrazně zlepšit příležitosti opětovného použití kódu a umožní vývojářům a návrhářům uživatelského rozhraní snadněji spolupracovat při vývoji příslušných částí aplikace.
Model MVVM
Model MVVM obsahuje tři základní komponenty: model, zobrazení a model zobrazení. Každý z nich slouží k odlišnému účelu. Následující diagram znázorňuje vztahy mezi třemi komponentami.
Kromě porozumění zodpovědnostem jednotlivých komponent je také důležité pochopit, jak komunikují. Na vysoké úrovni zobrazení "zná" model zobrazení a model zobrazení "ví o" modelu, ale model zobrazení nezná model zobrazení a model zobrazení toto zobrazení nezná. Model zobrazení proto izoluje zobrazení od modelu a umožňuje, aby se model vyvinul nezávisle na zobrazení.
Výhody použití modelu MVVM jsou následující:
- Pokud existující implementace modelu zapouzdřuje existující obchodní logiku, může být obtížné nebo rizikové ji změnit. V tomto scénáři model zobrazení funguje jako adaptér pro třídy modelu a brání v provádění velkých změn v kódu modelu.
- Vývojáři můžou vytvářet testy jednotek pro model zobrazení a model bez použití zobrazení. Testy jednotek pro model zobrazení můžou provádět přesně stejné funkce jako zobrazení.
- Uživatelské rozhraní aplikace je možné přepracovat bez zásahu do modelu zobrazení a kódu modelu za předpokladu, že je zobrazení implementováno zcela v JAZYCE XAML nebo C#. Proto by nová verze zobrazení měla fungovat s existujícím modelem zobrazení.
- Návrháři a vývojáři můžou během vývoje pracovat nezávisle a souběžně na svých komponentách. Návrháři se můžou zaměřit na zobrazení, zatímco vývojáři můžou pracovat na modelu zobrazení a komponentách modelu.
Klíčem k efektivnímu používání virtuálního počítače MVVM je pochopení, jak začlenit kód aplikace do správných tříd a jak třídy interagují. Následující části diskutují o zodpovědnostech jednotlivých tříd v modelu MVVM.
Zobrazení
Zobrazení zodpovídá za definování struktury, rozložení a vzhledu toho, co uživatel vidí na obrazovce. V ideálním případě je každé zobrazení definováno v XAML s omezeným kódem, který neobsahuje obchodní logiku. V některých případech ale kód na pozadí může obsahovat logiku uživatelského rozhraní, která implementuje vizuální chování, které je obtížné vyjádřit v JAZYCE XAML, například animace.
V aplikaci .NET MAUI je zobrazení obvykle odvozenou nebo ContentView
odvozenou ContentPage
třídou. Zobrazení ale mohou být reprezentována také šablonou dat, která určuje prvky uživatelského rozhraní, které se mají použít k vizuálnímu znázornění objektu při zobrazení. Šablona dat jako zobrazení neobsahuje žádný kód a je navržená tak, aby se svážela s konkrétním typem modelu zobrazení.
Tip
Vyhněte se povolení a zakázání prvků uživatelského rozhraní v kódu.
Ujistěte se, že modely zobrazení odpovídají za definování změn logického stavu, které ovlivňují některé aspekty zobrazení, například jestli je příkaz k dispozici, nebo indikaci, že operace čeká na vyřízení. Proto povolte a zakažte prvky uživatelského rozhraní vazbou pro zobrazení vlastností modelu, a ne jejich povolení a zakázání v kódu.
Existuje několik možností spuštění kódu v modelu zobrazení v reakci na interakce v zobrazení, například kliknutí na tlačítko nebo výběr položky. Pokud ovládací prvek podporuje příkazy, vlastnost Command ovládacího prvku může být vázána na vlastnost ICommand v modelu zobrazení. Po vyvolání příkazu ovládacího prvku se spustí kód v modelu zobrazení. Kromě příkazů je možné chování připojit k objektu v zobrazení a poslouchat buď příkaz, který má být vyvolán, nebo událost, která se má vyvolat. V reakci může chování vyvolat ICommand v modelu zobrazení nebo metodu v modelu zobrazení.
ViewModel
Model zobrazení implementuje vlastnosti a příkazy, ke kterým může zobrazení vytvořit vazbu dat, a upozorní zobrazení změn stavu prostřednictvím událostí oznámení o změnách. Vlastnosti a příkazy, které model zobrazení poskytuje, definují funkce, které má uživatelské rozhraní nabídnout, ale zobrazení určuje, jak se má tato funkce zobrazit.
Tip
Udržujte uživatelské rozhraní responzivní pomocí asynchronních operací.
Aplikace pro více platforem by měly vést k odblokování vlákna uživatelského rozhraní, aby se zlepšilo vnímání výkonu uživatele. Proto v modelu zobrazení použijte asynchronní metody pro vstupně-výstupní operace a vyvolání událostí, které asynchronně upozorňují zobrazení změn vlastností.
Model zobrazení je také zodpovědný za koordinaci interakcí zobrazení se všemi třídami modelu, které jsou požadovány. Mezi modelem zobrazení a třídami modelu modelu je obvykle relace 1:N. Model zobrazení se může rozhodnout vystavit třídy modelu přímo do zobrazení, aby ovládací prvky v zobrazení mohly data svázat přímo s nimi. V tomto případě budou třídy modelu muset být navrženy tak, aby podporovaly datové vazby a změny událostí oznámení.
Každý model zobrazení poskytuje data z modelu ve formuláři, který může zobrazení snadno využívat. K tomu model zobrazení někdy provádí převod dat. Umístění tohoto převodu dat do modelu zobrazení je vhodné, protože poskytuje vlastnosti, se kterými může zobrazení vytvořit vazbu. Model zobrazení může například zkombinovat hodnoty dvou vlastností, aby bylo snazší zobrazení zobrazit.
Tip
Centralizace převodů dat ve vrstvě převodu
Převaděče je také možné použít jako samostatnou vrstvu převodu dat, která se nachází mezi modelem zobrazení a zobrazením. To může být nezbytné například v případě, že data vyžadují speciální formátování, které model zobrazení neposkytuje.
Aby se model zobrazení mohl v zobrazení účastnit obousměrné datové vazby, musí PropertyChanged
její vlastnosti vyvolat událost. Zobrazit modely splňují tento požadavek implementací INotifyPropertyChanged
rozhraní a vyvoláním PropertyChanged
události při změně vlastnosti.
Pro kolekce je k dispozici popisný ObservableCollection<T>
pro zobrazení. Tato kolekce implementuje oznámení o změněných kolekcí, což vývojářům brání v implementaci INotifyCollectionChanged
rozhraní v kolekcích.
Model
Třídy modelu jsou ne vizuálové třídy, které zapouzdřují data aplikace. Model si proto můžete představit jako reprezentaci doménového modelu aplikace, který obvykle zahrnuje datový model spolu s obchodní a ověřovací logikou. Mezi příklady objektů modelu patří objekty pro přenos dat (DTO), objekty CLR (Plain Old CLR Objects) a vygenerované entity a objekty proxy.
Třídy modelů se obvykle používají ve spojení se službami nebo úložišti, které zapouzdřují přístup k datům a ukládání do mezipaměti.
Připojení modelů zobrazení k zobrazením
Modely zobrazení lze připojit k zobrazením pomocí funkcí datové vazby rozhraní .NET MAUI. Existuje mnoho přístupů, které lze použít k vytvoření zobrazení a zobrazení modelů a jejich přidružení za běhu. Tyto přístupy spadají do dvoukategoriích Volba mezi zobrazením prvního složení a prvním složením modelu zobrazení je problém s předvolbou a složitostí. Všechny přístupy však sdílejí stejný cíl, což je pro zobrazení mít model zobrazení přiřazený k jeho BindingContext vlastnost.
S prvním složením aplikace se koncepčně skládá ze zobrazení, která se připojují k modelům zobrazení, na které závisí. Hlavní výhodou tohoto přístupu je, že usnadňuje vytváření volně propojených aplikací testovatelných jednotek, protože modely zobrazení nemají žádnou závislost na samotných zobrazeních. Je také snadné pochopit strukturu aplikace pomocí vizuální struktury, a nemusíte sledovat provádění kódu, abyste pochopili, jak se třídy vytvářejí a přidruží. Kromě toho je první konstrukce v souladu s navigačním systémem Microsoftu Maui, který je zodpovědný za vytváření stránek při navigaci, což zkompiluje model zobrazení, který je nejprve složitý a nesprávně zarovnaný s platformou.
Při prvním složení modelu zobrazení se aplikace koncepčně skládá z modelů zobrazení a služba zodpovědná za vyhledání zobrazení modelu zobrazení. Zobrazení prvního složení modelu je pro některé vývojáře přirozenější, protože vytváření zobrazení může být abstrahováno, což jim umožňuje zaměřit se na logickou strukturu aplikace bez uživatelského rozhraní. Kromě toho umožňuje vytvářet modely zobrazení jinými modely zobrazení. Tento přístup je ale často složitý a může být obtížné pochopit, jak se vytvářejí a přidružují různé části aplikace.
Tip
Udržujte modely zobrazení a zobrazení nezávislé.
Vazba zobrazení na vlastnost ve zdroji dat by měla být hlavní závislost zobrazení na příslušném modelu zobrazení. Konkrétně neodkazujte na typy zobrazení, jako jsou button a ListView, z modelů zobrazení. Podle zde uvedených principů lze modely zobrazení testovat izolovaně, a tím snížit pravděpodobnost vad softwaru omezením rozsahu.
V následujících částech najdete hlavní přístupy k připojování modelů zobrazení k zobrazení.
Vytvoření modelu zobrazení deklarativním způsobem
Nejjednodušším přístupem je, aby zobrazení deklarativně vytvořilo instanci odpovídajícího modelu zobrazení v XAML. Při vytváření zobrazení se vytvoří také odpovídající objekt modelu zobrazení. Tento přístup je ukázaný v následujícím příkladu kódu:
<ContentPage xmlns:local="clr-namespace:eShop">
<ContentPage.BindingContext>
<local:LoginViewModel />
</ContentPage.BindingContext>
<!-- Omitted for brevity... -->
</ContentPage>
ContentPage
Při vytvoření je instance objektu LoginViewModel
automaticky vytvořena a nastavena jako zobrazení BindingContext
.
Tato deklarativní konstrukce a přiřazení modelu zobrazení podle zobrazení má výhodu, že je jednoduchá, ale má nevýhodu, že v modelu zobrazení vyžaduje výchozí konstruktor (bez parametrů).
Programové vytvoření modelu zobrazení
Zobrazení může mít kód v souboru s kódem, což vede k přiřazení modelu zobrazení k jeho BindingContext
vlastnosti. To se často provádí v konstruktoru zobrazení, jak je znázorněno v následujícím příkladu kódu:
public LoginView()
{
InitializeComponent();
BindingContext = new LoginViewModel(navigationService);
}
Programová konstrukce a přiřazení modelu zobrazení v kódu zobrazení má výhodu, že je jednoduchá. Hlavní nevýhodou tohoto přístupu je ale to, že zobrazení musí poskytnout model zobrazení se všemi požadovanými závislostmi. Použití kontejneru injektáže závislostí může pomoct udržovat volné spojení mezi zobrazením a modelem zobrazení. Další informace naleznete v tématu Injektáž závislostí.
Aktualizace zobrazení v reakci na změny v podkladovém modelu zobrazení nebo modelu
Všechny třídy modelu zobrazení a modelu, které jsou přístupné pro zobrazení, by měly implementovat INotifyPropertyChanged rozhraní. Implementace tohoto rozhraní v modelu zobrazení nebo třídě modelu umožňuje třídě poskytovat oznámení o změnách pro všechny ovládací prvky vázané na data v zobrazení, když se změní hodnota podkladové vlastnosti.
Aplikace by měla být navržena pro správné použití oznámení o změně vlastnosti splněním následujících požadavků:
- Vždy vyvolání
PropertyChanged
události, pokud se změní hodnota veřejné vlastnosti. Nepředpokládáme, že vyvoláníPropertyChanged
události je možné ignorovat z důvodu znalostí o tom, jak dochází k vazbě XAML. - Vždy vyvolání
PropertyChanged
události pro všechny počítané vlastnosti, jejichž hodnoty jsou používány jinými vlastnostmi v modelu zobrazení nebo modelu. - Vždy vyvolání
PropertyChanged
události na konci metody, která provede změnu vlastnosti, nebo pokud je objekt známý jako v bezpečném stavu. Vyvolání události přeruší operaci tím, že vyvolá obslužné rutiny události synchronně. Pokud k tomu dojde uprostřed operace, může objekt vystavit funkcím zpětného volání, pokud je v nebezpečném, částečně aktualizovaném stavu. Kromě toho je možné kaskádové změny aktivovat událostmiPropertyChanged
. Kaskádové změny obecně vyžadují dokončení aktualizací před provedením kaskádové změny. - Nikdy nevyvolá
PropertyChanged
událost, pokud se vlastnost nezmění. To znamená, že před vyvoláníPropertyChanged
události musíte porovnat staré a nové hodnoty. - Pokud inicializujete vlastnost, nikdy nezvyšujte
PropertyChanged
událost během konstruktoru modelu zobrazení. Ovládací prvky vázané na data v zobrazení nebudou v tuto chvíli odebírat oznámení o změnách. - Nikdy nezvyšujte více než jednu
PropertyChanged
událost se stejným argumentem názvu vlastnosti v rámci jednoho synchronního vyvolání veřejné metody třídy. Například vzhledem kNumberOfItems
vlastnosti, jejíž backing store je_numberOfItems
pole, pokud metoda zvýší_numberOfItems
padesátkrát během provádění smyčky, měla by vyvolat oznámení o změně vlastnosti naNumberOfItems
vlastnost pouze jednou, jakmile je všechna práce dokončena. U asynchronních metod vyvolátePropertyChanged
událost pro daný název vlastnosti v každém synchronním segmentu asynchronního řetězce pokračování.
Jednoduchým způsobem, jak tuto funkci poskytnout, by bylo vytvořit rozšíření BindableObject
třídy. V tomto příkladu ExtendedBindableObject
třída poskytuje oznámení o změnách, která jsou zobrazena v následujícím příkladu kódu:
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 ...
}
}
Třída .NET MAUIBindableObject
implementuje INotifyPropertyChanged
rozhraní a poskytuje metoduOnPropertyChanged
. Třída ExtendedBindableObject
poskytuje metodu RaisePropertyChanged
vyvolání oznámení o změně vlastnosti a v tom používá funkce poskytované BindableObject
třídou.
Zobrazit třídy modelu pak mohou odvozovat z ExtendedBindableObject
třídy. Proto každá třída modelu zobrazení používá metodu RaisePropertyChanged
ExtendedBindableObject
ve třídě k poskytnutí oznámení o změně vlastnosti. Následující příklad kódu ukazuje, jak aplikace eShop pro více platforem vyvolá oznámení o změně vlastnosti pomocí výrazu lambda:
public bool IsLogin
{
get => _isLogin;
set
{
_isLogin = value;
RaisePropertyChanged(() => IsLogin);
}
}
Použití výrazu lambda tímto způsobem zahrnuje malé náklady na výkon, protože výraz lambda se musí vyhodnotit pro každé volání. I když jsou náklady na výkon malé a obvykle by to nemělo vliv na aplikaci, můžou náklady narůstat, pokud existuje mnoho oznámení o změnách. Výhodou tohoto přístupu je ale to, že poskytuje podporu bezpečnosti typů kompilace a refaktoringu při přejmenování vlastností.
MVVM Frameworks
Model MVVM je dobře zavedený v .NET a komunita vytvořila mnoho architektur, které pomáhají usnadnit tento vývoj. Každá architektura poskytuje jinou sadu funkcí, ale je standardní, aby poskytovaly model běžného zobrazení s implementací INotifyPropertyChanged
rozhraní. Mezi další funkce architektur MVVM patří vlastní příkazy, pomocné rutiny navigace, komponenty lokátoru závislostí nebo lokátor služby a integrace platformy uživatelského rozhraní. I když tyto architektury není nutné používat, mohou urychlit a standardizovat vývoj. Multiformní aplikace eShop používá sadu nástrojů .NET Community MVVM Toolkit. Při výběru architektury byste měli zvážit potřeby vaší aplikace a silné stránky vašeho týmu. Níže uvedený seznam obsahuje některé z nejběžnějších architektur MVVM pro .NET MAUI.
Interakce s uživatelským rozhraním pomocí příkazů a chování
V aplikacích s více platformami se akce obvykle vyvolávají v reakci na akci uživatele, například kliknutí na tlačítko, které lze implementovat vytvořením obslužné rutiny události v souboru kódu. V modelu MVVM však odpovědnost za implementaci akce spočívá v modelu zobrazení a umístění kódu do kódu by se mělo vyhnout.
Příkazy poskytují pohodlný způsob, jak znázorňovat akce, které lze svázat s ovládacími prvky v uživatelském rozhraní. Zapouzdřují kód, který implementuje akci, a pomáhají ji oddělit od jeho vizuální reprezentace v zobrazení. Díky tomu budou modely zobrazení přenosnější na nové platformy, protože nemají přímou závislost na událostech poskytovaných architekturou uživatelského rozhraní platformy. .NET MAUI obsahuje ovládací prvky, které se dají deklarativní připojit k příkazu, a tyto ovládací prvky můžou vyvolat příkaz, když uživatel s ovládacím prvek pracuje.
Chování také umožňuje deklarativní připojení ovládacích prvků k příkazu. Chování se ale dá použít k vyvolání akce, která je přidružená k rozsahu událostí vyvolaných ovládacím prvku. Chování proto řeší mnoho stejných scénářů jako ovládací prvky s podporou příkazů a současně poskytuje větší míru flexibility a řízení. Kromě toho lze chování použít také k přidružení objektů příkazů nebo metod k ovládacím prvkům, které nebyly speciálně navrženy pro interakci s příkazy.
Implementace příkazů
Modely zobrazení obvykle zpřístupňují veřejné vlastnosti pro vazbu ze zobrazení, které implementují ICommand
rozhraní. Mnoho ovládacích prvků a gest .NET MAUI poskytuje Command
vlastnost, která může být data svázaná s objektem ICommand
poskytovaným modelem zobrazení. Ovládací prvek tlačítka je jedním z nejčastěji používaných ovládacích prvků a poskytuje vlastnost příkazu, která se spustí při kliknutí na tlačítko.
Poznámka:
I když je možné zveřejnit skutečnou ICommand
implementaci rozhraní, které model zobrazení používá (například Command<T>
RelayCommand
), doporučuje se zveřejnit příkazy veřejně jako ICommand
. Pokud byste někdy potřebovali změnit implementaci později, můžete ji snadno prohodit.
Rozhraní ICommand
definuje metodu Execute
, která zapouzdřuje samotnou operaci, metodu CanExecute
, která označuje, zda lze vyvolat příkaz, a CanExecuteChanged
událost, která nastane v případě, že dojde ke změnám, které ovlivňují, zda má být příkaz spuštěn. Ve většině případů zadáme metodu Execute
pouze pro naše příkazy. Podrobnější přehled najdete ICommand
v dokumentaci k commandingu pro .NET MAUI.
K dispozici v rozhraní .NET MAUI jsou třídy, Command<T>
které implementují ICommand
rozhraní, kde T
je typ argumentů do Execute
a CanExecute
Command
. Command
a Command<T>
jsou to základní implementace, které poskytují minimální sadu funkcí potřebných ICommand
pro rozhraní.
Poznámka:
Mnoho architektur MVVM nabízí více funkcí bohaté implementace ICommand
rozhraní.
Nebo Command
Command<T>
konstruktor vyžaduje objekt zpětného volání akce, který je volán při ICommand.Execute
vyvolání metody. Metoda CanExecute
je volitelný parametr konstruktoru a je Func, který vrací logickou hodnotu.
Multi-platformní aplikace eShop používá RelayCommand a AsyncRelayCommand. Hlavní výhodou moderních aplikací je, že AsyncRelayCommand
poskytuje lepší funkce pro asynchronní operace.
Následující kód ukazuje, jak Command
je instance, která představuje příkaz registru, vytvořen zadáním delegáta na metodu modelu Register view:
public ICommand RegisterCommand { get; }
Příkaz je vystaven zobrazení prostřednictvím vlastnosti, která vrací odkaz na .ICommand
Execute
Když je volána metoda objektuCommand
, jednoduše přesměruje volání metody v modelu zobrazení prostřednictvím delegáta, který byl zadán v konstruktoruCommand
. Asynchronní metoda může být vyvolána příkazem pomocí asynchronních a await klíčových slov při zadávání delegáta Execute
příkazu. To znamená, že zpětné volání je a Task
mělo by být očekáváno. Například následující kód ukazuje, jak ICommand
se instance, která představuje příkaz pro přihlášení, vytvoří zadáním delegáta na metodu SignInAsync
modelu zobrazení:
public ICommand SignInCommand { get; }
...
SignInCommand = new AsyncRelayCommand(async () => await SignInAsync());
Parametry lze předat do Execute
a CanExecute
akcí pomocí AsyncRelayCommand<T>
třídy k vytvoření instance příkazu. Například následující kód ukazuje, jak AsyncRelayCommand<T>
se instance používá k označení, že NavigateAsync
metoda bude vyžadovat argument typu řetězec:
public ICommand NavigateCommand { get; }
...
NavigateCommand = new AsyncRelayCommand<string>(NavigateAsync);
V obou třídách RelayCommand
RelayCommand<T>
je delegát metody CanExecute
v každém konstruktoru volitelný. Pokud není zadaný delegát, Command
vrátí hodnotu true pro CanExecute
. Model zobrazení však může indikovat změnu stavu příkazu CanExecute
voláním ChangeCanExecute
metody na objektu Command
. To způsobí vyvolání CanExecuteChanged
události. Všechny ovládací prvky uživatelského rozhraní vázané na příkaz pak aktualizují svůj povolený stav tak, aby odrážely dostupnost příkazu vázaného na data.
Vyvolání příkazů ze zobrazení
Následující příklad kódu ukazuje, jak Grid
v LoginView
vazbách na RegisterCommand
třídu LoginViewModel
pomocí TapGestureRecognizer
instance:
<Grid Grid.Column="1" HorizontalOptions="Center">
<Label Text="REGISTER" TextColor="Gray"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
</Grid>
Parametr příkazu lze také volitelně definovat pomocí CommandParameter
vlastnosti. Typ očekávaného argumentu je zadán v Execute
metodách cíle.CanExecute
Když TapGestureRecognizer
uživatel pracuje s připojeným ovládacím prvku, automaticky vyvolá cílový příkaz. Pokud CommandParameter
je zadaný, předá se jako argument delegátu Execute příkazu.
Implementace chování
Chování umožňuje přidání funkcí do ovládacích prvků uživatelského rozhraní bez nutnosti jejich podtřídy. Místo toho je funkce implementována ve třídě chování a připojena k ovládacímu prvku, jako by byla součástí samotného ovládacího prvku. Chování umožňuje implementovat kód, který byste obvykle museli zapsat jako kód, protože přímo komunikuje s rozhraním API ovládacího prvku tak, aby bylo možné ho stručně připojit k ovládacímu prvku a zabalit pro opakované použití ve více než jednom zobrazení nebo aplikaci. V kontextu MVVM jsou chování užitečným přístupem pro připojení ovládacích prvků k příkazům.
Chování připojené k ovládacímu prvku prostřednictvím připojených vlastností se označuje jako připojené chování. Toto chování pak může použít vystavené rozhraní API prvku, ke kterému je připojeno k přidání funkcí do tohoto ovládacího prvku nebo jiných ovládacích prvků ve vizuálním stromu zobrazení.
Chování rozhraní .NET MAUI je třída, která je odvozena od Behavior
třídy, Behavior<T>
kde T je typ ovládacího prvku, na který má chování platit. Tyto třídy poskytují OnAttachedTo
a OnDetachingFrom
metody, které by měly být přepsány tak, aby poskytovaly logiku, která se spustí při připojení k chování a odpojení od ovládacích prvků.
V aplikaci BindableBehavior<T>
eShop pro více platforem je třída odvozena od Behavior<T>
třídy. Účelem BindableBehavior<T>
třídy je poskytnout základní třídu pro chování .NET MAUI , které vyžadují BindingContext
, aby chování bylo nastaveno na připojený ovládací prvek.
BindableBehavior<T>
Třída poskytuje přepisovatelnou OnAttachedTo
metodu, která nastavuje BindingContext
chování, a přepisovatelnou OnDetachingFrom
metoduBindingContext
, která vyčistí .
Multi-platformní aplikace eShop obsahuje třídu EventToCommandBehavior , která je poskytována MAUI sadou nástrojů Community. EventToCommandBehavior
spustí příkaz v reakci na událost, ke které dochází. Tato třída je odvozena od BaseBehavior<View>
třídy, aby chování bylo možné vytvořit vazbu a spuštění ICommand
určené vlastností Command
při použití chování. Následující příklad kódu ukazuje EventToCommandBehavior
třídu:
/// <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);
}
}
}
OnDetachingFrom
Metody OnAttachedTo
se používají k registraci a zrušení registrace obslužné rutiny události pro událost definovanou EventName
ve vlastnosti. Když se událost aktivuje, OnTriggerHandled
vyvolá se metoda, která spustí příkaz.
Výhodou použití EventToCommandBehavior
příkazu ke spuštění příkazu při spuštění události je, že příkazy mohou být přidruženy k ovládacím prvkům, které nebyly navrženy pro interakci s příkazy. Kromě toho se tím přesune kód zpracování událostí k zobrazení modelů, kde je možné testovat jednotky.
Vyvolání chování ze zobrazení
To EventToCommandBehavior
je užitečné zejména pro připojení příkazu k ovládacímu prvku, který nepodporuje příkazy. Například LoginView používá EventToCommandBehavior
ke spuštění ValidateCommand
, když uživatel změní hodnotu svého hesla, jak je znázorněno v následujícím kódu:
<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>
Za běhu EventToCommandBehavior
bude reagovat na interakci s .Entry
Když uživatel zadá do Entry
pole, TextChanged
aktivuje se událost, která spustí ValidateCommand
v poli LoginViewModel
. Ve výchozím nastavení se argumenty události události předávají příkazu. V případě potřeby EventArgsConverter
lze vlastnost použít k převodu EventArgs
poskytnuté události na hodnotu, kterou příkaz očekává jako vstup.
Další informace o chování naleznete v tématu Chování v .NET MAUI Developer Center.
Shrnutí
Model-View-ViewModel (MVVM) pomáhá čistě oddělit obchodní a prezentační logiku aplikace od uživatelského rozhraní. Udržování čistého oddělení mezi logikou aplikace a uživatelským rozhraním pomáhá řešit řadu problémů s vývojem a usnadňuje testování, údržbu a vývoj aplikací. Může také výrazně zlepšit příležitosti opětovného použití kódu a umožní vývojářům a návrhářům uživatelského rozhraní snadněji spolupracovat při vývoji příslušných částí aplikace.
Pomocí vzoru MVVM se uživatelské rozhraní aplikace a podkladové prezentace a obchodní logiky rozdělí do tří samostatných tříd: zobrazení, které zapouzdřuje uživatelské rozhraní a logiku uživatelského rozhraní; model zobrazení, který zapouzdřuje logiku a stav prezentace; a model, který zapouzdřuje obchodní logiku a data aplikace.