Основы и примеры MVVM. Yet Another Property Changed Notification

  • Вы работаете с WPF? Вы слышали про MVVM?
  • Тогда мы идем к вам!!!

Итак, для тех, кто еще не в курсе, что такое «MVVM» (аббревиатура Model-View-ViewModel) — хочу вас обрадовать: вы обязательно об этом скоро узнаете 😉


Для самых любознательных рекомендую отличный материал от наших коллег из Infragistics, публикующихся на страничках MSDN: http://msdn.microsoft.com/ru-ru/magazine/dd419663.aspx

В современном мире мы, проектируя интерфейсы пользователя на WPF, сталкиваемся с такими вещами, как привязка данных (Data Binding). Для того, чтобы этот механизм использовался в полном объеме, привязываемый объект должен реализовывать механизм оповещений (INotifyPropertyChanged) об изменении его свойств, чтобы binding — среда могла правильно определить изменение свойства и выполнить обновление GUI. Вот как обычно выглядит привязка в коде на XAML:

теперь, имея экземпляр класса со свойством SomeData, мы можем присвоить его свойству DataContext данного элемента TextBox и увидеть, что данные отобразятся:

Доработаем немного наш класс, чтобы он реализовал интерфейс INotifyPropertyChanged (здесь я приведу самый простой пример, без проверки наличия данного свойства у класса и т.п.):

Зачастую бывает так, что при проектировании классов, мы упускаем из виду необходимость реализовать интерфейс INotifyPropertyChanged, например, когда реализацией классов занимается другой разработчик, либо это не описано в требованиях или не учтено в дизайне, а иногда и попросту эта вещь забывается.

Кроме того, бывает так, что для огромного количества свойств в классе (а это и есть плохой дизайн!!!!) очень проблематично выделять дополнительное поле-контейнер данных (в нашем примере — string _someData), а также оповещение об изменении свойства NotifyPropertyChanged(«SomeData») долго писать для каждого свойства в отдельности.

Было бы очень здорово этот механизм автоматизировать! Отлично, сегодня мы попробуем это осуществить.

Отдельная благодарность — Денису aka Storm — за идею.

Итак, вернемся к исходному классу:

Есть несколько способов авто реализации данного механизма: инжектирование интерфейсов (при помощи генерации proxy-объекта, этот способ мы рассмотрим позже, возможно в другой статье), создание run-time wrapper-а, реализующего интерфейс и имеющего все свойства оригинального объекта, создание CustomTypeDescriptor.

Мы будем рассмотрим последний способ, как наиболее подходящий для наших целей:

В настоящий момент пользы от CustomTypeDescriptor нет, однако мы можем описать список свойств, которые имеет дескриптор, переопределив метод GetProperties и предварительно получив все свойства оригинального класса:

Опять же, ничего полезного пока нет, потому что нам все еще необходим механизм, оповещающий об изменении свойств, причем каждого свойства. То есть событие, срабатывающее при вызове метода SetValue каждого PropertyDescriptor’а в данной коллекции свойств. Для этого нужно реализовать класс-наследник от PropertyDescriptor.

Обратите внимание, что мы сделали обертку (wrapped) над оригинальным PropertyDescriptor’ом, потому что базовый абстрактный класс не имеет реализации метода SetValue.

Теперь мы можем использовать наш ChangeNotificationPropertyDescriptor в классе ChangeNotifier, кроме того сделаем наш класс generic, чтобы им было удобнее пользоваться:

Обратите внимание на объявление T Target — это экземпляр оригинального класса (модель). T — оригинальный класс.

Чтобы создать экземпляр такого дескриптора для нашего SomeClass выполним следующие действия:

Теперь экземпляр ChangeNotifier можно, например, присвоить свойству DataContext нескольким элементам TextBox, для которых назначен Binding на свойство SomeData. Это свойство при изменении будет вызывать оповещение PropertyChanged и данные обновятся в обоих текстовых полях.

Код примера:

Window1.xaml:

Window1.xaml.cs:

Ну вот и все на сегодня!

Спасибо всем большое за то что дочитали до этого места 😉


Похожие записи:

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля отмечены *