Was ist das MVVM-Muster?
.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).
Ü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 vonEmployeeViewModel
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.