Obserwowalny obiekt
Jest ObservableObject
to klasa bazowa dla obiektów, które można zaobserwować przez zaimplementowanie INotifyPropertyChanged
interfejsów i INotifyPropertyChanging
. Może służyć jako punkt wyjścia dla wszystkich rodzajów obiektów, które muszą obsługiwać powiadomienia o zmianie właściwości.
Interfejsy API platformy:
ObservableObject
, ,TaskNotifier
TaskNotifier<T>
Jak to działa
ObservableObject
ma następujące główne funkcje:
- Zapewnia on podstawową implementację elementu
INotifyPropertyChanged
iINotifyPropertyChanging
, uwidaczniając zdarzeniaPropertyChanged
iPropertyChanging
. - Udostępnia szereg
SetProperty
metod, których można użyć do łatwego ustawiania wartości właściwości z typów dziedziczynych zObservableObject
klasy i w celu automatycznego zgłaszania odpowiednich zdarzeń. - Zapewnia metodę
SetPropertyAndNotifyOnCompletion
, która jest analogiczna doSetProperty
metody , ale z możliwością ustawianiaTask
właściwości i automatycznego zgłaszania zdarzeń powiadomień po zakończeniu przypisanych zadań. - Uwidacznia
OnPropertyChanged
metody iOnPropertyChanging
, które można zastąpić w typach pochodnych, aby dostosować sposób zgłaszania zdarzeń powiadomień.
Właściwość Simple
Oto przykład implementacji obsługi powiadomień dla właściwości niestandardowej:
public class User : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
Podana SetProperty<T>(ref T, T, string)
metoda sprawdza bieżącą wartość właściwości i aktualizuje ją, jeśli jest inna, a następnie automatycznie zgłasza odpowiednie zdarzenia. Nazwa właściwości jest automatycznie przechwytywana za pomocą atrybutu [CallerMemberName]
, dlatego nie trzeba ręcznie określać, która właściwość jest aktualizowana.
Zawijanie nieoserwowalnego modelu
Typowy scenariusz, na przykład podczas pracy z elementami bazy danych, polega na utworzeniu modelu zawijania "możliwego do powiązania", który przekazuje właściwości modelu bazy danych i zgłasza zmiany właściwości w razie potrzeby. Jest to również konieczne, gdy chcesz wstrzyknąć obsługę powiadomień do modeli, które nie implementują interfejsu INotifyPropertyChanged
. ObservableObject
Udostępnia dedykowaną metodę, aby ten proces był prostszy. W poniższym przykładzie User
model jest bezpośrednio mapowaniem tabeli bazy danych bez dziedziczenia z ObservableObject
klasy :
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);
}
}
W tym przypadku używamy SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
przeciążenia. Podpis jest nieco bardziej złożony niż poprzedni — jest to konieczne, aby kod nadal był bardzo wydajny, nawet jeśli nie mamy dostępu do pola zapasowego, takiego jak w poprzednim scenariuszu. Możemy szczegółowo zapoznać się z każdą częścią tego podpisu metody, aby zrozumieć rolę różnych składników:
TModel
jest argumentem typu wskazującym typ modelu, który opakowujemy. W tym przypadku będzie to naszaUser
klasa. Pamiętaj, że nie musimy jawnie określać tego elementu — kompilator języka C# wywnioskuje to automatycznie, wywołując metodęSetProperty
.T
jest typem właściwości, którą chcemy ustawić. Podobnie jakTModel
w przypadku metody , jest to automatycznie wnioskowane.T oldValue
jest pierwszym parametrem, a w tym przypadku używamyuser.Name
polecenia , aby przekazać bieżącą wartość tej właściwości, którą opakowujemy.T newValue
jest nową wartością ustawioną na właściwość , a tutaj przekazujemyvalue
wartość wejściową w ustawieniu właściwości.TModel model
to model docelowy, który opakowujemy, w tym przypadku przekazujemy wystąpienie przechowywane wuser
polu.Action<TModel, T> callback
to funkcja, która zostanie wywołana, jeśli nowa wartość właściwości jest inna niż bieżąca, a właściwość musi zostać ustawiona. Będzie to wykonywane przez tę funkcję wywołania zwrotnego, która otrzymuje jako dane wejściowe modelu docelowego i nową wartość właściwości do ustawienia. W tym przypadku po prostu przypisujemy wartość wejściową (o nazwien
) doName
właściwości (wykonując polecenieu.Name = n
). Ważne jest, aby uniknąć przechwytywania wartości z bieżącego zakresu i korzystać tylko z tych podanych jako dane wejściowe wywołania zwrotnego, ponieważ umożliwia to kompilatorowi języka C# buforowanie funkcji wywołania zwrotnego i wykonywanie szeregu ulepszeń wydajności. Jest to spowodowane tym, że nie tylko uzyskujemy bezpośredni dostęp douser
pola tutaj lubvalue
parametru w zestawie, ale zamiast tego używamy tylko parametrów wejściowych dla wyrażenia lambda.
Metoda SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string)
sprawia, że tworzenie tych właściwości opakowujących jest niezwykle proste, ponieważ dba zarówno o pobieranie, jak i ustawianie właściwości docelowych przy jednoczesnym zapewnieniu niezwykle kompaktowego interfejsu API.
Uwaga
W porównaniu z implementacją tej metody przy użyciu wyrażeń LINQ, w szczególności za pomocą parametru typu Expression<Func<T>>
zamiast parametrów stanu i wywołania zwrotnego, ulepszenia wydajności, które można osiągnąć w ten sposób, są naprawdę znaczące. W szczególności ta wersja jest ok. 200x szybsza niż ta używająca wyrażeń LINQ i w ogóle nie wykonuje żadnych alokacji pamięci.
Obsługa Task<T>
właściwości
Jeśli właściwość jest właściwością Task
, należy również zgłosić zdarzenie powiadomienia po zakończeniu zadania, aby powiązania były aktualizowane w odpowiednim czasie. Np. aby wyświetlić wskaźnik ładowania lub inne informacje o stanie operacji reprezentowanej przez zadanie. ObservableObject
ma interfejs API dla tego scenariusza:
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();
}
}
SetPropertyAndNotifyOnCompletion<T>(ref TaskNotifier<T>, Task<T>, string)
W tym miejscu metoda zajmie się aktualizowaniem pola docelowego, monitorowaniem nowego zadania, jeśli jest obecne, i wywoływaniem zdarzenia powiadomienia po zakończeniu tego zadania. W ten sposób można po prostu powiązać z właściwością zadania i otrzymywać powiadomienia o zmianie stanu. Jest TaskNotifier<T>
to specjalny typ uwidoczniony przez ObservableObject
to zawijanie wystąpienia docelowego Task<T>
i umożliwia niezbędną logikę powiadomień dla tej metody. Typ TaskNotifier
jest również dostępny do użycia bezpośrednio, jeśli masz tylko ogólne Task
.
Uwaga
Metoda SetPropertyAndNotifyOnCompletion
ma zastąpić użycie NotifyTaskCompletion<T>
typu z Microsoft.Toolkit
pakietu. Jeśli ten typ był używany, można go zastąpić tylko właściwością wewnętrzną Task
(lub Task<TResult>
), a następnie SetPropertyAndNotifyOnCompletion
można użyć metody w celu ustawienia jej wartości i wywołania zmian powiadomień. Wszystkie właściwości uwidocznione przez NotifyTaskCompletion<T>
typ są dostępne bezpośrednio w Task
wystąpieniach.
Przykłady
- Zapoznaj się z przykładową aplikacją (dla wielu struktur interfejsu użytkownika), aby zobaczyć, jak działa zestaw narzędzi MVVM Toolkit.
- Więcej przykładów można również znaleźć w testach jednostkowych.