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 i INotifyPropertyChanging, uwidaczniając zdarzenia PropertyChanged i PropertyChanging .
  • Udostępnia szereg SetProperty metod, których można użyć do łatwego ustawiania wartości właściwości z typów dziedziczynych z ObservableObjectklasy i w celu automatycznego zgłaszania odpowiednich zdarzeń.
  • Zapewnia metodę SetPropertyAndNotifyOnCompletion , która jest analogiczna do SetProperty metody , ale z możliwością ustawiania Task właściwości i automatycznego zgłaszania zdarzeń powiadomień po zakończeniu przypisanych zadań.
  • Uwidacznia OnPropertyChanged metody i OnPropertyChanging , 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 ObservableObjectklasy :

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 nasza User 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 jak TModelw przypadku metody , jest to automatycznie wnioskowane.
  • T oldValue jest pierwszym parametrem, a w tym przypadku używamy user.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 przekazujemy valuewartość wejściową w ustawieniu właściwości.
  • TModel model to model docelowy, który opakowujemy, w tym przypadku przekazujemy wystąpienie przechowywane w user 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 nazwie n) do Name właściwości (wykonując polecenie u.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 do user pola tutaj lub value 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.