Поделиться через


ObservableObject

Это ObservableObject базовый класс для объектов, наблюдаемых путем реализации INotifyPropertyChanged и INotifyPropertyChanging интерфейсов. Его можно использовать в качестве отправной точки для всех типов объектов, которые должны поддерживать уведомления об изменении свойств.

API платформы: ObservableObject, , TaskNotifierTaskNotifier<T>

Принцип работы

ObservableObject имеет следующие основные функции:

  • Она предоставляет базовую реализацию для INotifyPropertyChanged и INotifyPropertyChanging, предоставляя события PropertyChanged и PropertyChanging события.
  • Он предоставляет ряд SetProperty методов, которые можно использовать для легкого задания значений свойств из типов, наследуемых от ObservableObject, и автоматического вызова соответствующих событий.
  • Он предоставляет SetPropertyAndNotifyOnCompletion метод, который аналогичен SetProperty , но с возможностью задать Task свойства и автоматически вызывать события уведомлений при завершении назначенных задач.
  • Он предоставляет OnPropertyChanged методы и OnPropertyChanging методы, которые можно переопределить в производных типах, чтобы настроить способ создания событий уведомлений.

Простое свойство

Ниже приведен пример реализации поддержки уведомлений для пользовательского свойства:

public class User : ObservableObject
{
    private string name;

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }
}

SetProperty<T>(ref T, T, string) Предоставленный метод проверяет текущее значение свойства и обновляет его, если он отличается, а затем автоматически вызывает соответствующие события. Имя свойства автоматически фиксируется с помощью атрибута [CallerMemberName] , поэтому не нужно вручную указывать, какое свойство обновляется.

Упаковка не наблюдаемой модели

Распространенный сценарий, например при работе с элементами базы данных, заключается в создании модели оболочки "привязки", которая ретранслирует свойства модели базы данных и вызывает уведомления об изменении свойства при необходимости. Это также необходимо, если требуется внедрить поддержку уведомлений в модели, которые не реализуют INotifyPropertyChanged интерфейс. ObservableObject предоставляет выделенный метод, чтобы упростить этот процесс. В следующем примере User модель напрямую сопоставляет таблицу базы данных, не наследуя от 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);
    }
}

В этом случае мы используем перегрузку SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) . Сигнатура немного сложнее предыдущей. Это необходимо, чтобы код все еще был чрезвычайно эффективным, даже если у нас нет доступа к резервному полю, как в предыдущем сценарии. Мы можем подробно ознакомиться с каждой частью сигнатуры этого метода, чтобы понять роль различных компонентов:

  • TModel — это аргумент типа, указывающий тип модели, которую мы упаковаем. В этом случае это будет наш User класс. Обратите внимание, что нам не нужно явно указывать этот параметр. Компилятор C# будет автоматически выводить это путем вызова SetProperty метода.
  • T — это тип свойства, которое мы хотим задать. TModelАналогично, это автоматически выводится.
  • T oldValue является первым параметром, и в этом случае мы используем user.Name для передачи текущего значения этого свойства, которое мы упаковаем.
  • T newValue — это новое значение для задания свойства, и здесь мы передаваем valueзначение, которое является входным значением в методе задания свойств.
  • TModel model — это целевая модель, которую мы упаковаем, в этом случае мы передаваем экземпляр, хранящийся в user поле.
  • Action<TModel, T> callback — это функция, которая будет вызываться, если новое значение свойства отличается от текущего, а свойство должно быть задано. Это будет сделано этой функцией обратного вызова, которая получает в качестве входных данных целевую модель и новое значение свойства для задания. В этом случае мы просто назначаем входное значение (которое мы вызвалиn) Name свойству (выполняя).u.Name = n Важно избегать записи значений из текущей области и взаимодействовать только с теми, которые заданы в качестве входных данных обратному вызову, так как это позволяет компилятору C# кэшировать функцию обратного вызова и выполнять ряд улучшений производительности. Это связано с тем, что мы не только напрямую обращаются к user полю здесь или value параметру в методе задания, но вместо этого мы используем только входные параметры для лямбда-выражения.

Метод SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>, string) делает создание этих свойств оболочки чрезвычайно простым, так как он заботится о получении и настройке целевых свойств при предоставлении чрезвычайно компактного API.

Примечание.

По сравнению с реализацией этого метода с помощью выражений LINQ, в частности с помощью параметра типа Expression<Func<T>> вместо параметров состояния и обратного вызова, улучшения производительности, которые можно добиться таким образом, действительно важны. В частности, эта версия составляет ~200x быстрее, чем одна с помощью выражений LINQ, и не делает выделения памяти вообще.

Обработка Task<T> свойств

Если свойство является Task свойством, необходимо также вызвать событие уведомления после завершения задачи, чтобы привязки обновлялись в нужное время. Например, чтобы отобразить индикатор загрузки или другие сведения о состоянии операции, представленной задачей. ObservableObject имеет API для этого сценария:

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) Здесь метод будет заботиться об обновлении целевого поля, мониторинге новой задачи, если она присутствует, и вызове события уведомления при завершении этой задачи. Таким образом, можно просто привязаться к свойству задачи и получать уведомления при изменении его состояния. Это TaskNotifier<T> специальный тип, предоставляемый ObservableObject тем, что упаковывает целевой Task<T> экземпляр и включает необходимую логику уведомлений для этого метода. Тип TaskNotifier также доступен для использования непосредственно, если у вас есть только общий Task тип.

Примечание.

Метод SetPropertyAndNotifyOnCompletion предназначен для замены NotifyTaskCompletion<T> использования типа из Microsoft.Toolkit пакета. Если используется этот тип, его можно заменить только внутренним Task (или Task<TResult>) свойством, а затем SetPropertyAndNotifyOnCompletion метод можно использовать для задания его значения и повышения изменений уведомлений. Все свойства, NotifyTaskCompletion<T> предоставляемые типом, доступны непосредственно на Task экземплярах.

Примеры

  • Ознакомьтесь с примером приложения (для нескольких платформ пользовательского интерфейса), чтобы просмотреть набор средств MVVM в действии.
  • Дополнительные примеры можно найти в модульных тестах.