INotifyPropertyChanged против DependencyProperty в ViewModel

При реализации ViewModel в приложении WPF с архитектурой Model-View-ViewModel, кажется, есть два основных варианта, как сделать его доступным для привязки к данным. Я видел реализации, которые используют DependencyProperty для свойств, с которыми View будет связываться, и я видел, как ViewModel вместо этого реализует INotifyPropertyChanged.

Мой вопрос в том, когда я должен предпочесть одно другому? Есть ли разница в производительности? Действительно ли стоит передавать зависимости ViewModel в WPF? Что еще нужно учитывать при принятии дизайнерского решения?


person bitbonk    schedule 14.11.2008    source источник
comment
см. stackoverflow.com/questions/1329138/ для проверенного компилятором способа реализации INotifyPropertyChanged. Избегайте использования имен свойств в виде волшебной строки.   -  person Ian Ringrose    schedule 26.08.2009
comment
Обычно существует большая разница между свойством зависимости и обычным свойством в классе, реализующем INotifyPropertyChanged. Свойства зависимости могут быть источником или целью в привязке данных, но обычные свойства с поддержкой INotifyPropertyChanged могут использоваться только как источник. Так что эти решения не полностью взаимозаменяемы. Инфраструктура привязки данных требует для работы DP в качестве цели, но источником может быть либо обычное свойство с поддержкой INotifyPropertyChanged, либо общий DP.   -  person Mostafa Rezaei    schedule 26.08.2011
comment
См. stackoverflow.com/a/10595688/200442, чтобы узнать о способе реализации INotifyPropertyChanged .net 4.5.   -  person Daniel Little    schedule 17.09.2012
comment
лучше всего объясняется здесь stackoverflow.com/a/3552550/366064   -  person Bizhan    schedule 16.10.2017


Ответы (14)


Кент написал интересный блог на эту тему: Модели просмотра: POCOs против DependencyObjects .

Краткое резюме:

  1. DependencyObjects не помечены как сериализуемые
  2. Класс DependencyObject переопределяет и запечатывает методы Equals () и GetHashCode ().
  3. DependencyObject имеет сходство с потоком - к нему можно получить доступ только в потоке, в котором он был создан.

Я предпочитаю подход POCO. Базовый класс для PresentationModel (он же ViewModel), который реализует интерфейс INotifyPropertyChanged, можно найти здесь: http://compositeextensions.codeplex.com

person jbe    schedule 23.04.2009
comment
DependencyObject также зависит от библиотек WPF, тогда как POCO этого не делает, позволяя вашим моделям представления управлять некоторым другим стеком пользовательского интерфейса, где WPF недоступен (Compact Framework, Mono). - person codekaizen; 06.01.2010
comment
Тогда ясно, что свойства зависимости созданы исключительно для пользовательского интерфейса, а не для бизнес-уровня. - person Andrei Rînea; 04.05.2010
comment
Для свойств зависимостей также требуется родительский объект DependencyObject. Ваша ViewModel действительно не должна наследовать от DependencyObject. - person Gusdor; 13.06.2011

Согласно руководству по производительности WPF, DependencyObjects определенно работает лучше, чем POCO, которые реализуют INotifyPropertyChanged:

http://msdn.microsoft.com/en-us/library/bb613546.aspx

person James Ashley    schedule 03.11.2010
comment
Я должен согласиться с этим ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/ - person Jonathan ANTOINE; 21.04.2011
comment
Если вы выберете .NET Framework версии 4, ссылка будет по-прежнему работать. Это просто недоступно для текущей версии. - person doubleYou; 17.03.2016
comment
Спасибо, что указали на это, существует множество скандальной дезинформации от разработчиков, делающих непристойные заявления о том, что INotifyPropertyChanged быстрее или требует меньше накладных расходов, чем DP, и это просто необоснованно. DP - это быстрые, элегантные и эффективные способы структурного определения виртуального дерева (данных). - person tpartee; 06.07.2017
comment
В DependencyObjects есть скрытое зло. Их нужно создавать в том же потоке, что и связанные с ними элементы управления. Это означает поток графического интерфейса. Это означает, что вам нужно отправить создание в этот поток. Вы не можете загрузить и создать эти вещи, например, в каком-то фоновом потоке из БД. Если только вы не отправите создание. Безумный. - person ed22; 08.04.2019

Выбор полностью зависит от вашей бизнес-логики и уровня абстракции пользовательского интерфейса. Если вы не хотите хорошего разлуки, DP подойдет вам.

DependencyProperties будет применяться в основном на уровне VisualElements, поэтому было бы не лучшим решением создавать множество DP для каждого из наших бизнес-требований. Кроме того, DP дороже, чем INotifyPropertyChanged. Когда вы разрабатываете WPF / Silverlight, постарайтесь полностью разделить UI и ViewModel, чтобы в любой момент мы могли изменить элементы управления Layout и UI (на основе темы и стилей).

См. Также этот пост - https://stackoverflow.com/questions/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel. Ссылка содержит много ссылок на шаблон Model-View-ViewModel, который очень важен для этого обсуждения.

person Jobi Joy    schedule 14.11.2008
comment
Сообщение jbe более точно отвечает на различия. Тот факт, что виртуальная машина (или презентатор) наследуется от DependencyObject, не означает, что она не может быть стилизована или логически не отделена от представления, это просто означает, что хранилище для значений свойств отличается от явно объявленных полей в POCO стиль. При этом сериализация, логическое равенство и сходство потоков - это реальные проблемы, с которыми приходится иметь дело виртуальным машинам на основе DepedencyObject. - person micahtan; 25.05.2009
comment
Также есть более высокая стоимость DP, чем INotifyPropertyChanged - где ваш источник доказательства по этому поводу? Многие разработчики заявляют об этом без каких-либо доказательств. Согласно MSDN, это неправда. попробуйте спроектировать UI и ViewModel полностью отдельно, чтобы в любой момент мы могли изменить элементы управления Layout и UI - опять же, это не имеет абсолютно никакого отношения к POCO + PropChange по сравнению с DO / DP. Во всяком случае, реестр отражений и путей в DO / DP улучшает вашу способность работать с визуальной стороной. - person tpartee; 06.07.2017

С точки зрения выразительности, мне очень нравится использовать свойства зависимостей, и я съеживаюсь при мысли о INotifyPropertyChanged. Помимо имен свойств string и возможных утечек памяти из-за подписки на события, INotifyPropertyChanged является гораздо более явным механизмом.

Свойства зависимостей подразумевают «когда это, то делай» с использованием легко понятных статических метаданных. Это декларативный подход, который заслуживает моего голоса за элегантность.

person Bryan Watts    schedule 15.11.2008
comment
У строковой части теперь есть решение с оператором nameof. - person Newtopian; 30.05.2017
comment
@Newtopian: Верно. С [CallerMemberName] также возможны некоторые интересные вещи. - person Bryan Watts; 31.05.2017
comment
Не говоря уже о множестве преимуществ регистрации собственности (отражения) в WPF и CLR при использовании модели DO / DP по сравнению с POCO. - person tpartee; 06.07.2017

Свойства зависимости предназначены для поддержки привязки (в качестве цели) к элементам пользовательского интерфейса, а не к источнику привязки данных, именно здесь на помощь приходит INotifyProperty. С чистой точки зрения вы не должны использовать DP в ViewModels.

"Чтобы быть источником привязки, свойство не обязательно должно быть свойством зависимости; вы можете использовать любое свойство CLR в качестве источника привязки. Однако, чтобы быть целью привязки, свойство должно быть Свойство зависимости. Чтобы односторонняя или двусторонняя привязка была эффективной, свойство источника должно поддерживать уведомления об изменениях, которые распространяются на систему привязки и, следовательно, на цель. Для настраиваемых источников привязки CLR это означает, что свойство должно поддерживать INotifyPropertyChanged. Коллекции должны поддерживать INotifyCollectionChanged. "

Все объекты зависимостей не могут быть сериализованы (это может помешать использованию ViewModels и DTO (POCO).

Между DP в Silverlight и WPF есть отличия.

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx.

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx

person Nick Hermans    schedule 09.11.2011
comment
Я без проблем использую сериализованные объекты зависимостей с 2009 года, поэтому не уверен, о чем вы говорите, когда говорите, что все объекты зависимостей не могут быть сериализованы - да, могут. На самом деле существует множество вариантов: codeproject.com/Articles/61440 / emphess.net/2008/11/25/dependencyproperty- сериализация И один из моих личных фаворитов: просто создайте резервные хранилища для всех ваших DP и сделайте их сериализуемыми (за 2 минуты поиска в Google не было легко доступных хороших простых примеров, но я уверяю вас, что это работает). - person tpartee; 06.07.2017

INotifyPropertyChanged при использовании также дает вам возможность добавить больше логики в код ваших методов получения и установки ваших свойств.

DependencyProperty пример:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

В вашем геттере и сеттере - все, что вы можете сделать, это просто вызвать SetValue и GetValue соответственно, b / c в других частях фреймворка геттер / сеттер не вызывается, вместо этого он напрямую вызывает SetValue, GetValue, поэтому ваша логика свойств не будет надежно исполниться.

С помощью INotifyPropertyChanged определите событие:

public event PropertyChangedEventHandler PropertyChanged;

А затем просто имейте любую логику в любом месте вашего кода, затем вызовите:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Это может быть геттер / сеттер или где-нибудь еще.

person Adam    schedule 13.02.2009
comment
Вы также можете получать уведомления об изменениях из DependencyProperties. См. PropertyMetadata.PropertyChangedCallback. Пример: msdn.microsoft.com/en-us/library/ms745795.aspx - person Joe White; 25.04.2009
comment
Кроме того, вы можете вызывать SetValue из любого места, а не только изнутри свойства. - person aL3891; 06.05.2011
comment
Это вводит в заблуждение и не соответствует действительности - существует несколько способов подключиться к событиям изменения в DP, даже если он изменен «внутренне». Один из них был отмечен выше Джо Уайтом. - person tpartee; 06.07.2017

Это действительно хорошая идея передать зависимости ViewModel в WPF?

.NET 4.0 будет иметь System.Xaml.dll, поэтому вам не придется зависеть от произвольной структуры, чтобы использовать его. См. сообщение Роба Рельеа о его сеансе PDC.

Мое мнение

XAML - это язык для описания объектов, а WPF - это платформа, описанные объекты которой являются элементами пользовательского интерфейса.

Их отношения аналогичны C #, языку описания логики, и .NET, структуре, реализующей определенные виды логики.

Назначение XAML - декларативные графы объектов. Технологии W * F - отличные кандидаты для этой парадигмы, но XAML существует независимо от них.

XAML и вся система зависимостей были реализованы как отдельные стеки для WF и WPF, вероятно, чтобы использовать опыт разных команд без создания зависимости (без каламбура) между ними.

person Bryan Watts    schedule 14.11.2008
comment
Отвечая на этот вопрос, вы, кажется, делаете предположение, что bitbonk считает XAML и WPF одним и тем же. ViewModels должен иметь как можно меньше зависимостей WPF, не для увеличения логического разделения, а для уменьшения сложности кода и избежания всех проблем, связанных с простым написанием логики в коде программной части пользовательского элемента управления. Вы неизбежно реализуете концепции WPF, такие как ICommand, и представите поведение, которое только WPF / Silverlight сможет легко обернуть - единственными проблемами потоковой передачи презентации в модели представления должны быть CollectionViews и ObservableCollection. - person Gusdor; 13.06.2011

Мне тоже недавно пришлось обдумывать это решение.

Я обнаружил, что механизм INotifyPropertyChanged лучше соответствует моим потребностям, поскольку он позволяет мне приклеивать мой графический интерфейс к существующей структуре бизнес-логики без дублирования состояния. Фреймворк, который я использовал, имел свой собственный шаблон наблюдателя, и было легко перенаправить один уровень уведомления на следующий. У меня просто был класс, который реализовал интерфейс наблюдателя из моей структуры бизнес-логики и интерфейс INotifyPropertyChanged.

С DP вы не можете определить серверную часть, которая хранит состояние самостоятельно. Мне пришлось бы разрешить .net кэшировать копию каждого элемента состояния, к которому я привязан. Это выглядело как ненужные накладные расходы - мое состояние большое и сложное.

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

Как уже говорилось, когда мне нужен был настраиваемый виджет графического интерфейса пользователя, чтобы раскрыть свойство и чтобы изменения этого свойства влияли на другие виджеты графического интерфейса, DP доказал простое решение.

Итак, я обнаружил, что DP полезен для уведомлений от графического интерфейса пользователя к графическому интерфейсу.

person morechilli    schedule 24.11.2008

Свойства зависимостей являются основой создания настраиваемого элемента управления. Если вы заинтересованы в использовании Intelli-sense для отображения ваших свойств в окне свойств во время разработки XAML, вы должны использовать свойства Dependency. INPC никогда не будет отображать свойство в окне свойств во время разработки.

person JWP    schedule 28.10.2014

Кажется, что свойства зависимостей следует использовать в элементах управления, которые вы создаете, например в кнопках. Чтобы использовать свойства в XAML и использовать все функции WPF, эти свойства должны быть Dependency Properties.

Однако для вашей ViewModel лучше использовать INotifyPropertyChanged. Использование INotifyPropertyChanged даст вам возможность иметь логику получения / установки, если вам нужно.

Я рекомендую проверить версию базового класса Джоша Смита для ViewModel, которая уже реализует INotifyPropertyChanged:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

Я думаю, что это отличный пример того, как создать ViewModel.

person timothymcgrath    schedule 25.03.2009

Я думаю, что DependencyProperty и INotifyPropertyChanged используются в Binding для двух разных целей: первая для включения свойства в качестве цели привязки и получения входных данных от другого свойства (используйте {Binding ...} для установки свойства), а последняя когда вы хотите, чтобы значение свойства использовалось в качестве источника привязки (имя в выражении пути привязки). Так что выбор чисто технический.

person Domnik    schedule 02.09.2010
comment
В любом случае можно использовать INotifyPropertyChanged. Вы можете привязать к нему TwoWay. DependencyProperty требуется по техническим причинам только для некоторых действий, выполняемых с объектом View (например, установка некоторых свойств при создании экземпляра объекта View в XAML). DependencyProperty никогда не требуется для ViewModel. - person oillio; 01.02.2011

Я предпочитаю более прямой подход, о котором я писал в блоге Модель представления без INotifyPropertyChanged. Используя альтернативу привязке данных, вы можете выполнять привязку непосредственно к свойствам среды CLR без какого-либо бухгалтерского кода. Вы просто пишете простой старый код .NET в своей модели представления, и он обновляется при изменении модели данных.

person Michael L Perry    schedule 30.12.2008
comment
Без INotifyPropertyChanged используются PropertyDescriptor, что вызывает утечки памяти - person Tilak; 15.03.2013
comment
Библиотека Update Controls, которую я представляю в этом сообщении блога, использует слабые ссылки, а не дескрипторы свойств. Не вызывает утечки памяти. - person Michael L Perry; 09.04.2014
comment
Майкл, ваша библиотека генерирует много кода. Я не вижу пользы. Я могу добиться того же, создав оболочку модели с сгенерированными вызовами событий PropertyChanged. - person Der_Meister; 01.01.2016

Есть только одна вещь, почему предпочесть DependencyObject - привязка будет работать лучше. Просто попробуйте пример с ListBox и TextBox, заполните список данными из свойства INotifyPropertyChanged по сравнению с DependencyProperty и отредактируйте текущий элемент из _6 _...

person ramos    schedule 27.03.2011
comment
Пример кода, пожалуйста - person Hassan Tareq; 26.12.2017

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

person JWP    schedule 03.02.2011