ObservableObject
ObservableObject
ist eine Basisklasse für Objekte, die durch Implementieren der INotifyPropertyChanged
-Objekte und INotifyPropertyChanging
-Schnittstellen feststellbar sind. Sie kann als Ausgangspunkt für alle Arten von Objekten verwendet werden, die Eigenschaftsänderungsbenachrichtigungen unterstützen müssen.
Plattform-APIs:
ObservableObject
,TaskNotifier
,TaskNotifier<T>
Funktionsweise
ObservableObject
hat die folgenden Hauptfunktionen:
- Sie stellt eine Basisimplementierung für
INotifyPropertyChanged
undINotifyPropertyChanging
bereit, die diePropertyChanged
- undPropertyChanging
-Ereignisse verfügbar macht. - Sie stellt eine Reihe von
SetProperty
-Methoden bereit, die verwendet werden können, um Eigenschaftswerte von Typen, dieObservableObject
erben, einfach festzulegen und die entsprechenden Ereignisse automatisch auszuheben. - Sie stellt die
SetPropertyAndNotifyOnCompletion
-Methode bereit, die analog zuSetProperty
ist, aber mit der Möglichkeit,Task
-Eigenschaften festzulegen und die Benachrichtigungsereignisse automatisch auszuheben, wenn die zugewiesenen Aufgaben abgeschlossen werden. - Sie macht die Methoden
OnPropertyChanged
undOnPropertyChanging
verfügbar, die in abgeleiteten Typen außer Kraft gesetzt werden können, um anzupassen, wie die Benachrichtigungsereignisse ausgelöst werden.
Einfache Eigenschaft
Hier ist ein Beispiel für die Implementierung der Benachrichtigungsunterstützung für eine benutzerdefinierte Eigenschaft:
public class User : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
Die bereitgestellte SetProperty<T>(ref T, T, string)
-Methode überprüft den aktuellen Wert der Eigenschaft und aktualisiert ihn, falls anders, und löst dann auch automatisch die relevanten Ereignisse aus. Der Eigenschaftenname wird automatisch mithilfe des [CallerMemberName]
-Attributs erfasst, sodass nicht manuell angegeben werden muss, welche Eigenschaft aktualisiert wird.
Umschließen eines nicht feststellbaren Modells
Ein häufiges Szenario, z. B. beim Arbeiten mit Datenbankelementen, besteht darin, ein umgebrochenes „bindbares“ Modell zu erstellen, das Eigenschaften des Datenbankmodells weitergibt, und löst bei Bedarf die Änderungsbenachrichtigungen der Eigenschaft aus. Dies ist auch erforderlich, wenn Benachrichtigungsunterstützung in Modelle eingefügt werden soll, die die INotifyPropertyChanged
-Schnittstelle nicht implementieren. ObservableObject
bietet eine dedizierte Methode, um diesen Prozess zu vereinfachen. Im folgenden Beispiel ist ein Modell eine User
-Datenbanktabelle direkt zuzuordnen, ohne von ObservableObject
zu erben:
public class ObservableUser : ObservableObject
{
private readonly User user;
public ObservableUser(User user) => this.user = user;
public string Name
{
get => user.Name;
set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
}
}
In diesem Fall verwenden wir die SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
-Überladung. Die Signatur ist etwas komplexer als die vorherige. Dies ist erforderlich, damit der Code weiterhin extrem effizient ist, auch wenn wir keinen Zugriff auf ein Sicherungsfeld wie im vorherigen Szenario haben. Wir können jeden Teil dieser Methodensignatur detailliert durchgehen, um die Rolle der verschiedenen Komponenten zu verstehen:
TModel
ist ein Typargument, das den Typ des Modells angibt, das wir umschließen. In diesem Fall ist es unsereUser
-Klasse. Beachten Sie, dass dies nicht explizit angegeben werden muss – der C#-Compiler leitet dies automatisch ab, indem wir dieSetProperty
-Methode aufrufen.T
ist der Typ der Eigenschaft, die festgelegt werden soll. Ähnlich wieTModel
wird dies automatisch abgeleitet.T oldValue
ist der erste Parameter, und in diesem Fall verwenden wiruser.Name
, um den aktuellen Wert dieser Eigenschaft zu übergeben, die wir umschließen.T newValue
ist der neue Wert, der auf die Eigenschaft festgelegt werden soll, und hier übergeben wirvalue
, was der Eingabewert innerhalb des Eigenschaftensetters ist.TModel model
ist das Zielmodell, das wir umschließen, in diesem Fall übergeben wir die imuser
-Feld gespeicherte Instanz.Action<TModel, T> callback
ist eine Funktion, die aufgerufen wird, wenn sich der neue Wert der Eigenschaft von der aktuellen unterscheidet und die Eigenschaft festgelegt werden muss. Dies erfolgt durch diese Rückruffunktion, die als Eingabe des Zielmodells und des festzulegenden neuen Eigenschaftswerts empfängt. In diesem Fall weisen wir der Eigenschaft einfach den Eingabewert (den wirn
genannt haben) zurName
-Eigenschaft zu (durchu.Name = n
). Es ist hier wichtig, die Erfassung von Werten aus dem aktuellen Bereich zu vermeiden und nur mit den Als Eingaben für den Rückruf angegebenen zu interagieren, da dadurch der C#-Compiler die Rückruffunktion zwischenspeichert und eine Reihe von Leistungsverbesserungen durchführt. Aus diesem Grund greifen wir nicht nur direkt auf dasuser
-Feld hier oder denvalue
-Parameter im Setter zu, sondern verwenden stattdessen nur die Eingabeparameter für den Lambda-Ausdruck.
Die SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
-Methode macht das Erstellen dieser Umschließungs-Eigenschaften extrem einfach, da sie sowohl das Abrufen als auch das Festlegen der Zieleigenschaften übernimmt und gleichzeitig eine extrem kompakte API bereitstellt.
Hinweis
Im Vergleich zur Implementierung dieser Methode mithilfe von LINQ-Ausdrücken, insbesondere durch einen Parameter vom Typ Expression<Func<T>>
anstelle der Zustands- und Rückrufparameter, sind die Leistungsverbesserungen, die auf diese Weise erreicht werden können, wirklich signifikant. Insbesondere ist diese Version ~ 200x schneller als die, die LINQ-Ausdrücke verwendet, und macht überhaupt keine Speicherzuweisungen.
Behandeln von Task<T>
-Eigenschaften
Wenn es sich bei einer Eigenschaft um eine Task
-Eigenschaft handelt, müssen Sie das Benachrichtigungsereignis auch nach Abschluss der Aufgabe auslösen, sodass Bindungen zur richtigen Zeit aktualisiert werden. Beispiel: Anzeigen einer Ladeanzeige oder anderer Statusinformationen zu dem Vorgang, der durch den Vorgang dargestellt wird. ObservableObject
verfügt über eine API für dieses Szenario:
public class MyModel : ObservableObject
{
private TaskNotifier<int>? requestTask;
public Task<int>? RequestTask
{
get => requestTask;
set => SetPropertyAndNotifyOnCompletion(ref requestTask, value);
}
public void RequestValue()
{
RequestTask = WebService.LoadMyValueAsync();
}
}
Hier übernimmt die SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string)
-Methode die Aktualisierung des Zielfelds, die Überwachung des neuen Vorgangs, falls vorhanden, und das Benachrichtigungsereignis wird ausgelöst, wenn diese Aufgabe abgeschlossen ist. Auf diese Weise ist es möglich, nur eine Bindung an eine Aufgabeneigenschaft zu erstellen und benachrichtigt zu werden, wenn sich der Status ändert. TaskNotifier<T>
ist ein spezieller Typ, der von ObservableObject
bereitgestellt wird, der eine Zielinstanz Task<T>
umschließt und die erforderliche Benachrichtigungslogik für diese Methode aktiviert. Der TaskNotifier
-Typ kann auch direkt verwendet werden, wenn Sie nur über einen allgemeinen Task
verfügen.
Hinweis
Die SetPropertyAndNotifyOnCompletion
-Methode soll die Verwendung des NotifyTaskCompletion<T>
-Typs aus dem Microsoft.Toolkit
-Paket ersetzen. Wenn dieser Typ verwendet wurde, kann er nur durch die innere Task
- (oder Task<TResult>
-) Eigenschaft ersetzt werden, und dann kann die SetPropertyAndNotifyOnCompletion
-Methode verwendet werden, um den Wert festzulegen und Benachrichtigungsänderungen auszulösen. Alle vom Typ NotifyTaskCompletion<T>
bereitgestellten Eigenschaften sind direkt auf Task
-Instanzen verfügbar.
Beispiele
- Sehen Sie sich die Beispiel-App (für mehrere Benutzeroberflächen-Frameworks) an, um das MVVM-Toolkit in Aktion zu sehen.
- Weitere Beispiele finden Sie auch in den Komponententests.