Använda en vymodell

Slutförd

När du har lärt dig mer om de komponenter som utgör MVVM-mönstret (Model-View-ViewModel) upptäckte du förmodligen att modellen och vyn var lätta att definiera. Nu ska vi utforska hur du använder viewmodel för att bättre definiera dess roll i mönstret.

Exponera egenskaper för användargränssnittet

Precis som i föregående exempel förlitar sig viewmodels vanligtvis på modeller för de flesta av sina data och affärslogik. Men det är viewmodel som formaterar, konverterar och berikar data på det sätt som den aktuella vyn kräver.

Formatera med hjälp av en vymodell

Du har redan sett ett exempel på formatering med semestertid. Datumformatering, teckenkodning och serialisering är alla exempel på hur viewmodel kan formatera data från modellen.

Konvertera med hjälp av en vymodell

Ofta tillhandahåller modellen information på indirekta sätt. Men viewmodel kan åtgärda det. Anta till exempel att du vill visa på skärmen om en anställd är övervakare. Men vår Employee modell säger inte det direkt. I stället måste du härleda detta faktum baserat på om personen har andra som rapporterar till dem. Anta att modellen har den här egenskapen:

public IList<Employee> DirectReports
{
    get
    {
        ...
    }
}

Om listan är tom kan du dra slutsatsen att det Employee här inte är en övervakare. I det här fallet EmployeeViewModel inkluderar egenskapen IsSupervisor som tillhandahåller den logiken:

public bool IsSupervisor => _model.DirectReports.Any();

Berika med hjälp av en vymodell

Ibland kanske en modell bara tillhandahåller ett ID för relaterade data. Eller så kan du behöva gå till flera modellklasser för att korrelera de data som krävs för en enda skärm. Viewmodel är också en idealisk plats för att utföra dessa uppgifter. Anta att du vill visa alla projekt som en anställd hanterar för närvarande. Dessa data ingår inte i modellklassen Employee . Du kan komma åt den genom att titta på modellklassen CompanyProjects . Vår EmployeeViewModel, som alltid, exponerar sitt arbete som en offentlig egendom:

public IEnumerable<string> ActiveProjects => CompanyProjects.All
    .Where(p => p.Owner == _model.Id && p.IsActive)
    .Select(p => p.Name);

Använda genomströmningsegenskaper med en vymodell

Ofta behöver en viewmodel exakt den egenskap som modellen tillhandahåller. För dessa egenskaper skickar viewmodel bara data genom:

public string Name
{
    get => _model.Name;
    set => _model.Name = value;
}

Ange omfånget för viewmodel

Du kan använda en viewmodel på vilken nivå som helst där det finns en vy. En sida har vanligtvis en vymodell, men det kan även vara undervyer på sidan. En vanlig orsak till kapslade visningsmodeller är när sidan visar en ListView på sidan. Listan har en vymodell som representerar samlingen, till exempel EmployeeListViewModel. Varje element i listan är en EmployeeViewModel.

Diagram över en EmployeeListViewModel med flera EmployeeViewModel-underobjekt.

Det är också vanligt att ha en vy på den översta nivån som innehåller data och tillstånd för hela programmet, men som inte är associerade med någon viss sida. En sådan vymodell används ofta för att underhålla det "aktiva" objektet. Tänk på det ListView exempel som vi just beskrev. När användaren väljer en anställds rad representerar medarbetaren det aktuella objektet. Om användaren navigerar till en detaljsida eller väljer en knapp i verktygsfältet medan raden är markerad, ska åtgärden eller visningen vara för den medarbetaren. Ett elegant sätt att hantera det här scenariot är att ha ListView.SelectItem databundna till en egenskap som verktygsfältet eller detaljsidan också kan komma åt. Att placera den egenskapen på en central viewmodel fungerar bra.

Identifiera när viewmodels ska återanvändas med vyer

Hur du definierar relationen mellan viewmodel och modell och mellan viewmodel och vy styrs mer av appkrav än av regler. Syftet med viewmodel är att tillhandahålla den struktur och de data som behövs för vyn. Det syftet bör vägleda beslut om "hur stor" för att begränsa en vymodell.

Viewmodels återspeglar ofta strukturen för en modellklass, och de har en en-till-en-relation med den klassen. Du såg ett exempel tidigare med den omslutna EmployeeViewModel och utökade en enda Employee instans. Men det är inte alltid en en-till-en-relation. Om viewmodel är utformad för att tillhandahålla vad vyn behöver, kan du i stället få något som liknar HRDashboardViewModel att ge en översikt över en HR-avdelning, som inte har någon explicit relation med någon modell men kan använda data från vilken modellklass som helst .

På samma sätt kan det hända att viewmodels och vyer ofta har en en-till-en-relation. Men inte alltid. Nu ska vi tänka på en ListView som visar en rad för varje anställd. När du väljer en av raderna går du till en informationssida för anställda.

Listsidan har sin viewmodel med en samling. Som tidigare nämnts kan samlingen vara en samling EmployeeViewModel objekt. Och när användaren väljer en rad kan instansen EmployeeViewModel skickas till EmployeeDetailPage. Och detaljsidan kan använda den EmployeeViewModel som dess BindingContext.

Det här scenariot kan vara en utmärkt möjlighet att återanvända viewmodel. Men tänk på att viewmodels är avsedda att ge vad vyn behöver. I vissa fall kanske du vill ha separata viewmodels, även om de alla baseras på samma modellklass. I det här exemplet behöver raderna ListView förmodligen mycket mindre information än den fullständiga detaljsidan. Om hämtningen av data från detaljsidan medför för mycket omkostnader kanske du vill ha både EmployeeListRowViewModel och EmployeeDetailViewModel modeller som betjänar dessa respektive vyer.

Viewmodel-objektmodell

Att använda en basklass som implementeras INotifyPropertyChanged innebär att du inte behöver implementera gränssnittet på varje viewmodel igen. Överväg HR-programmet enligt beskrivningen i föregående del av den här utbildningsmodulen. Klassen EmployeeViewModel implementerade INotifyPropertyChanged gränssnittet och tillhandahöll en hjälpmetod med namnet OnPropertyChanged för att skapa PropertyChanged händelsen. Andra viewmodels i projektet, som de som beskriver resurser som tilldelats en anställd, skulle också kräva INotifyPropertyChanged att integreras fullt ut med en vy.

MVVM Toolkit-biblioteket, som är en del av .NET Community Toolkit, är en samling vanliga, fristående, lätta typer som ger en startimplementering för att skapa moderna appar med MVVM-mönstret.

I stället för att skriva en egen viewmodel-basklass ärver du från toolkit-klassen ObservableObject , vilket ger allt du behöver för en viewmodel-basklass. EmployeeViewModel Kan förenklas från:

using System.ComponentModel;

public class EmployeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private Employee _model;

    public string Name
    {
        get {...}
        set
        {
            _model.Name = value;
            OnPropertyChanged(nameof(Name))
        }
    }

    protected void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Till följande kod:

using Microsoft.Toolkit.Mvvm.ComponentModel;

public class EmployeeViewModel : ObservableObject
{
    private string _name;

    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
}

Koden kan förenklas ytterligare med hjälp av källgeneratorer som tillhandahålls av MVVM Toolkit. Genom att göra klassen partial och lägga till i [ObservableProperty] variabeln private genereras den offentliga egenskapen Name med lämpliga meddelanden om egenskapsändring.

using Microsoft.Toolkit.Mvvm.ComponentModel;

public partial class EmployeeViewModel : ObservableObject
{
    [ObservableProperty]
    private string _name;
}

MVVM Toolkit distribueras via CommunityToolkit.Mvvm NuGet-paketet.

Kontrollera dina kunskaper

1.

När du använder MVVM-mönstret (Model-View-ViewModel) med .NET MAUI frikopplas inte modellen, vyn och viewmodel helt från varandra. Vilket val beskriver ett gemensamt beroende mellan MVVM-delarna?

2.

Vilket är mest troligt att vara nära kopplat till plattformen och svårt att skapa enhetstester för: modellen, vyn eller viewmodel?