Was ist das MVVM-Muster?

Abgeschlossen

.NET MAUI-Apps ohne MVVM enthalten in der Regel mehr Code in den zugehörigen CodeBehind-Dateien. Die CodeBehind-Dateien in .NET MAUI folgen diesem Muster: {something}.xaml.cs. Der meiste Code in der CodeBehind-Datei steuert üblicherweise das Verhalten der Benutzeroberfläche (User Interface, UI). Das UI-Verhalten kann alle Ereignisse umfassen, die auf der Benutzeroberfläche stattfinden, z.B. die Änderung einer Farbe oder eine Textänderung. Darüber hinaus kann das Verhalten auch alle Ereignisse einschließen, die aufgrund der Benutzeroberfläche stattfinden, z. B. die Verwendung von Klickhandlern für Schaltflächen.

Ein Problem bei diesem Ansatz ist, dass es sehr schwierig ist, für CodeBehind-Dateien Unittests zu schreiben. CodeBehind-Dateien setzen häufig einen Anwendungsstatus voraus, der durch eine XAML-Analyse oder sogar durch andere Seiten erzeugt wird. Diese Bedingungen sind schwer zu berücksichtigen bei einem Test Runner für Unittests, der möglicherweise noch nicht einmal auf einem mobilen Gerät ausgeführt wird – geschweige denn mit einer Benutzeroberfläche. Über Komponententests kann also das Verhalten auf der Benutzeroberfläche in diesen Dateien selten getestet werden.

Aber hier ist das MVVM-Muster nützlich. Bei richtiger Verwendung löst das MVVM-Muster diese Probleme, indem der Großteil der UI-Verhaltenslogik in Klassen (ViewModels genannt) verschoben wird, für die Unittests durchgeführt werden können. Das MVVM-Muster wird am häufigsten mit Frameworks eingesetzt, die die Datenbindung unterstützen. Dies liegt daran, dass .NET MAUI eine Datenbindung zwischen jedem UI-Element und einem viewmodel ermöglicht und so Code in einer View oder CodeBehind-Datei (fast vollständig) überflüssig macht.

Welche Bestandteile umfasst eine MVVM-Anwendung?

Neben dem viewmodel als bemerkenswertestem Bestandteil des MVVM-Musters definiert das Muster außerdem ein Model und eine View. Die Definitionen dieser Elemente entsprechen denen einiger gängiger Muster, wie etwa dem Model-View-Controller (MVC).

Was ist ein Model?

In einer MVVM-Anwendung wird der Ausdruck Modell verwendet, um Ihre Geschäftsdaten und -vorgänge zu kennzeichnen. Das Modell umfasst sich nicht mit der Benutzerpräsentation der App.

Eine nützliche Regel zum Bestimmen des Code im Modell ist, dass er auf verschiedenen Plattformen portierbar sein sollte. Von einer mobilen App über eine Weboberfläche oder sogar über ein Befehlszeilenprogramm mit demselben Modell in allen Instanzen. Es steht nicht im Zusammenhang mit der Anzeige der Informationen für den Benutzer.

Im Hinblick auf die Anwendung für Personalabteilungen (HR) aus unserem Szenario kann das Model eine Employee-Klasse und eine Department-Klasse umfassen, die Daten und Logik zu diesen Entitäten enthält. Das Model könnte zum Beispiel auch eine EmployeeRepository-Klasse enthalten, die Persistenzlogik umfasst. Einige andere Softwareentwurfsmuster würden Elemente wie Repositorys als vom Model getrennt betrachten. Aber im Kontext von MVVM beziehen wir uns häufig auf Geschäftslogik oder Geschäftsdaten als Teil des Models.

Hier zwei Beispiele für eine Model-Komponente in C#:

public class Employee
{
    public int Id { get; }
    public string Name { get; set; }
    public Employee Supervisor { get; set; }
    public DateTime HireDate { get; set; }
    public void ClockIn() { ... }
}

public class EmployeeRepository
{
    public IList<Employee> QueryEmployees() { ... }
    ...
}

Was ist eine View?

Der Code View steuert Elemente, die direkt mit dem Benutzer interagieren, z. B. Steuerelemente wie Schaltflächen und Eingabefelder sowie andere rein visuelle Elemente wie Designs, Formatvorlagen und Schriftarten.

In .NET MAUI müssen Sie keinen C#-Code schreiben, um eine Ansicht selbst zu generieren. Stattdessen definieren Sie Ihre Ansichten häufig nach XAML-Dateien. Natürlich gibt es Situationen, in denen Sie ein benutzerdefiniertes Benutzersteuerelement aufrufen, in dem Sie eine eigene Ansicht über Code erstellen.

Was ist ein viewmodel?

Das bringt uns zurück zum viewmodel. Daas viewmodel ist der Vermittler zwischen der Geschäftslogik (Modell) und den View-Elementen (UI).

Ein Diagramm, das veranschaulicht, wie ein Ansichtsmodell ein Vermittler zwischen einem Modell und einer Ansicht ist.

Überlegen Sie, was ein viewmodel für die HR-Anwendung tun könnte. Angenommen, es gibt eine Ansicht, in der die verfügbare Urlaubszeit eines Mitarbeiters angezeigt wird, und Sie möchten, dass der Urlaubsausgleich als "2 Wochen, 3 Tage, 4 Stunden" angezeigt wird. Die Geschäftslogik im Modell bietet jedoch den gleichen Wert wie 13,5 Tage, eine Dezimalzahl, die die Gesamtzahl der Tage in einem 8-Stunden-Arbeitstag darstellt. Das Objektmodell sieht möglicherweise wie in der folgenden Liste aus:

  • Model – Die Employee-Klasse, die eine Methode umfasst:

    public decimal GetVacationBalanceInDays()
    {
        //Some math that calculates current vacation balance
        ...
    }
    
  • ViewModel – Eine EmployeeViewModel-Klasse mit einer Eigenschaft wie dieser:

    public class EmployeeViewModel
    {
        private Employee _model;
    
        public string FormattedVacationBalance
        {
            get
            {
                decimal vacationBalance = _model.GetVacationBalanceInDays();
                ... // Some logic to format and return the string as "X weeks, Y days, Z hours"
            }
        }
    }
    
  • View – Eine XAML-Seite, die eine einzige Beschriftung und eine Schaltfläche zum Schließen enthält. Die Bezeichnung weist eine Bindung an die Eigenschaft vom viewmodel auf:

    <Label Text="{Binding FormattedVacationBalance}" />
    

    Anschließend müssen Sie nur noch das BindingContext-Objekt für die Seite auf eine Instanz von EmployeeViewModel festlegen.

In diesem Beispiel enthält das Modell die Geschäftslogik. Diese Logik ist nicht an eine visuelle Anzeige oder ein visuelles Gerät gebunden. Sie können dieselbe Logik für ein Handheld-Gerät oder einen Desktopcomputer verwenden. „View“ besitzt keinerlei Kenntnis über die Geschäftslogik. Die Ansichtssteuerelemente wie die Beschriftung wissen, wie Text auf dem Bildschirm abgerufen wird, aber es ist nicht wichtig, ob es sich um einen Urlaubsausgleich oder eine zufällige Zeichenfolge handelt. Das viewmodel besitzt ein wenig Kenntnis beider Welten, sodass es als Vermittler fungieren kann.

Das Interessante am viewmodel ist, wie es seine Vermittlerrolle erfüllt: Es macht Eigenschaften verfügbar, an die die View gebunden werden kann. Öffentliche Eigenschaften sind die einzige Möglichkeit, durch die ein viewmodel Daten bereitstellen kann. Dies führt uns zum Grund der Namensgebung für ein viewmodel. Das Model repräsentiert im MVVM-Muster die Struktur, Daten und Logik der Geschäftsprozesse, das viewmodel stellt die Struktur, Daten und Logik dar, die von der View benötigt werden.

Wie funktioniert die Ansicht mit dem viewmodel?

Wenn Sie sich die Beziehung ansehen, die das viewmodel mit dem Modell hat, ist es eine Standard class-to-class-Beziehung. Das viewmodel verfügt über eine Instanz des Modells und macht Aspekte des Modells für die Ansicht über Eigenschaften verfügbar. Aber wie ruft eine Ansicht Eigenschaften für das viewmodel ab und legt sie fest? Wie aktualisiert die Ansicht die visuellen Steuerelemente mit neuen Werten, wenn sich das viewmodel ändert? Die Antwort ist die Datenbindung.

Die Eigenschaften des viewmodel werden zum Zeitpunkt gelesen, zu dem das Objekt an die Ansicht gebunden ist. Die Bindung hat jedoch keine Möglichkeit zu wissen, ob sich die Eigenschaften des viewmodel ändern, nachdem die Bindung auf die Ansicht angewendet wurde. Durch das Ändern des viewmodel wird der neue Wert nicht automatisch über die Bindung an die Ansicht weitergegeben. Um das viewmodel auf die Ansicht zu aktualisieren, muss das viewmodel die System.ComponentModel.INotifyPropertyChanged-Schnittstelle implementieren.

Die INotifyPropertyChanged Schnittstelle deklariert ein einzelnes Ereignis namens PropertyChanged. Es verwendet einen einzelnen Parameter, den Namen der Eigenschaft, die ihren Wert geändert hat. Das in .NET MAUI verwendete Bindungssystem versteht diese Schnittstelle und überwacht dieses Ereignis. Wenn sich eine Eigenschaft im viewmodel ändert und das Ereignis auslöst, benachrichtigt die Bindung das Ziel der Änderung.

Überlegen Sie, wie dies in der HR-Anwendung mit einem Mitarbeitenden-viewmodel funktioniert. Das viewmodel verfügt über Eigenschaften, die eine*n Mitarbeitende*n darstellen, z. B. den Namen des*der Mitarbeitenden. Das viewmodel implementiert die INotifyPropertyChanged-Schnittstelle und wenn sich die Name-Eigenschaft ändert, löst das PropertyChanged-Ereignis wie folgt aus:

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));
}

Die Ansicht, die die Details des Mitarbeiters beschreibt, enthält ein Bezeichnungssteuerelement, das an die Name-Eigenschaft des viewmodel gebunden ist:

<Label Text="{Binding Name}" />

Wenn sich die Name-Eigenschaft im viewmodel ändert, wird das PropertyChanged-Ereignis mit dem Namen dieser Eigenschaft ausgelöst. Die Bindung überwacht das Ereignis und benachrichtigt dann die Bezeichnung, dass die Name Eigenschaft geändert wurde. Anschließend wird die Eigenschaft der Bezeichnung Text mit dem neuesten Wert aktualisiert.

Überprüfen Sie Ihr Wissen

1.

Ein Entwickler arbeitet an einer .NET MAUI-Anwendung und möchte das MVVM-Muster implementieren. Sie haben ein Modell für Geschäftsdaten und -vorgänge und eine Ansicht für die Benutzerinteraktion erstellt. Was sollten sie als Vermittler zwischen dem Modell und der Ansicht erstellen?

2.

Ein Team entwickelt eine mobile Anwendung mit .NET MAUI und möchte sicherstellen, dass ihre Geschäftslogik auf verschiedenen Plattformen portierbar ist. Wo sollte diese Geschäftslogik im MVVM-Muster platziert werden?