В MVVM должен ViewModel или Модель реализовать INotifyPropertyChanged?

165

Большинство примеров MVVM, с которыми я работал, имели реализацию ModelINotifyPropertyChanged , но в примере CommandSink Джоша Смита реализована ViewModelINotifyPropertyChanged .

Я все еще когнитивно собираю концепции MVVM, поэтому я не знаю:

  • Вы должны положить INotifyPropertyChangedв ViewModel, чтобы приступить CommandSinkк работе
  • Это просто отклонение от нормы, и это не имеет значения
  • У вас всегда должна быть реализация Model, INotifyPropertyChangedи это просто ошибка, которая будет исправлена, если она будет разработана из примера кода для приложения.

Каким был опыт других в проектах MVVM, над которыми вы работали?

Эдвард Тангей
источник
4
если вы внедряете INPC, попробуйте github.com/Fody/PropertyChanged - это сэкономит вам недели печатания.
CAD Bloke

Ответы:

104

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

Я уверен, что другие не согласятся, но так я работаю.

Стивен Роббинс
источник
84
Что вы делаете, если в модели изменяется свойство? Вам нужно как-то привязать его к view-модели. Честный вопрос, я сейчас занимаюсь этой загадкой.
Роджер Липскомб
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 о том, что свойство Model изменилось, кажется, что INotifyPropertyChanged прекрасно работает как «событие, к которому может подключиться модель представления». Почему бы не использовать его?
skybluecodeflier
146

Я категорически не согласен с концепцией, что Модель не должна реализовывать INotifyPropertyChanged. Этот интерфейс не зависит от пользовательского интерфейса! Это просто сообщает об изменении. Действительно, WPF активно использует это для выявления изменений, но это не значит, что это интерфейс пользовательского интерфейса. Я бы сравнил это со следующим комментарием: « Шина - это автомобильный аксессуар ». Конечно, но велосипеды, автобусы и т. Д. Тоже им пользуются. Итак, не воспринимайте этот интерфейс как интерфейс.

Сказав это, это не обязательно означает, что я считаю, что Модель должна предоставлять уведомления. На самом деле, как правило, модель не должна реализовывать этот интерфейс, если в этом нет необходимости.В большинстве случаев, когда данные сервера не передаются клиентскому приложению, модель может устареть. Но если слушать данные финансового рынка, то я не понимаю, почему модель не может реализовать интерфейс. Например, что если у меня есть логика, не связанная с пользовательским интерфейсом, например, служба, которая, когда она получает цену Bid или Ask для данного значения, выдает предупреждение (например, по электронной почте) или размещает заказ? Это может быть возможным чистым решением.

Тем не менее, существуют разные способы достижения цели, но я бы всегда выступал за простоту и избегал избыточности.

Что лучше? Определить события в коллекции или изменения свойств в модели представления и распространить их на модель или иметь представление, чтобы обновить модель (через модель представления)?

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

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

Хотелось бы, чтобы у меня было больше времени, чтобы объяснить множество разновидностей MVVM и некоторые решения общих проблем - в основном, предоставленные другими разработчиками, но, думаю, мне придется сделать это в другой раз.

Пауло Соуза
источник
7
Подумай об этом так. Если вы, как разработчик, используете DLL-файл с моделями, вы, конечно, не будете переписывать их для поддержки INotifyPropertyChanged.
Ли Тревей
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. => UPVOTE!
j00hi
13

Я думаю, что MVVM очень плохо назван, и вызов ViewModel ViewModel заставляет многих пропускать важную функцию хорошо спроектированной архитектуры, которая является DataController, который контролирует данные, независимо от того, кто пытается их коснуться.

Если вы рассматриваете модель представления как нечто большее, чем DataController, и реализуете архитектуру, в которой ваш DataController является единственным элементом, который касается данных, то вы никогда не будете касаться данных напрямую, но всегда будете использовать DataController. DataController полезен для пользовательского интерфейса, но не обязательно только для пользовательского интерфейса. Это для бизнес-уровня, пользовательского интерфейса и т. Д.

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

Вы в конечном итоге с такой моделью. Даже бизнес должен касаться только данных, используя ViewModel. Тогда ваша загадка просто уходит.

Rhyous
источник
3
Это прекрасно, если ваши данные изменяются только тогда, когда DataController меняет их. Если данные поступают из базы данных или другого хранилища данных, которое может предоставить другую возможность для изменений, вам может потребоваться способ сообщить VIEWMODEL (DataController в вашем шаблоне) и VIEW, когда это произошло. Вы можете либо опросить, используя DataController, либо перейти от какого-либо внешнего процесса к вашей DataModel и разрешить вашей DataModel отправлять уведомления об изменениях в ваш DataController.
ScottCher
4
Вы совершенно правы. Дизайнерские скороговорки очень высокого уровня. В большинстве случаев шаблон дизайна заставляет вас делать все правильно, но время от времени они поворачивают вас не в ту сторону. Никогда не избегайте делать что-то правильно, потому что это выходит за рамки вашего дизайна.
Rhyous
Вы также должны нажать на свой DataController, поскольку он контролирует и модель данных, и попросит его обновить.
Rhyous
Кроме того, модель в MVVM должна быть специфичной для пользовательского интерфейса (например, DTO). Таким образом, любая БД или сложная бизнес-логика должны располагаться на другом уровне, а затем необработанные данные должны быть предоставлены через модель представления.
Кодовое имя Джек
9

Это зависит от того, как вы реализовали свою модель. Моя компания использует бизнес-объекты, подобные объектам Lhotka CSLA, и широко использует INotifyPropertyChangedвсю бизнес-модель.

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

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

Стив Митчам
источник
3
Как именно вы распространяете OnPropertyChanged модели в 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наши модели, и из-за этого мы столкнулись с множеством проблем; ненужное дублирование свойств было необходимо в ВМ, и в то же время нам пришлось обновить базовый объект (с обновленными значениями), прежде чем передавать их в BL / DL.

Вы столкнетесь с проблемами, особенно если вам нужно работать с коллекцией объектов вашей модели (скажем, в редактируемой сетке или списке) или со сложными моделями; объекты модели не будут обновляться автоматически, и вам придется управлять всем этим в вашей виртуальной машине.

akjoshi
источник
3

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

Андрей Хатаев
источник
3

Я думаю, что ответ вполне ясен, если вы хотите придерживаться MV-VM.

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

В шаблоне MVVM представление инкапсулирует UI и любую логику UI, модель представления инкапсулирует логику и состояние представления, а модель - бизнес-логику и данные.

«Представление взаимодействует с моделью представления посредством привязки данных, команд и событий уведомлений об изменениях. Модель представления запрашивает, наблюдает и координирует обновления модели, преобразовывая, проверяя и агрегируя данные, необходимые для отображения в представлении».

Джон Д
источник
4
Цитата открыта для интерпретации. Я думаю, что вы должны добавить свою интерпретацию, чтобы прояснить свой ответ :-)
Сорен Бойзен
@ Джон Д.: В этой статье дается только одна интерпретация MVVM и способ ее реализации, но она не определяет MVVM.
Акёши
Более того, если вы читаете статью полностью, она определяет класс Model следующим образом: «Как правило, модель реализует средства, облегчающие привязку к представлению. Обычно это означает, что она поддерживает уведомление об изменении свойства и коллекции через интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. . Классы моделей, представляющие коллекции объектов, обычно являются производными от класса ObservableCollection <T>, который обеспечивает реализацию интерфейса INotifyCollectionChanged. "
Акёши,
2

Я бы сказал в вашей ViewModel. Это не часть Модели, поскольку Модель не зависит от пользовательского интерфейса. Модель должна быть «все, кроме бизнес-агностика»

Стив Данн
источник
2

Реализация INPC в моделях может быть использована, если модели открыто представлены во ViewModel. Но, как правило, ViewModel оборачивает модели своими собственными классами, чтобы уменьшить сложность модели (которая не должна быть полезной для привязки). В этом случае INPC должен быть реализован во ViewModel.

Стефан Бутин
источник
1

Я использую INotifyPropertyChangeинтерфейс в модели. На самом деле изменение свойства модели должно выполняться только пользовательским интерфейсом или внешним клиентом.

Я заметил несколько преимуществ и недостатков:

преимущества

Уведомитель в бизнес-модели

  1. Что касается домена, это правильно. Следует решить, когда повышать, а когда нет.

Недостатки

Модель имеет свойства (кол-во, ставка, комиссия, общая сумма). Totalfrieght рассчитывается с использованием кол-во, ставка, изменение комиссии.

  1. При загрузке значений из db, общий расчет цены вызывается 3 раза (кол-во, ставка, комиссия). Это должно быть один раз.

  2. Если на бизнес-уровне назначена скорость, кол-во, снова вызывается уведомитель.

  3. Должна быть возможность отключить это, возможно, в базовом классе. Однако разработчики могли забыть это сделать.

Ананд Кумар
источник
Из-за всех перечисленных вами недостатков мы просто решили, что в нашем относительно большом проекте WPF было НЕПРАВИЛЬНОЕ решение внедрить INPC в моделях. Они должны иметь дело только с постоянством слоя. Все остальные вещи, такие как проверка, уведомление об изменении и вычисленные свойства, должны обрабатываться во ViewModel. Теперь я ясно понимаю, что повторение свойств модели во ViewModel НЕ всегда является нарушением принципа СУХОЙ.
try2fly.b4ucry
1

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

Если у вас есть простая модель с множеством свойств, вы можете использовать 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 может измениться».

dzendras
источник
1

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

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

Адам
источник
1
А как насчет моделей с несколькими свойствами? Вы повторяете код в ВМ.
Луис
0

Предположим, что ссылка на объект в вашем представлении изменяется. Как вы будете уведомлять все свойства, которые будут обновлены, чтобы показать правильные значения? На OnPropertyChangedмой взгляд, вызов всех свойств объекта - это чушь.

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

Чтобы избежать сотен уведомлений во время загрузки объектов, у меня есть частный логический индикатор, который я установил в значение true во время загрузки, который проверяется по объекту OnPropertyChangedи ничего не делает.

Dummy01
источник
0

Обычно ViewModel будет реализовывать INotifyPropertyChanged. Модель может быть чем угодно (XML-файл, база данных или даже объект). Модель используется для передачи данных модели представления, которая распространяется на представление.

посмотреть здесь

Сайед
источник
1
эмм .. нет Модель не может быть XML-файлом или базой данных. И модель не используется для предоставления данных. В противном случае его следует называть не «моделью», а «данными» ..? Модель используется для описания данных. Совершенно понятно, не так ли? :)
Тарас
1
Если у вас есть лучший ответ, пожалуйста, поделитесь! мы все здесь, чтобы делиться знаниями, а не соревноваться
Адам
0

Я думаю, что viewmodel реализует INotifyPropertyChangeи модель может использовать уведомления на другом "уровне".

например, с какой-либо службой документов и объектом документа у вас есть событие documentChanged, которое прослушивает модель представления, чтобы очистить и перестроить представление. В редактируемой модели представления у вас есть изменение свойства для свойств документа для поддержки представлений. Если служба многое делает с документом при сохранении (дата обновления, последний пользователь и т. Д.), Вы легко получаете перегрузку событий Ipropertychanged, и достаточно просто изменить документ.

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

Брэм
источник
0

Все свойства, которые связаны с моим представлением, находятся в моих ViewModel (s). Таким образом, они должны реализовать интерфейс INotifyPropertyChanged. Поэтому View получает все изменения.

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

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

donotbefake
источник