Inotifypropertychanged c как использовать

от admin

Inotifypropertychanged c как использовать

В прошлой теме использовался объект Phone для привязки к текстовым блокам. Однако если мы изменим его, содержимое текстовых блоков не изменится. Например, добавим в окно приложения кнопку:

И в файле кода для этой кнопки определим обработчик, в котором будет меняться свойства ресурса:

Сколько бы мы не нажимали на кнопку, текстовые блоки, привязанные к ресурсу, не изменятся. Чтобы объект мог полноценно реализовать механизм привязки, нам надо реализовать в его классе интерфейс INotifyPropertyChanged. И для этого изменим класс Phone следующим образом:

Когда объект класса изменяет значение свойства, то он через событие PropertyChanged извещает систему об изменении свойства. А система обновляет все привязанные объекты.

Как работает интерфейс INotifyPropertyChanged?

Собственно сабж: как изнутри работает данный интерфейс? Что происходит при вызове метода NotifyPropertyChanged(string propertyName) ? Как UI элемент узнает, что что-то произошло и нужно «перерисоваться»? Объясните пожалуйста. Чем подробнее — тем лучше)

Если вы имплементируете интерфейс INotifyPropertyChanged , то вы должны определить event PropertyChanged , и отправлять его каждый раз, когда значение изменится.

Если у вас есть привязка к какому-то свойству, то Binding в числе прочего проверяет, реализует ли binding source (то есть, объект, к свойству которого происходит binding), этот интерфейс, и если да, то подписывается на этот самый event . По приходу event’а значение свойства перечитывается (через reflection).

Дополнение: А что происходит с ItemsSource ? А для него интересен интерфейс INotifyCollectionChanged , который сообщает о том, что в коллекции появились/исчезли элементы.

Если вы привязываете ItemsSource в ListView к коллекции, Binding заботится о том, чтобы ListView знал текущее значение самой коллекции. То есть, если вы коллекцию подмените на другую коллекцию, об этом сообщит Binding. А вот нотификация об изменении списка элементов коллекции происходит следующим образом.

Когда ListView видит, что объект коллекции поменялся (эту информацию поставляет Binding ), он самостоятельно проверяет наличие интерфейса INotifyCollectionChanged , и если коллекция его поддерживает, подписывается на событие CollectionChanged . Когда это событие приходит, в нём есть NotifyCollectionChangedEventArgs , который содержит информацию о том, что именно произошло, какие элементы добавились (и куда), а какие исчезли (и откуда). Имея эту информацию, ListView может перестроить список.

Заметьте, что обыкновенный List<T> не реализует интерфейс INotifyCollectionChanged , поэтому если в привязке лежит List<T> , ListView автоматически обновляться не будет. Обычно никто не реализует INotifyCollectionChanged самостоятельно, а пользуются готовыми коллекциями. Например, ObservableCollection<T> .

C# Language
Интерфейс INotifyPropertyChanged

Интерфейс INotifyPropertyChanged необходим, когда вам нужно, чтобы ваш класс сообщал об изменениях, происходящих с его свойствами. Интерфейс определяет одно событие PropertyChanged .

С привязкой XAML событие PropertyChanged автоматически подключается, поэтому вам нужно реализовать интерфейс INotifyPropertyChanged для вашей модели представления или классов контекста данных для работы с привязкой XAML.

Внедрение INotifyPropertyChanged в C # 6

Реализация INotifyPropertyChange может быть подвержена ошибкам, так как интерфейс требует указания имени свойства как строки. Чтобы сделать реализацию более надежной, можно использовать атрибут CallerMemberName .

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

INotifyPropertyChanged с помощью метода общего набора

В NotifyPropertyChangedBase ниже классе NotifyPropertyChangedBase определяется общий метод Set, который можно вызвать из любого производного типа.

Чтобы использовать этот общий метод Set, вам просто нужно создать класс, который происходит из NotifyPropertyChangedBase.

Как показано выше, вы можете вызвать Set(ref _fieldName, value); в настройщике свойства, и он автоматически поднимет событие PropertyChanged, если это необходимо.

Затем вы можете зарегистрироваться в событие PropertyChanged из другого класса, который должен обрабатывать изменения свойств.

Быстрый старт с WPF. Часть 1. Привязка, INotifyPropertyChanged и MVVM

По разным причинам большинство из нас использует десктопные приложения, как минимум, браузер 🙂 А у некоторых из нас возникает необходимость в написании своих. В этой статье я хочу пробежаться по процессу разработки несложного десктопного приложения с использованием технологии Windows Presentation Foundation (WPF) и применением паттерна MVVM. Желающих продолжить чтение прошу под кат.

Думаю, необязательно говорить, что WPF — это разработка Microsoft 🙂 Технология эта предназначена для разработки десктопных приложений под Windows, начиная с Windows XP. Почему именно так? Это связано с тем, что WPF работает поверх платформы .NET, минимальные требования которой — Windows XP и новее. К сожалению, WPF не работает на других платформах, хотя есть шансы, что в ближайшее время это изменится: в стадии разработки находится WPF-based фреймворк Avalonia.

В чём особенность WPF?

Два основных отличия WPF от других средств построения десктопных приложений:

  • Язык разметки XAML, предназначенный для разметки самого интерфейса окна.
  • Рендеринг посредством DirectX, аппаратное ускорение графики.

Я не буду углубляться в подробности, т.к. это не совсем тема статьи. Если интересно, то гуглить XAML, WPF rendering, milcore.dll и DirectX 🙂

О чём эта статья?

Эта статья содержит пример приложения, построенного на технологии WPF:

Я постараюсь ориентировать материал статьи в практическую сторону в стиле «повторяй за мной» с пояснениями.

Что нам понадобится для повторения статьи?

Небольшой опыт разработки на C# 🙂 Как минимум, нужно хорошо понимать синтаксис языка. Также понадобится Windows-машина (в примерах будет Win 10) с установленной на ней Visual Studio (в примерах будет 2017, есть бесплатная Community версия). При установке VS необходимо будет включить поддержку десктопной разработки под платформу .NET

Так же в этом разделе я опишу создание проекта.

Запускаем VS, создаём новый проект, тип приложения выбираем WPF App (.NET Framework) (можно ввести в строке поиска справа вверху), называем как угодно.

image

После создания нового проекта откроется окно редактора интерфейса, у меня оно выглядит так

image

Внизу находится редактор разметки, вверху — предпросмотр интерфейса окна, но можно поменять относительное расположение редактора кода и предпросмотра интерфейса так, что они будут располагаться в горизонтальном порядке, с помощью вот этих кнопок (справа на границе двух областей):

image

Перед тем, как начать

Элементы окна (их ещё называют контрОлами от слова Control) должны размещаться внутри контейнера или внутри другого элемента типа ContentControl. Контейнер — это специальный контрол, позволяющий разместить внутри себя несколько дочерних контролов и организовать их взаимное расположение. Примеры контейнеров:

  • Grid — позволяет организовать элементы по столбцам и строкам, ширина каждого столбца или строки настраивается индивидуально.
  • StackPanel — позволяет расположить дочерние элементы в одну строку или столбец.

Есть и другие контейнеры. Поскольку контейнер тоже является контролом, то внутри контейнера могут быть вложенные контейнеры, содержащие вложенные контейнеры и так далее. Это позволяет гибко располагать контролы относительно друг друга. Так же с помощью контейнеров мы можем не менее гибко управлять поведением вложенных контролов при изменении размеров окна.

Читать:
Наименование параметра что это
MVVM и интерфейс INotifyPropertyChanged. Копия текста.

Итогом этого примера станет приложение с двумя контролами, в одном из которых можно редактировать текст, а в другом только просматривать. Изменения из одного в другой будут переходить синхронно без явного копирования текста с помощью привязки (binding).

Итак, у нас есть свежесозданный проект (я назвал его Ex1), перейдём в редактор разметки и первым делом заменим контейнер, указанный по умолчанию (<Grid></Grid>) на <StackPanel></StackPanel>. Этого контейнера будет достаточно, т.к. нам понадобится расположить всего лишь два контрола один над другим. Укажем явно, каким образом будут располагаться компоненты, добавив свойство Orientation=»Vertical». Добавим внутрь стек панели парочку элементов: поле для ввода текста и поле для отображения текста. Поскольку эти контролы не будут содержать вложенного кода, можно описать их самозакрывающимся тегом (см. код ниже). После всех вышеописанных процедур код описания контейнера и вложенных контролов должен принять такой вид:

Теперь сосредоточимся на цели этого примера. Мы хотим, чтобы при наборе текста в текстбоксе этот же текст синхронно отображался в текстблоке, избежав при этом явной операции копирования текста. Нам понадобится некая связующая сущность, и вот тут-то мы и подошли к такой штуке, как привязка (binding), о которой было сказано выше. Привязка в терминологии WPF — это механизм, позволяющий связывать некоторые свойства контролов с некоторыми свойствами объекта C#-класса и выполнять взаимное обновление этих свойств при изменении одной из частей связки (это может работать в одну, в другую или в обе стороны сразу). Для тех, кто знаком с Qt, можно провести аналогию слотов и сигналов. Чтобы не растягивать время, перейдём к коду.

Итак, для организации привязки нужны свойства контролов и некое свойство некоего C#-класса. Для начала разберёмся с XAML-кодом. Текст обоих контролов хранится в свойстве Text, поэтому добавим привязку для этих свойств. Делается это так:

Мы сделали привязку, но пока непонятно к чему 🙂 Нам нужен объект какого-то класса и какое-то свойство в этом объекте, к которому будет выполнена привязка (как ещё говорят, на которое нужно забиндиться).

Так что это за класс? Этот класс называется вьюмоделью (view model) и служит как раз связующим звеном между view (интерфейсом или его частями) и model (моделью, т.е. теми частями кода, которые отвечают за логику приложения. Это позволяет отделить (в какой-то степени) логику приложения от интерфейса (представления, view) и называется паттерном Model-View-ViewModel (MVVM). В рамках WPF этот класс также называется DataContext.

Однако, просто написать класс вьюмодели недостаточно. Нужно ещё как-то оповещать механизм привязки о том, что свойство вьюмодели или свойство вью изменилось. Для этого существует специальный интерфейс INotifyPropertyChanged, который содержит событие PropertyChanged. Реализуем этот интерфейс в рамках базового класса BaseViewModel. В дальнейшем все наши вьюмодели мы будем наследовать от этого базового класса, чтобы не дублировать реализацию интерфейса. Итак, добавим в проект каталог ViewModels, а в этот каталог добавим файл BaseViewModel.cs. Получим такую структуру проекта:

image

Код реализации базовой вьюмодели:

Создадим для нашего класса MainWindow свою вьюмодель, унаследовавшись от базовой. Для этого в том же каталоге ViewModels создадим файл MainWindowViewModel.cs, внутри которого будет такой код:

Шикарно! Теперь нужно добавить в эту вьюмодель свойство, на которое будем биндить текст наших контролов. Поскольку это текст, тип этого свойства должен быть string:

В итоге получим такой код

Так, кажется, справились. Осталось забиндиться на это свойство из вьюхи и готово. Давайте сделаем это прямо сейчас:

Ништяк, запускаем проект, набираем текст в текстбокс иииии… ничего не происходит))) Ну, ничего страшного, на самом деле мы идём правильной дорогой, просто пока ещё не дошли до нужной точки.

Предлагаю на минутку остановиться и подумать, чего же нам не хватает. Вьюха у нас есть. Вьюмодель тоже. Свойства вроде забиндили. Нужный интерфейс реализовали. Проделали кучу работы ради копирования жалкой строчки текста, за что нам это. 111

Ладно, шутки в сторону. Мы забыли создать объект вьюмодели и кое-что ещё (об этом позже). Сам класс мы описали, но это ничего не значит, ведь у нас нет объектов этого класса. Ок, где нужно хранить ссылку на этот объект? Ближе к началу примера я упомянул некий DataContext, используемый в WPF. Так вот, у любой вью есть свойство DataContext, которому мы можем присвоить ссылку на нашу вьюмодель. Сделаем это. Для этого откроем файл MainWindow.xaml и нажмём F7, чтобы открыть код этой вьюхи. Он практически пустой, в нём есть только конструктор класса окна. Добавим в него создание нашей вьюмодели и поместим её в DataContext окна (не забываем добавить using с нужным неймспейсом):

Это было просто, но этого всё равно не хватает. По-прежнему при запуске приложения никакой синхронизации текста не происходит. Что ещё нужно сделать?

Нужно вызвать событие PropertyChanged при изменении свойства SynchronizedText и сообщить вьюхе о том, что она должна следить за этим событием. Итак, чтобы вызвать событие, модифицируем код вьюмодели:

Что мы тут сделали? Добавили скрытое поле для хранения текста, обернули его в уже существующее свойство, а при изменении этого свойства не только меняем скрытое поле, но и вызываем метод OnPropertyChanged, определённый в базовой вьюмодели и вызывающий событие PropertyChanged, объявленное в интерфейсе INotifyPropertyChanged, так же реализованное в базовой вьюмодели. Получается, что при каждом изменении текста возникает событие PropertyChanged, которому передаётся имя свойства вьюмодели, которое было изменено.

Ну, почти всё, финишная прямая! Осталось указать вьюхе, что оно должно слушать событие PropertyChanged:

Помимо того, что мы указали, по какому триггеру должно происходить обновление, мы так же указали, в какую сторону это обновление отслеживается: от вью к вьюмодели или наоборот. Поскольку в текстбоксе мы вводим текст, то нам интересны изменения только во вью, поэтому выбираем режим OneWayToSource. В случае с текстблоком всё ровно наоборот: нам интересны изменения во вьюмодели, чтобы отобразить их во вью, поэтому выбираем режим OneWay. Если бы нам нужно было, чтобы изменения отслеживались в обе стороны, можно было не указывать Mode вообще, либо указать TwoWay явно.

Итак, запускаем программу, набираем текст и voi-la! Текст синхронно меняется, и мы нигде ничего не копировали!

image

Спасибо за внимание, продолжение следует. Будем разбираться с DataTemplate и паттерном Command.

Похожие статьи