В MVVM трябва ли ViewModel или моделът да внедри INotifyPropertyChanged?

Повечето примери за MVVM, през които съм работил, имат Модел имплементиран INotifyPropertyChanged, но в Примерът за CommandSink на Джош Смит ViewModel имплементира INotifyPropertyChanged.

Все още когнитивно обединявам концепциите на MVVM, така че не знам дали:

  • Трябва да поставите INotifyPropertyChanged в ViewModel, за да накарате CommandSink да работи
  • Това е просто отклонение от нормата и няма особено значение
  • Винаги трябва да имате модела implement INotifyPropertyChanged и това е просто грешка, която ще бъде коригирана, ако това бъде разработено от примерен код към приложение

Какъв е опитът на другите по MVVM проекти, по които сте работили?


person Edward Tanguay    schedule 21.04.2009    source източник
comment
ако внедрите INPC, опитайте github.com/Fody/PropertyChanged - това ще ви спести седмици на писане.   -  person CAD bloke    schedule 26.04.2016


Отговори (17)


Бих казал точно обратното, винаги поставям моя INotifyPropertyChanged в моя ViewModel - наистина не искате да замърсявате модела си с доста специфична за WPF функция като INotifyPropertyChanged, тези неща трябва да стоят в ViewModel.

Сигурен съм, че другите не биха се съгласили, но това е начинът, по който работя.

person Steven Robbins    schedule 21.04.2009
comment
Какво правите, ако свойство се промени в модела? Трябва по някакъв начин да го заведете в изгледния модел. Честен въпрос, в момента се занимавам с тази главоблъсканица. - person Roger Lipscombe; 18.01.2010
comment
EventAggregator в кода на Prism е добра алтернатива на INotifyPropertyChanged на модела, с персонализирано свойство, променено тип събитие. Кодът на събитието в този проект поддържа препращане на събития между фонови и потребителски нишки, което понякога може да бъде проблем. - person Steve Mitcham; 12.02.2010
comment
@Roger - като малка отстъпка за вашето използване, бихте могли да направите събитие, към което моделът на изглед може да се закачи, което ще бъде извикано, когато потребителският ви интерфейс се актуализира. - person Steve; 25.03.2010
comment
INotifyProperyChanged не е специфичен за WPF, той живее в пространството на имената System.ComponentModel, използвал съм го в WinForms приложения, също така INotifyPropertyChanged е в .Net от 2.0, WPF съществува едва от 3.0 - person benPearce; 04.03.2011
comment
Аз съм фен на поставянето на INotifyPropertyChanged както в MODEL, така и в VIEWMODEL. Не мога да измисля причина да не направя това. Това е елегантен начин за информиране на VIEWMODEL за това кога във вашия MODE са се случили промени на фона, които засягат VIEWMODEL точно както се използва за информиране на VIEW и е имало промени във VIEWMODEL. - person ScottCher; 30.04.2011
comment
@Steve - относно информирането на ViewModel, че свойството на модела се е променило, изглежда, че INotifyPropertyChanged работи добре като събитие, към което viewmodel може да се закачи. Защо не го използвате? - person skybluecodeflier; 01.07.2011
comment
INotifyPropertyChanged няма нищо общо с WPF, а вместо това се занимава с обвързване на данни. DependencyProperties са решението на WPF за обвързващи промени. INotify е полезен дори извън потребителския интерфейс, кажете например, че искате нещо във вашия модел да слуша за промени в обект с настройки. Ако вашият обект за настройки внедрява INotify, тогава вместо да разберат кой използва настройките и да им каже да актуализират, класовете, които използват настройки, чакат самите настройки да публикуват актуализация. - person Mranz; 27.06.2012
comment
@Roger и всеки друг, който се чуди за това: В крайна сметка внедрих INotifyPropertyChanged както в моя Model, така и в ViewModel. След това се абонирам за събитието PropertyChanged на модела от моя ViewModel, така че да може също да повдигне събитието PropertyChanged, така че обвързванията на GUI да се актуализират съответно. Вижте отговора на stackoverflow.com/questions/5821956/ за повече подробности. - person Kyle Tolle; 15.11.2012
comment
Трябваше да поставя INotifyPropertyChanged на модели, така че моите колекции от модели да се актуализират правилно. Не можах да получа промени в свойствата в единичен запис на по-голяма колекция, за да попълня изгледа, без да го поставя в модела. - person WiteCastle; 12.02.2015
comment
Какво ще стане, ако имам ObservableCollection модели и сменя някой модел вътре? - person Konrad; 29.03.2018

Категорично не съм съгласен с концепцията, че моделът не трябва да прилага INotifyPropertyChanged. Този интерфейс не е специфичен за потребителския интерфейс! Той просто информира за промяна. Наистина, WPF силно използва това, за да идентифицира промените, но това не означава, че е UI интерфейс. Бих го сравнил със следния коментар: „Гумата е аксесоар за автомобил“. Разбира се, но велосипеди, автобуси и т.н. също го използват. В обобщение, не приемайте този интерфейс като UI нещо.

Като каза това, това не означава непременно, че вярвам, че моделът трябва да предоставя известия. Всъщност, като основно правило, моделът не трябва да прилага този интерфейс, освен ако не е необходимо. В повечето случаи, когато към клиентското приложение не се изпращат сървърни данни, моделът може да е остарял. Но ако слушам данни от финансовия пазар, тогава не виждам защо моделът не може да внедри интерфейса. Като пример, какво ще стане, ако имам логика, различна от потребителския интерфейс, като например услуга, която, когато получи цена Bid или Ask за дадена стойност, издава предупреждение (напр. чрез имейл) или прави поръчка? Това може да е възможно чисто решение.

Въпреки това има различни начини за постигане на нещата, но аз винаги бих твърдял в полза на простотата и избягването на излишъка.

Какво е по-добре? Дефиниране на събития в колекция или промени в свойствата на модела на изгледа и разпространението им в модела или изгледът да актуализира вътрешно модела (чрез модела на изглед)?

В крайна сметка, когато видите някой да твърди, че „не можете да направите това или онова“, това е знак, че той не знае какво говори.

Наистина зависи от вашия случай и всъщност MVVM е рамка с много проблеми и тепърва ще виждам обща реализация на MVVM навсякъде.

Иска ми се да имам повече време да обясня многото разновидности на MVVM и някои решения на често срещани проблеми - предимно предоставени от други разработчици, но предполагам, че ще трябва да го направя друг път.

person Paulo Sousa    schedule 10.05.2010
comment
Мислете за това по този начин. Ако вие, като разработчик, използвате .dll с модели в себе си, със сигурност няма да ги пренаписвате, за да поддържате INotifyPropertyChanged. - person Lee Treveil; 03.02.2011
comment
Силно съгласен с теб. Подобно на мен, може също да се радвате да откриете, че официалната MVVM документация ‹msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx› (раздел Модел) е съгласен с нас. :-) - person Noldorin; 30.09.2011
comment
Въпреки това, има различни начини за постигане на нещата, но аз винаги бих твърдял в полза на простотата и избягването на излишъка. Много важно. - person Bastien Vandamme; 29.05.2012
comment
INotifyPropertyChanged е част от пространството от имена System.ComponentModel, което е за поведение на компоненти и контроли по време на изпълнение и проектиране. НЕ ИЗПОЛЗВАЙТЕ INotifyPropertyChanged в модели, само във ViewModels. Връзка към документи: docs.microsoft.com/en-us/dotnet/ api/system.componentmodel - person Igor Popov; 02.05.2018
comment
@IgorPopov няма смисъл IMO, това е в пространството на имената System.ComponentModel, което е за поведение на компоненти и контроли по време на изпълнение и по време на проектиране, какво от това? - person akjoshi; 03.07.2018
comment
@akjoshi, защото въвеждате зависимост от изгледа от модела - което трябва да избягвате! - person Igor Popov; 04.07.2018
comment
Стара публикация, знам, но често се връщам към нея, когато стартирам нов проект с помощта на MVVM. Наскоро започнах да налагам много по-стриктно принципа на единната отговорност. Моделът трябва да има една отговорност. Да бъда модел. Веднага след като добавите INotifyPropertyChanged към модела, той вече не следва принципа на единичната отговорност. Цялата причина за съществуването на ViewModel е да позволим на модела да бъде моделът, да позволим на модела да има една единствена отговорност. - person Rhyous; 16.03.2019
comment
@Rhyous Има смисъл, но как да уведомите изгледа или изгледа на модела за промените, настъпили в модела? Когато промяната в модела е проста, това не е проблем, защото моделът на изглед вероятно ще знае коя е промяната. Но си представете, че промяна в модела причинява други промени в модела да се случват (може би в колекция). Това е мястото, където нещата стават трудни без известие. Любопитен съм как бихте се справили с това. - person redcurry; 17.10.2019
comment
@redcurry Гледате на проблема от грешен ъгъл. Трябва да се запитате защо и как се променя моделът? Моделът трябва да бъде защитен от директни промени от външен клас. ViewModel, Controller, Proxy независимо от модела, който отговаря на вашата архитектура. Всички те са форма на обвивка. Ако подсистема x променя модел, тогава на подсистема X е доставен грешен обект. Трябва да се достави опакован предмет. Ето защо моделите трябва да имат интерфейси. Така че можете да изпратите подсистема x модел, който имплементира интерфейса на модела, но това, което изпращате, наистина е обвит обект. - person Rhyous; 17.10.2019
comment
@Rhyous Дори ако моделът е опакован, той има способността да се променя в отговор на някаква друга промяна в модела. Например, да кажем, че промяната на Заплатата на обект на служител също води до промяна на данъчната група на служителя. Как обвивката (като ViewModel) ще разбере, че TaxBracket се е променил? - person redcurry; 17.10.2019

В M-V-VM ViewModel винаги (Моделът не винаги) прилага INotifyPropertyChanged

Разгледайте шаблона/комплекта с инструменти за проект M-V-VM от http://blogs.msdn.com/llobo/archive/2009/05/01/download-m-v-vm-project-template-toolkit.aspx. Той използва DelegateCommand за командване и трябва да бъде страхотен начален шаблон за вашите M-V-VM проекти.

person Soni Ali    schedule 04.05.2009
comment
Това първо изречение обобщава доста добре вариантите на другите отговори, imo. =› ГЛАСУВАЙТЕ ЗА! - person j00hi; 12.02.2015

Мисля, че MVVM е много лошо наименуван и наричането на ViewModel ViewModel кара мнозина да пропускат важна характеристика на една добре проектирана архитектура, която е DataController, който контролира данните, независимо кой се опитва да ги докосне.

Ако мислите за View-Model като повече за DataController и внедрявате архитектура, при която вашият DataController е единственият елемент, който докосва данните, тогава никога няма да докосвате данните директно, а винаги ще използвате DataController. DataController е полезен за потребителския интерфейс, но не непременно само за потребителския интерфейс. Той е за бизнес слой, UI слой и т.н.

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

В крайна сметка получавате модел като този. Дори бизнесът трябва да докосва данните само с помощта на ViewModel. Тогава главоблъсканицата ви просто изчезва.

person Rhyous    schedule 01.01.2011
comment
Това е чудесно, ако вашите данни се променят само когато DataController ги променя. Ако данните идват от база данни или друго хранилище за данни, което може да осигури друг път за промяна, може да се наложи да имате начин да информирате VIEWMODEL (DataController във вашия шаблон) и VIEW, когато това се случи. Можете или да анкетирате, като използвате DataController, или да изпратите от някакъв външен процес към вашия DataModel и да позволите на вашия DataModel да изпраща известия за промяна до вашия DataController. - person ScottCher; 30.04.2011
comment
Вие сте абсолютно прав. Моделите на дизайна са на много високо ниво. През повечето време моделът на проектиране ви кара да правите нещата както трябва, но от време на време ви обръщат по грешния път. Никога не трябва да избягвате да правите нещо правилно, защото то е извън вашия модел на проектиране. - person Rhyous; 18.05.2011
comment
Вие също бихте насочили към вашия DataController, тъй като той контролира и модела на данните и бихте му казали да актуализира. - person Rhyous; 16.03.2019
comment
Освен това моделът в MVVM трябва да се поддържа специфичен за нуждите на потребителския интерфейс (напр. DTO). Така че всяка база данни или сложна бизнес логика трябва да се случи в различен слой и след това груби данни трябва да бъдат предоставени чрез модела на изгледа. - person Code Name Jack; 12.06.2019

Зависи как сте внедрили вашия модел. Моята компания използва бизнес обекти, подобни на CSLA обектите на Lhotka, и използва широко INotifyPropertyChanged в целия бизнес модел.

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

Имаме и View Models, които разпространяват промените от Модела, където е необходимо, но самите View Models слушат основните промени в Модела.

person Steve Mitcham    schedule 21.04.2009
comment
Как точно разпространявате OnPropertyChanged на Model към OnPropertyChanged на ViewModel? Имам проблем, когато ViewModel има различни имена на свойства от Model - тогава ще е необходимо някакво съпоставяне между имена, нали? - person Martin Konicek; 09.07.2009
comment
Не е нещо наистина сложно, ние просто препращаме събитията. Предполагам, че ако имената бяха различни, можеше да се използва справочна таблица. Ако промяната не беше съпоставяне едно към едно, тогава можете просто да закачите събитието и след това да задействате необходимите събития в манипулатора. - person Steve Mitcham; 09.07.2009

Съгласен съм с отговора на Paulo, прилагането на 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.

Ще се сблъскате с проблеми, особено ако трябва да работите с колекция от вашите моделни обекти (да речем в редактируема мрежа или списък) или сложни модели; моделните обекти няма да се актуализират автоматично и ще трябва да управлявате всичко това във вашата VM.

person akjoshi    schedule 15.12.2015

Бих казал във вашия ViewModel. Не е част от модела, тъй като моделът не зависи от потребителския интерфейс. Моделът трябва да бъде „всичко ОСВЕН бизнес агностик“

person Steve Dunn    schedule 21.04.2009

Но понякога (както в тази презентация текст на връзката) моделът е услуга, която доставя на приложението някои данни онлайн и след това трябва да внедрите известие, че са пристигнали нови данни или данните са се променили с помощта на събития...

person Andrey Khataev    schedule 10.11.2009

Мисля, че всичко зависи от случая на използване.

Когато имате прост модел с много свойства, можете да го накарате да внедри INPC. Под просто имам предвид, че този модел изглежда по-скоро като POCO.

Ако вашият модел е по-сложен и живее в домейн на интерактивен модел - модели, препращащи към модели, абонирани за събития на други модели - да имате моделни събития, внедрени като INPC, е кошмар.

Поставете се в позиция на някаква моделна единица, която трябва да си сътрудничи с някои други модели. Имате различни събития, за които да се абонирате. Всички те са изпълнени като INPC. Представете си тези манипулатори на събития, които имате. Една огромна каскада от клаузи if и/или клаузи switch.

Друг проблем с INPC. Трябва да проектирате приложенията си така, че да разчитат на абстракция, а не на имплементация. Това обикновено се прави с помощта на интерфейси.

Нека да разгледаме 2 различни реализации на една и съща абстракция:

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 може да се промени“.

person dzendras    schedule 19.02.2013

Мисля, че отговорът е доста ясен, ако искате да се придържате към MV-VM.

вижте: http://msdn.microsoft.com/en-us/library/gg405484(v=PandP.40).aspx

В модела MVVM изгледът капсулира потребителския интерфейс и всяка логика на потребителския интерфейс, моделът на изгледа капсулира логиката и състоянието на представяне, а моделът капсулира бизнес логиката и данните.

„Изгледът взаимодейства с модела на изгледа чрез обвързване на данни, команди и събития за уведомяване за промяна. Моделът на изгледа запитва, наблюдава и координира актуализациите на модела, преобразувайки, валидирайки и агрегирайки данни, ако е необходимо за показване в изгледа.“

person John D    schedule 24.08.2013
comment
Цитатът е отворен за тълкуване. Мисля, че трябва да добавите вашето тълкуване, за да направите отговора си ясен :-) - person Søren Boisen; 21.05.2015
comment
@John D: Тази статия дава само една интерпретация на MVVM и начин за прилагането й, тя не дефинира MVVM. - person akjoshi; 18.04.2019
comment
Освен това, ако прочетете цялата статия, тя дефинира класа Model по следния начин: Обикновено моделът прилага съоръженията, които улесняват свързването към изгледа. Това обикновено означава, че поддържа известие за промяна на собственост и колекция чрез интерфейсите INotifyPropertyChanged и INotifyCollectionChanged. Класовете модели, които представляват колекции от обекти, обикновено произлизат от класа ObservableCollection‹T›, който осигурява имплементация на интерфейса INotifyCollectionChanged. - person akjoshi; 18.04.2019

Внедряването на INPC в модели може да се използва, ако моделите са ясно изложени в ViewModel. Но като цяло ViewModel обвива моделите е негови собствени класове, за да намали сложността на модела (което не трябва да е полезно за обвързването). В този случай INPC трябва да бъде внедрен в ViewModel.

person stéphane Boutinet    schedule 05.04.2019

Използвам интерфейса INotifyPropertyChange в модел. Всъщност промяната на свойството на модела трябва да се задейства само от потребителския интерфейс или външен клиент.

Забелязах няколко предимства и недостатъци:

Предимства

Нотификаторът е в бизнес модела

  1. Що се отнася до домейна, това е правилно. То трябва да реши кога да вдигне и кога не.

Недостатъци

Моделът има свойства (количество, процент, комисионна, обща цена). Totalfriegh се изчислява с помощта на количество, процент, промяна на комисионната.

  1. При зареждане на стойности от db, общото изчисление на превоза се извиква 3 пъти (количество, тарифа, комисионна). Трябва да е веднъж.

  2. Ако скоростта, количеството са зададени в бизнес слоя, отново се извиква нотификатор.

  3. Трябва да има опция за деактивиране на това, вероятно в основния клас. Разработчиците обаче може да са забравили да направят това.

person Anand Kumar    schedule 20.09.2011
comment
Поради всички недостатъци, които изброихте, ние просто решихме, че е ГРЕШНО решение в нашия сравнително голям WPF проект да внедрим INPC в модели. Те трябва да се занимават само със слоя за постоянство. Всички други неща като валидиране, известие за промяна и изчислени свойства трябва да се обработват във ViewModel. Сега ясно разбирам, че повтарянето на свойствата на модела във ViewModel НЕ винаги е нарушение на принципа DRY. - person try2fly.b4ucry; 13.04.2020

Просто използвайте INotifyPropertyChange във вашия изгледмодел, а не в модела,

моделът обикновено използва IDataErrorInfo за обработка на грешките при валидиране, така че просто запазете своя ViewModel и сте точно на пътя на MVVM.

person Adam    schedule 30.11.2011
comment
Какво ще кажете за моделите с няколко свойства? повтаряте код във VM. - person Luis; 30.03.2018

Да предположим, че референцията на обекта във вашия изглед се промени. Как ще уведомите всички свойства да бъдат актуализирани, за да показват правилните стойности? Извикването на OnPropertyChanged според вас за всички свойства на обекта е боклук от моя гледна точка.

Така че това, което правя, е да позволя на самия обект да уведомява всеки, когато стойност в свойство се промени, и според мен използвам свързвания като Object.Property1, Object.Property2 и други. По този начин, ако просто искам да променя обекта, който в момента се поддържа в моя изглед, просто правя OnPropertyChanged("Object").

За да избегна стотици известия по време на зареждането на обекти, имам частен булев индикатор, който задавам на true по време на зареждане, който се проверява от OnPropertyChanged на обекта и не прави нищо.

person Dummy01    schedule 23.08.2011

Обикновено ViewModel ще имплементира INotifyPropertyChanged. Моделът може да бъде всичко (xml файл, база данни или дори обект). Моделът се използва за предоставяне на данните на модела на изгледа, който се разпространява в изгледа.

вижте тук

person Syed    schedule 16.07.2011
comment
емм.. не. Моделът не може да бъде xml файл или база данни. И моделът не се използва за предоставяне на данните. В противен случай трябва да се нарича не модел, а данни..? Моделът се използва за описание на данните. Доста разбираемо, нали? :) - person Taras; 31.01.2018
comment
Ако имате по-добър отговор, моля, споделете! всички сме тук, за да споделяме знания, а не да се състезаваме - person Adam; 30.03.2018

imho мисля, че viewmodel прилага INotifyPropertyChange и моделът може да използва известие на различно "ниво".

например с някаква документна услуга и документен обект имате събитие documentChanged, което моделът на изглед слуша, за да изчисти и възстанови изгледа. В модела на изглед за редактиране имате промяна на свойствата за свойствата на документа, за да поддържате изгледите. Ако услугата прави много с документа при запазване (актуализиране на дата на промяна, последен потребител и т.н.), лесно получавате претоварване от Ipropertychanged събития и просто documentchanged е достатъчно.

Но ако използвате INotifyPropertyChange във вашия модел, мисля, че е добра практика да го предавате във вашия изгледмодел, вместо да се абонирате за него директно във вашия изглед. В този случай, когато събитията се променят във вашия модел, трябва само да промените модела на изглед и изгледът остава недокоснат.

person Bram    schedule 14.10.2011

Всички свойства, които са обвързани с моя изглед, са в моите ViewModel(s). Следователно те трябва да внедрят интерфейса INotifyPropertyChanged. Следователно изгледът получава всички промени.

[Използвайки инструментариума MVVM Light, им позволих да наследят от ViewModelBase.]

Моделът поддържа бизнес логиката, но няма нищо общо с изгледа. Следователно няма нужда от интерфейс INotifyPropertyChanged.

person donotbefake    schedule 28.02.2019