Чи повинно ViewModel або Model впроваджувати INotifyPropertyChanged у MVVM?


165

Більшість прикладів MVVM я працював через мали модель реалізації INotifyPropertyChanged, але в прикладі CommandSink Джоша Сміта знаряддя ViewModelINotifyPropertyChanged .

Я все ще пізнавально складаю концепції MVVM, тож не знаю, чи:

  • Ви повинні помістити INotifyPropertyChangedв ViewModel, щоб приступити CommandSinkдо роботи
  • Це лише аберація норми, і це насправді не має значення
  • У вас завжди має бути реалізація Model, INotifyPropertyChangedі це лише помилка, яка буде виправлена, якби це було розроблено з прикладу коду до програми

Яким був досвід інших щодо проектів MVVM, над якими ви працювали?


4
якщо ви реалізуєте INPC, спробуйте github.com/Fody/PropertyChanged - це заощадить вам тижні друку.
CAD заблокували

Відповіді:


104

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

Я впевнений, що інші не погоджуються, але саме так я працюю.


84
Що робити, якщо властивість змінюється в моделі? Вам потрібно якось дістати його до перегляду-моделі. Чесне запитання, я зараз маю справу з цією головоломкою.
Роджер Ліпскомб

4
EventAggregator в коді Prism є хорошою альтернативою INotifyPropertyChanged на моделі, із спеціальним властивістю, що змінив тип події. Код події в цьому проекті підтримує події переадресації між потоками фону та інтерфейсу користувача, що іноді може бути проблемою.
Стів Мітхам

51
INotifyProperyChanged НЕ WPF конкретно, він живе в просторі імен System.ComponentModel, я використовував його в WinForms додатків, а також INotifyPropertyChanged було в .NET , починаючи з 2.0, WPF тільки навколо з 3,0
benPearce

40
Я прихильник розміщення INotifyPropertyChanged і в МОДЕЛІ, і в VIEWMODEL. Я не можу придумати причину, щоб цього не зробити. Це елегантний спосіб інформування VIEWMODEL про те, коли у вашому РЕЖІВ відбулися фонові зміни, які впливають на VIEWMODEL так само, як його використовували для інформування про VIEW і зміни в VIEWMODEL.
ScottCher

6
@Steve - про інформування ViewModel про те, що властивість моделі змінилося, схоже, INotifyPropertyChanged працює чудово, як "подія, в яку може вступити viewmodel". Чому б не використовувати його?
skybluecodeflier

146

Я категорично не згоден з концепцією того, що Модель не повинна реалізовувати цю програму INotifyPropertyChanged. Цей інтерфейс не призначений для інтерфейсу! Він просто повідомляє про зміну. Дійсно, WPF активно використовує це для виявлення змін, але це не означає, що це інтерфейс інтерфейсу. Я порівняв би це із наступним коментарем: " Шина - автомобільний аксесуар ". Звичайно, але велосипеди, автобуси тощо користуються цим. Підсумовуючи це, не сприймайте цей інтерфейс як предмет інтерфейсу.

Сказавши це, це не обов'язково означає, що я вважаю, що Модель повинна надавати сповіщення. Насправді, як правило, модель не повинна реалізовувати цей інтерфейс, якщо це не потрібно. У більшості випадків, коли дані про сервер не передаються клієнтській програмі, модель може бути несвіжою. Але якщо слухати дані фінансового ринку, то я не бачу, чому модель не може реалізувати інтерфейс. Як приклад, що робити, якщо у мене є логіка, що не користується інтерфейсом користувача, наприклад, послуга, що коли вона отримує ціну або пропозицію за задане значення, вона надсилає попередження (наприклад, електронною поштою) або розміщує замовлення? Це може бути можливим чистим рішенням.

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

Що краще? Визначення подій колекції чи зміни властивості моделі перегляду та їх поширення до моделі чи перегляд власне оновлення моделі (через модель перегляду)?

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

Це дійсно залежить від вашого випадку, і насправді MVVM - це рамка з безліччю питань, і я ще не бачу спільної реалізації MVVM в усьому світі.

Мені б хотілося, щоб у мене було більше часу для пояснення багатьох ароматів MVVM та деяких варіантів вирішення загальних проблем - в основному, це надано іншими розробниками, але, мабуть, мені доведеться це зробити в інший раз.


7
Думай про це так. Якщо ви, як розробник, споживаєте .dll з Моделями у вас, звичайно, не будете переписувати їх для підтримки INotifyPropertyChanged.
Lee Treveil

2
Сильно погоджуюся з вами. Як і я, вам може бути приємно дізнатись, що офіційна документація MVVM < msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx > (розділ "Модель") погоджується з нами. :-)
Нолдорін

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

1
INotifyPropertyChangedє частиною System.ComponentModelпростору імен, яка призначена для " поведінки компонентів та елементів керування під час виконання та проектування ". НЕ ВИКОРИСТОВУЙТЕ INotifyPropertyChanged в моделях, просто в ViewModels. Посилання на документи: docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Ігор Попов

1
Старий пост, я знаю, але я часто повертаюся до нього, коли починаю новий проект, використовуючи MVVM. Нещодавно я почав більш чітко виконувати Принцип єдиної відповідальності. Модель - мати одну відповідальність. Щоб бути моделлю. Як тільки ви додасте INotifyPropertyChanged до моделі, вона більше не дотримується принципу єдиної відповідальності. Вся причина у ViewModel полягає в тому, щоб модель була такою моделлю, щоб модель несла єдину відповідальність.
Rhyous

30

У MV-VM реалізується ViewModel завжди (Модель не завжди) INotifyPropertyChanged

Перегляньте шаблон / інструментарій проекту MV-VM від http://blogs.msdn.com/llobo/archive/2009/05/01/download-mv-vm-project-template-toolkit.aspx . Він використовує DelegateCommandдля командування, і це повинен бути чудовим стартовим шаблоном для ваших проектів MV-VM.


Це перше речення досить добре підсумовує думки інших відповідей, imo. => ПОВЕРНУТИСЯ!
j00hi

13

Я думаю, що MVVM дуже погано названий, а виклик ViewModel ViewModel змушує багатьох пропустити важливу особливість добре розробленої архітектури, а саме DataController, який контролює дані, незалежно від того, хто намагається їх торкнутися.

Якщо ви вважаєте модель View як більшу частину DataController і використовуєте архітектуру, де ваш DataController є єдиним елементом, який стосується даних, ви ніколи не торкаєтесь даних безпосередньо, а завжди використовуєте DataController. DataController корисний для інтерфейсу користувача, але не обов'язково лише для інтерфейсу користувача. Він призначений для бізнес-шару, шару інтерфейсу користувача тощо.

DataModel -------- DataController ------ View
                  /
Business --------/

Ви закінчуєте таку модель. Навіть бізнес повинен торкатися даних лише за допомогою ViewModel. Тоді ваша головоломка просто відходить.


3
Це чудово, якщо ваші дані змінюються лише тоді, коли DataController їх змінює. Якщо дані надходять із бази даних чи іншої сховища даних, яка може забезпечити інший шлях для змін, можливо, вам знадобиться спосіб інформувати про VIEWMODEL (DataController у вашому шаблоні) та VIEW, коли це сталося. Ви можете або опитати за допомогою DataController, або перейти від якогось зовнішнього процесу до вашої DataModel і дозволити вашій DataModel надсилати сповіщення про зміни на ваш DataController.
ScottCher

4
Ви абсолютно праві. Дизайнерські малюнки дуже високого рівня. Більшу частину часу модель дизайну приводить вас до того, щоб робити все правильно, але раз у раз вони перетворюють вас не так. Ніколи не слід уникати того, щоб робити щось правильно, оскільки це поза вашим дизайном.
Rhyous

Ви також будете натискати на свій DataController, оскільки він контролює та модель даних, і наказати йому оновити.
Rhyous

Крім того, модель в MVVM повинна дотримуватися конкретного інтерфейсу (наприклад, DTO). Отже, будь-яка БД або складна бізнес-логіка повинна відбуватися в іншому шарі, і тоді грубі дані повинні надаватися через модель перегляду.
Кодове ім’я Джек

9

Це залежить від того, як ви реалізували вашу модель. Моя компанія використовує бізнес-об’єкти, схожі на CSLA-об'єкти Lhotka та широко використовує INotifyPropertyChangedвсю бізнес-модель.

Наш механізм перевірки значною мірою покладається на повідомлення про те, що властивості змінюються завдяки цьому механізму, і він працює дуже добре. Очевидно, що якщо ви використовуєте іншу реалізацію, крім бізнес-об'єктів, де повідомлення про зміни не є настільки важливим для операції, у вас можуть бути інші методи виявлення змін у вашій бізнес-моделі.

У нас також є Моделі перегляду, які поширюють зміни від Моделі там, де це потрібно, але самі Моделі перегляду вислуховують основні зміни Моделі.


3
Як саме ви поширюєте OnPropertyChanged Model на OnPropertyChanged ViewModel? У мене виникають проблеми, коли ViewModel має інші імена властивостей, ніж Model - тоді знадобиться якесь зіставлення імені до імені, правда?
Мартін Конічек

Це не щось справжнє витончене, ми просто передаємо події. Я думаю, якщо імена були різними, тоді можна було б використовувати таблицю пошуку. Якщо зміна не була відображенням один на один, то ви можете просто підключити подію і потім запустити необхідні події в обробнику.
Стів Мітчам

6

Я погоджуюся з відповіддю Паулу: реалізація INotifyPropertyChangedв Моделях є цілком прийнятною і навіть пропонує Microsoft -

Зазвичай модель реалізує засоби, які полегшують прив’язку до виду. Зазвичай це означає, що він підтримує сповіщення про змінені властивості та колекцію через INotifyPropertyChangedта INotifyCollectionChangedінтерфейси. Класи моделей, які представляють колекції об'єктів, зазвичай походять від ObservableCollection<T>класу, який забезпечує реалізацію INotifyCollectionChangedінтерфейсу.

Хоча вам вирішувати, хочете ви цей тип реалізації чи ні, але пам’ятайте -

Що робити, якщо ваші модельні класи не реалізують необхідні інтерфейси?

Іноді вам потрібно буде працювати з модельними об'єктами , які не реалізують INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfoабо INotifyDataErrorInfoінтерфейси. У цих випадках модель перегляду може потребувати обгортання об'єктів моделі та піддавати потрібні властивості перегляду. Значення для цих властивостей будуть надані безпосередньо об'єктами моделі. Модель перегляду буде реалізовувати необхідні інтерфейси для властивостей, які він відкриває, так що представлення може легко прив'язувати до них дані.

Взято з - http://msdn.microsoft.com/en-us/library/gg405484(PandP.40).aspx

Я працював у деяких проектах, де ми не впроваджували INotifyPropertyChangedв наших моделях, і через це ми стикалися з багатьма проблемами; непотрібне дублювання властивостей було потрібне в VM, і в той же час нам довелося оновлювати базовий об'єкт (з оновленими значеннями), перш ніж передавати їх до BL / DL.

Особливо ви зіткнетесь з проблемами, якщо вам доведеться працювати зі збиранням об'єктів вашої моделі (скажімо, в редагованій сітці або списку) або складних моделей; об'єкти моделі не оновлюються автоматично, і вам доведеться керувати усім, що знаходиться у вашій машині.


3

Але іноді (як у тексті цього посилання на презентацію ) модель - це сервіс, який надає додатку деякі дані в Інтернеті, і тоді вам потрібно доповнити повідомлення про те, що нові дані надійшли або дані змінилися за допомогою подій ...


3

Я думаю, що відповідь цілком зрозуміла, якщо ви хочете дотримуватися MV-VM.

дивіться: http://msdn.microsoft.com/en-us/library/gg405484(v=PandP.40).aspx

У шаблоні MVVM подання інкапсулює інтерфейс користувача та будь-яку логіку інтерфейсу, модель перегляду інкапсулює логіку та стан презентації, а модель інкапсулює бізнес-логіку та дані.

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


4
Цитата відкрита для тлумачення. Думаю, ви повинні додати своє тлумачення, щоб зрозуміти свою відповідь :-)
Søren Boisen

@John D: Ця стаття дає лише одну інтерпретацію MVVM та спосіб її реалізації, вона не визначає MVVM.
akjoshi

Більше того, якщо ви прочитаєте повну статтю, він визначає клас Model таким чином: "Як правило, модель реалізує засоби, які полегшують прив'язку до подання. Це, як правило, означає, що вона підтримує сповіщення про зміни властивостей і збирання через інтерфейси INotifyPropertyChanged та INotifyCollectionChanged. Класи моделей, які представляють колекції об'єктів, зазвичай походять від класу ObservableCollection <T>, який забезпечує реалізацію інтерфейсу INotifyCollectionChanged. "
akjoshi

2

Я б сказав у вашому ViewModel. Це не є частиною Моделі, оскільки Модель є агностичним інтерфейсом користувача. Модель має бути "ВСЕ ОКРЕМИ ділової агностики"


2

Реалізація INPC у моделях може бути використана, якщо моделі відкриті в ViewModel. Але загалом, ViewModel обгортання моделей - це його власні класи для зменшення складності моделі (що не повинно бути корисним для прив’язки). У цьому випадку INPC має бути реалізований у ViewModel.


1

Я використовую INotifyPropertyChangeінтерфейс у моделі. Насправді зміна властивості моделі має бути запущено лише інтерфейсом користувача або зовнішнім клієнтом.

Я помітив кілька переваг і недоліків:

Переваги

Повідомлення є у бізнес-моделі

  1. Відповідно до домену, це правильно. Він повинен вирішити, коли піднімати, а коли не робити.

Недоліки

Модель має властивості (кількість, швидкість, комісія, тотальність). Загальна витрата обчислюється за допомогою кількості, кількості, зміни комісії.

  1. При завантаженні значень з db, обчислення загального фрейту називається 3 рази (qty, rate, комісія). Це має бути один раз.

  2. Якщо швидкість, qty призначена в бізнес-шарі, знову викликається сповіщувач.

  3. Повинна бути можливість відключити це, можливо, в базовому класі. Однак розробники могли забути це зробити.


Зважаючи на всі недоліки, які ви перерахували, ми просто вирішили, що це рішення WRONG у нашому відносно великому проекті WPF щодо впровадження INPC у моделях. Вони повинні мати справу лише зі стійкістю шару. Усі інші речі, такі як перевірка, сповіщення про зміни та обчислені властивості, повинні оброблятись у ViewModel. Тепер я чітко розумію, що повторення властивостей моделі у ViewModel НЕ завжди є порушенням принципу DRY.
try2fly.b4ucry

1

Я думаю, що все залежить від випадку використання.

Якщо у вас є проста модель з навантаженням властивостей, ви можете мати її, реалізуючи INPC. Під простим я маю на увазі, що ця модель виглядає скоріше як POCO .

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

Поставте себе в становищі якоїсь модельної структури, яка має співпрацювати з деякими іншими моделями. У вас є різні події, на які можна підписатися. Усі вони реалізовані як INPC. Уявіть тих обробників подій, які у вас є. Один величезний каскад клавіш if-та / або перемикання.

Ще одна проблема з INPC. Ви повинні розробити свої програми, щоб покладатися на абстракцію, а не на реалізацію. Зазвичай це робиться за допомогою інтерфейсів.

Давайте подивимось на дві різні реалізації однієї абстракції:

public class ConnectionStateChangedEventArgs : EventArgs
{
    public bool IsConnected {get;set;}
}

interface IConnectionManagerINPC : INotifyPropertyChanged
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    bool IsConnected {get;}
}

interface IConnectionManager
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged;
    bool IsConnected {get;}
}

А тепер подивіться на них обох. Що вам каже IConnectionManagerINPC? Що деякі його властивості можуть змінюватися. Ви не знаєте, хто з них. Насправді дизайн полягає в тому, що змінюється лише IsConnected, оскільки решта з них є лише для читання.

Навпаки, наміри IConnectionManager зрозумілі: "Я можу вам сказати, що значення мого IsConnected може змінюватися".


1

Просто використовуйте INotifyPropertyChangeу своїй моделі перегляду, а не в моделі,

зазвичай модель використовує IDataErrorInfoдля обробки помилок перевірки, тому просто тримайтеся у своєму ViewModel і ви прямо на дорозі MVVM.


1
Що з моделями з кількома властивостями? ви повторюєте код у віртуальній машині.
Луїс

0

Припустимо, що посилання на об'єкт у вашому перегляді змінюється. Як ви сповістите про всі оновлені властивості, щоб показати правильні значення? Зателефонувати OnPropertyChangedу ваш погляд на всі властивості об'єкта, на мій погляд, сміття.

Так що я роблю, щоб сам об'єкт повідомити кого - або , коли значення у власності змінюється, і на мій погляд , я використовую прив'язок , як Object.Property1, Object.Property2і. Таким чином, якщо я просто хочу змінити об'єкт, який зараз підтримується, на мій погляд, я просто це роблю OnPropertyChanged("Object").

Щоб уникнути сотень повідомлень під час завантаження об'єктів, у мене є приватний булевий індикатор, що я встановлюю його як істинне під час завантаження, яке перевіряється з об'єкта OnPropertyChangedі нічого не робить.


0

Зазвичай ViewModel реалізує INotifyPropertyChanged. Модель може бути будь-якою (файл XML, база даних або навіть об’єкт). Модель використовується для передачі даних в модель перегляду, яка поширюється на представлення.

дивіться тут


1
ем .. ні. Модель не може бути файлом або базою даних XML. І модель не використовується для надання даних. Інакше його слід називати не "модель", а "дані" ..? Модель використовується для опису даних. Цілком зрозуміло, чи не так? :)
Тарас

1
Якщо у вас є краща відповідь, будь ласка, поділіться! ми всі тут, щоб ділитися знаннями та не змагатись
Адам

0

Я думаю, реалізує INotifyPropertyChangeмодель перегляду, і модель може використовувати сповіщення на іншому "рівні".

наприклад, з деякою службою документування та об'єктом документа у вас є подія documentChanged, яку переглядає модель, щоб очистити та відновити подання. У моделі перегляду редагування ви маєте зміну властивостей властивостей документа для підтримки представлень. Якщо послуга робить багато з документом щодо збереження (оновлення дати зміни, останнього користувача тощо), ви легко отримуєте перевантаження подій Ipropertychanged, і достатньо лише зміненого документа.

Але якщо використовувати INotifyPropertyChange у своїй моделі, я вважаю, що це найкраща практика передати її у вашій моделі перегляду замість передплати на неї безпосередньо у вашому погляді. У тому випадку, коли події у вашій моделі змінюються, вам потрібно змінити лише модуль перегляду, і вигляд залишається недоторканим.


0

Усі властивості, прив'язані до мого перегляду, перебувають у моїх ViewModel (s). Таким чином, вони повинні реалізувати інтерфейс INotifyPropertyChanged. Тому Погляд отримує всі зміни.

[Використовуючи інструментарій MVVM Light, я дозволяю їм успадковувати з ViewModelBase.]

Модель дотримується ділової логіки, але не має нічого спільного з поглядом. Таким чином, інтерфейс INotifyPropertyChanged не потребує.

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