INotifyPropertyChanged vs. DependencyProperty у ViewModel


353

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

Моє запитання - коли я віддаю перевагу одному перед іншим? Чи є різниці в продуктивності? Це дійсно гарна ідея надати залежності від ViewModel WPF? Що ще потрібно враховувати, коли приймаю дизайнерське рішення?


11
см stackoverflow.com/questions/1329138 / ... для компілятора перевіреного шляху реалізації INotifyPropertyChanged. Уникайте того, щоб назви властивостей були магічними рядками.
Ян Рінроуз

10
Як правило, існує велика різниця між властивістю залежності і звичайною властивістю в класі, який реалізує INotifyPropertyChanged. Властивості залежності можуть бути джерелом або ціллю у прив'язці даних, але звичайні властивості з підтримкою INotifyPropertyChanged можуть використовуватися лише як джерело. Тож ці рішення не є взаємозамінними. Інфраструктура зв’язування даних вимагає використання DP як цілі, але джерелом може бути або звичайна властивість з підтримкою INotifyPropertyChanged, або загальна DP.
Мостафа Резай

4
Дивіться stackoverflow.com/a/10595688/200442 щодо способу реалізації .net 4.5 INotifyPropertyChanged.
Даніель Літтл

найкраще пояснено тут stackoverflow.com/a/3552550/366064
Біжан

Відповіді:


214

Кент написав цікавий блог на цю тему: Перегляд моделей: POCOs проти DependencyObjects .

Короткий підсумок:

  1. DependencyObjects не позначаються як серіалізаційні
  2. Клас DependencyObject перекриває і запечатує методи Equals () та GetHashCode ()
  3. Об'єкт DependencyObject має спорідненість до потоку - до нього можна отримати доступ лише в тій нитці, на якій він створений

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


24
DependencyObject також приймає залежність від бібліотек WPF, тоді як POCO не дозволяє, дозволяючи моделям перегляду управляти деякими іншими стеками інтерфейсу, де WPF недоступний (Compact Framework, Mono).
codekaizen

26
Тоді зрозуміло, що властивості Dependecy побудовані виключно для інтерфейсу користувача, а не для бізнес-рівня.
Андрій Ронеа

11
Властивості Dependancy також вимагають від батьків-факторів DependencyObject. Ваш ViewModel дійсно не повинен успадковувати DependencyObject.
Гусдор

38

Відповідно до посібника з продуктивності WPF, DependencyObjects безумовно краще, ніж POCO, які реалізують INotifyPropertyChanged:

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


1
Я повинен погодитись із цим ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/…
Джоната

Якщо ви вибрали .NET Framework версії 4, посилання все ще працює. Він просто недоступний для "поточної версії".
подвійно Ти

Дякуємо, що вказали на це, є багато скандальної дезінформації там, деви, які заявляють про нахабні твердження, що INotifyPropertyChanged швидше або вимагає менших накладних витрат, ніж DP, і це просто необгрунтовано. DP - це швидкі, елегантні та потужні способи структурного визначення віртуального дерева (даних).
tpartee

Існує приховане зло для Об'єктів Залежності. Їх потрібно створити на тій самій нитці, що й елементи керування, що їх пов’язують. Це означає, що GUI-потік. Це означає, що вам потрібно відправити створення до цього потоку. Наприклад, ви не можете завантажувати та створювати ці речі у фоновому потоці з БД. Якщо ви не відправляєте створення. Божевільний
ed22

28

Вибір повністю ґрунтується на вашій бізнес-логіці та рівні абстракції інтерфейсу користувача. Якщо ви не хочете гарної розлуки, то DP працюватиме на вас.

DependencyProperties застосовуватимуться головним чином на рівні VisualElements, тому це буде недоброю ідеєю, якщо ми створимо безліч ДП для кожного нашого бізнесу. Також існує більша вартість для DP, ніж INotifyPropertyChanged. Коли ви розробляєте WPF / Silverlight, спробуйте розробити інтерфейс користувача та ViewModel повністю розділеними, щоб у будь-який момент часу ми могли змінити елементи макета та інтерфейсу користувача (на основі теми та стилів)

Перегляньте також цю публікацію - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Посилання має багато посилань на модель Model-View-ViewModel, що дуже стосується цієї дискусії.


9
Допис від jbe відповідає точнішим відмінностям. Тільки тому, що VM (або Presenter) успадковується від DependencyObject не означає, що його не можна стилізувати або не логічно відокремлювати від представлення, це просто означає, що сховище для значень властивості відрізняється від явно оголошених полів у полі Стиль POCO Незважаючи на це, серіалізація, логічна рівність та спорідненість ниток - це реальні проблеми, з якими доводиться вирішувати VM на основі DepedencyObject.
micahtan

"Також існує більша вартість для DP, ніж INotifyPropertyChanged" - де ваше джерело доказів щодо цього? Дуже багато дияволів висловлюють цю претензію без будь-яких доказів, які підтверджують її. На думку MSDN, це неправда. "спробуйте спроектувати інтерфейс користувача та ViewModel повністю роздільно, так що в будь-який момент часу ми можемо змінити елементи макета та інтерфейсу користувача" - знову ж таки, це абсолютно не має нічого спільного з POCO + PropChange проти DO / DP. Якщо що-небудь, реєстр відображення та шляху в DO / DP покращує вашу здатність працювати з візуальної сторони.
tpartee

20

З точки зору виразності, я все одно насолоджуюся використанням властивостей залежності та придумкою при думці INotifyPropertyChanged. Крім stringімен властивостей та можливих витоків пам'яті через підписку на подію, INotifyPropertyChangedце набагато більш чіткий механізм.

Властивості залежності мають на увазі "коли це зробити", використовуючи легко зрозумілі статичні метадані. Це декларативний підхід, який отримує мій голос за елегантність.


1
Тепер рядкова частина має рішення з оператором nameof.
Ньютопський

@Newtopian: Правда. Також можливі цікаві речі [CallerMemberName].
Брайан Уоттс

Не кажучи вже про багатство переваг реєстрації власності (відображення) у WPF та CLR при використанні DO / DP моделі порівняно з POCO.
tpartee

16

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 ); }
}

У вашому getter та setter --- все, що ви можете зробити, це просто зателефонувати SetValue та GetValue відповідно, b / c в інших частинах структури, getter / setter не називається, замість цього він безпосередньо викликає 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...

Це може бути у геттера / сетера чи будь-де ще.


11
Ви можете отримувати сповіщення про зміни також від DependencyProperties. Див. Розділ PropertyMetadata.PropertyChangedCallback. Приклад за адресою: msdn.microsoft.com/en-us/library/ms745795.aspx
Джо Уайт

2
Також ви можете зателефонувати на SetValue з будь-якого місця, а не лише зсередини готелю
aL3891

Це вводить в оману та неправдиво - існує кілька способів підключити зміни подій на DP, навіть якщо він змінений "внутрішньо". На одну з них звернув увагу Джо Уайт
товариш

16

Властивості залежності покликані підтримувати прив'язку (як цільову) для елементів інтерфейсу, а не як джерело для прив'язки даних, саме тут надходить 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


Я без проблем використовую серіалізовані об'єкти залежності з 2009 року, тому не впевнений, про що ви говорите, коли ви говорите "Усі об'єкти залежності не можна серіалізувати" - так, вони можуть. Насправді існує багато варіантів: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization І один із моїх особистих улюблених: просто надайте резервні магазини для всіх своїх DP-служб і зробіть ці серіалізаційні ( жодних хороших простих прикладів не було легко за два хвилини пошуку в Google, але я запевняю, що це працює).
tpartee

7

Я теж повинен був нещодавно розглянути це рішення.

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

Завдяки DP ви не можете самостійно визначити, що зберігає стан. Мені довелося б дозволити .net кешувати копію кожного елемента стану, до якого я зобов'язуюсь. Це здавалося зайвим накладними - мій стан великий і складний.

Тож тут я виявив INotifyPropertyChanged краще для викриття властивостей з бізнес-логіки на GUI.

Зважаючи на те, що мені потрібен користувальницький віджет графічного інтерфейсу, щоб відкрити властивість, а зміни в цьому властивості впливати на інші віджети GUI DP виявилося простим рішенням.

Тож я знайшов DP корисним для GUI для GUI-сповіщення.


6

Це дійсно гарна ідея надати залежності від ViewModel WPF?

.NET 4.0 матиме System.Xaml.dll, тому вам не доведеться брати залежність від довільної бази, щоб використовувати її. Дивіться публікацію Роб Реліе про його сесію PDC.

Моє взяти

XAML - це мова для опису об'єктів, а WPF - це структура, описані об'єкти - елементи інтерфейсу.

Їх взаємозв'язок схожий на C #, мову опису логіки, і .NET, основу, яка реалізує певні види логіки.

Призначення XAML - графічні об'єктивні графіки. Технології W * F є чудовими кандидатами для цієї парадигми, але XAML існує незалежно від них.

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


Відповідаючи, ви, здається, робите припущення, що бітбонк вважає XAML і WPF однаковими. ViewModels повинні мати якомога менше залежностей WPF, щоб не збільшити логічне розділення, а зменшити складність коду та уникнути всіх проблем, пов’язаних із простою написанням логіки у кодовому контролі користувача. Ви неминуче будете реалізовувати такі концепції WPF, як ICommand та представляти поведінку, що лише WPF / Silverlight зможете легко перетворити її - вашою єдиною проблемою, що стосується потоку презентацій у моделі перегляду, повинні бути CollectionViews та ObservableCollection.
Гусдор

6

Властивості залежності - це клей створення користувацького контролю. Якщо вам цікаво використовувати Intelli-sense для показу своїх властивостей у вікні властивостей на час проектування XAML, ви повинні використовувати властивості Dependency. INPC ніколи не відображатиме властивість у вікні власності під час проектування.


4

Здається, що властивості Dependency слід використовувати в створених вами елементах управління, таких як кнопки. Щоб використовувати властивості в XAML та використовувати всі функції WPF, ці властивості повинні мати властивості Dependency Properties.

Однак ваш ViewModel краще використовувати INotifyPropertyChanged. Використання INotifyPropertyChanged надасть вам можливість мати логіку getter / setter, якщо потрібно.

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

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

Я думаю, що це відмінний приклад того, як зробити ViewModel.


4

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


2
INotifyPropertyChanged можна використовувати в будь-якому випадку. Ви можете прив’язати до нього TwoWay. Властивість DependencyPress потрібна з технічних причин лише для деяких дій, що виконуються на об’єкті View (наприклад, встановлення деяких властивостей при інстанціюванні об’єкта View у XAML). Властивість Dependency ніколи не потрібна для ViewModel.
oillio

3

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


Без INotifyPropertyChanged, PropertyDescriptorвикористовуються, що спричиняє витік пам'яті
Тилак

Бібліотека Update Controls, яку я представляю в цій публікації блогу, використовує слабкі посилання, а не дескриптори властивостей. Це не протікає пам'ять.
Майкл Л Перрі

1
Майкл, ваша бібліотека генерує багато коду. Я не бачу переваг. Я можу досягти цього, генеруючи обгортку моделі з генерованими викликами подій PropertyChanged.
Der_Meister

3

Є лише одне, чому віддати перевагу DependencyObject- В’язка працюватиме краще. Просто спробуйте приклад із ListBoxта TextBox, заповніть список із даними INotifyPropertyChangedвласності DependencyPropertyта відредагуйте поточний елемент із TextBox...


1
Зразок коду, будь ласка
Хасан Тарек

1

Якщо ви хочете виставити властивості іншим елементам управління, ви повинні використовувати властивості Dependpendation ... Але удачі, тому що вони потребують певного часу, щоб з'ясувати ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.