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.

Vzory podnikových aplikací pomocí úvodní miniatury eBooku .NET MAUI

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.

Model MVVM

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 ContentViewodvozenou ContentPagetří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álostmi PropertyChanged . 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 k NumberOfItems 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 na NumberOfItems vlastnost pouze jednou, jakmile je všechna práce dokončena. U asynchronních metod vyvoláte PropertyChanged 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 ICommandv 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 CanExecuteCommand . 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 CommandParameterje 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.