ObservableObject
Это ObservableObject
базовый класс для объектов, наблюдаемых путем реализации INotifyPropertyChanged
и INotifyPropertyChanging
интерфейсов. Его можно использовать в качестве отправной точки для всех типов объектов, которые должны поддерживать уведомления об изменении свойств.
API платформы:
ObservableObject
, ,TaskNotifier
TaskNotifier<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 в действии.
- Дополнительные примеры можно найти в модульных тестах.
MVVM Toolkit