Язык программирования C#9 и платформа .NET5
public interface INotifyPropertyChanged{event PropertyChangedEventHandler PropertyChanged;}Добавьте в файл
следующие операторыCar.cs:usingusing System.ComponentModel;using System.Runtime.CompilerServices;Затем обеспечьте реализацию классом
интерфейсаCar:INotifyPropertyChangedpublic class Car : INotifyPropertyChanged{// Для краткости код не показан.public event PropertyChangedEventHandler PropertyChanged;}Событие
принимает объектную ссылку и новый экземпляр классаPropertyChanged:PropertyChangedEventArgsPropertyChanged?.Invoke(this,new PropertyChangedEventArgs("Model"));Первый параметр представляет собой объект, который инициирует событие. Конструктор класса
принимает строку, указывающую свойство, которое было изменено и нуждается в обновлении. Когда событие инициировано, механизм привязки ищет элементы управления, привязанные к именованному свойству данного объекта. В случае передачи конструкторуPropertyChangedEventArgsзначенияPropertyChangedEventArgsобновляются все привязанные свойства объекта.String.EmptyВы сами управляете тем, какие свойства вовлечены в процесс автоматического обновления. Автоматически обновляться будут только те свойства, которые генерируют событие
внутри блокаPropertyChanged. Обычно в перечень входят все свойства классов моделей, но в зависимости от требований приложения некоторые свойства можно опускать. Вместо инициирования событияsetнепосредственно в блокеPropertyChangedдля каждого задействованного свойства распространенный подход предусматривает написание вспомогательного метода (как правило, называемогоset), который генерирует событие от имени свойств обычно в базовом классе для моделей. Добавьте в классOnPropertyChanged()следующий метод:Carprotected void OnPropertyChanged([CallerMemberName] string propertyName = ""){PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));}Модифицируйте каждое автоматическое свойство класса
, чтобы оно имело полноценные блокиCarиget, а также поддерживающее поле. В случае если значение изменилось, вызовите вспомогательный методset. Вот обновленное свойствоOnPropertyChanged():Idprivate int _id;public int Id{get => _id;set{if (value == _id) return;_id = value;OnPropertyChanged();}}Проделайте аналогичную работу со всеми остальными свойствами в классе и снова запустите приложение. Выберите автомобиль и щелкните на кнопке Change Color. Изменение немедленно отобразится в пользовательском интерфейсе. Первая проблема решена!
Использование операции nameof
В версии C# 6 появилась операция
, которая возвращает строковое имя переданного ей элемента. Ее можно применять в вызовах методаnameofвнутри блоковOnPropertyChanged(), например:setpublic string Color{get { return _color; }set{if (value == _color) return;_color = value;OnPropertyChanged(nameof(Color));}}Обратите внимание на то, что в случае использования операции
удалять атрибутnameofиз метода[CallerMemberName]необязательно (хотя он становится излишним). В конце концов, выбор между применением операцииOnPropertyChanged()или атрибутаnameofзависит от личных предпочтений.CallerMemberNameНаблюдаемые коллекции
Следующей проблемой, которую необходимо решить, является обновление пользовательского интерфейса при изменении содержимого коллекции, что достигается путем реализации интерфейса
. ПодобноINotifyCollectionChangedданный интерфейс открывает доступ к единственному событиюINotifyPropertyChanged. В отличие отCollectionChangedреализация интерфейсаINotifyPropertyChangedвручную предполагает больший объем действий, чем просто вызов метода в блокеINotifyCollectionChangedсвойства. Понадобится создать реализацию полного списка объектов и генерировать событиеsetкаждый раз, когда он изменяется.CollectionChangedИспользование класса ObservableCollection<T>
К счастью, существует намного более легкий способ, чем создание собственных классов коллекций. Класс
реализует интерфейсыObservableCollection<T>,INotifyCollectionChangedиINotifyPropertyChangedи входит в состав .NET Core. Никакой дополнительной работы делать не придется. Чтобы продемонстрировать его применение, добавьте операторCollection<T>для пространства именusingи модифицируйте закрытое полеSystem.Collections.ObjectModelследующим образом:_cars