Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
ObservableObject jest klasą bazową dla obiektów, które są obserwowalne dzięki implementacji interfejsów INotifyPropertyChanged 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
INotifyPropertyChangediINotifyPropertyChanging, uwidaczniając zdarzeniaPropertyChangediPropertyChanging. - Udostępnia szereg metod
SetProperty, których można używać do łatwego ustawiania wartości właściwości dla typów dziedziczących poObservableObject, a także do automatycznego wywoływania odpowiednich zdarzeń. - Zapewnia metodę
SetPropertyAndNotifyOnCompletion, która jest analogiczna do metodySetProperty, ale umożliwia ustawianie właściwościTaskoraz automatyczne wywoływanie zdarzeń powiadomień po zakończeniu przypisanych zadań. - Udostępnia metody
OnPropertyChangediOnPropertyChanging, które można przesłonić w typach pochodnych, aby dostosować sposób wywoływania zdarzeń powiadomień.
Prosta właściwość
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 model User bezpośrednio odwzorowuje tabelę bazy danych, bez dziedziczenia po ObservableObject:
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 przeciążenia SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string). 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 omówić każdą część tej sygnatury metody, aby zrozumieć rolę poszczególnych elementów:
-
TModeljest argumentem typu wskazującym typ modelu, który opakowujemy. W tym przypadku będzie to nasza klasaUser. Pamiętaj, że nie musimy jawnie określać tego elementu — kompilator języka C# wywnioskuje to automatycznie, wywołując metodęSetProperty. -
Tjest typem właściwości, którą chcemy ustawić. Podobnie jak wTModel, jest to automatycznie wywnioskowane. -
T oldValuejest pierwszym parametrem, a w tym przypadku używamyuser.Name, aby przekazać bieżącą wartość właściwości, którą opakowujemy. -
T newValueto nowa wartość ustawiana dla właściwości, a tutaj przekazujemyvalue, czyli wartość wejściową w setterze właściwości. -
TModel modelto model docelowy, który opakowujemy; w tym przypadku przekazujemy instancję przechowywaną w poluuser. -
Action<TModel, T> callbackto 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. Zostanie to wykonane przez tę funkcję wywołania zwrotnego, która jako dane wejściowe otrzymuje model docelowy i nową wartość właściwości do ustawienia. W tym przypadku po prostu przypisujemy wartość wejściową (o nazwien) doNamewł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. To dlatego nie uzyskujemy tutaj po prostu bezpośredniego dostępu do polauserani do parametruvaluew setterze, lecz zamiast tego używamy wyłącznie parametrów wejściowych 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 właściwości Task<T>
Jeśli właściwość ma typ Task, konieczne jest również wywołanie zdarzenia powiadomienia po ukończeniu zadania, aby powiązania były aktualizowane we właściwym momencie, na przykład w celu wyświetlenia wskaźnika ładowania lub innych informacji o stanie operacji reprezentowanej przez to 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.
TaskNotifier<T> to specjalny typ udostępniany przez ObservableObject, który opakowuje docelową instancję Task<T> i zapewnia logikę powiadomień wymaganą przez tę metodę. 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ć samą wewnętrzną właściwością Task (lub Task<TResult>), a następnie za pomocą metody SetPropertyAndNotifyOnCompletion ustawić jej wartość i wywołać powiadamianie o zmianach. Wszystkie właściwości udostępniane przez typ NotifyTaskCompletion<T> są dostępne bezpośrednio w wystąpieniach Task.
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.