Я просматриваю некоторые статьи о MVVM, в основном это и это .
Мой конкретный вопрос: как передать изменения модели из модели в модель просмотра?
В статье Джоша я не вижу, чтобы он этим занимался. ViewModel всегда запрашивает у модели свойства. В примере Рэйчел у нее есть реализация модели INotifyPropertyChanged
и она вызывает события из модели, но они предназначены для потребления самим представлением (см. Ее статью / код для более подробной информации о том, почему она это делает).
Нигде я не вижу примеров, когда модель предупреждает ViewModel об изменениях свойств модели. Меня это беспокоит, что, возможно, это не сделано по какой-то причине. Есть ли шаблон для предупреждения ViewModel об изменениях в Модели? Это может показаться необходимым, поскольку (1) возможно существует более 1 ViewModel для каждой модели, и (2) даже если существует только одна ViewModel, некоторые действия с моделью могут привести к изменению других свойств.
Я подозреваю, что могут быть ответы / комментарии в форме «Зачем вам это нужно?» комментарии, так что вот описание моей программы. Я новичок в MVVM, поэтому, возможно, весь мой дизайн ошибочен. Кратко опишу.
Я программирую что-то более интересное (по крайней мере, для меня!), Чем классы «Клиент» или «Продукт». Я программирую блэкджек.
У меня есть представление, которое не имеет никакого кода и просто полагается на привязку к свойствам и командам в модели представления (см. Статью Джоша Смита).
Хорошо это или плохо , но я придерживался мнения, что Модель должна содержать не только классы, такие как PlayingCard
,, Deck
но и BlackJackGame
класс, который сохраняет состояние всей игры и знает, когда игрок обанкротился, дилер должен взять карты и текущий счет игрока и дилера (меньше 21, 21, перебор и т. д.).
Из BlackJackGame
я раскрываю такие методы, как «DrawCard», и мне пришло в голову, что при отрисовке карты такие свойства, как CardScore
, и, IsBust
должны быть обновлены, и эти новые значения передаются в ViewModel. Возможно, это ошибочное мышление?
Можно было бы подумать, что ViewModel вызывал DrawCard()
метод, поэтому он должен знать, что нужно запрашивать обновленную оценку и выяснять, разоряется он или нет. Мнения?
В моей ViewModel у меня есть логика, позволяющая захватить реальное изображение игральной карты (в зависимости от масти, ранга) и сделать его доступным для просмотра. Модель не должна беспокоиться об этом (возможно, другая ViewModel будет просто использовать числа вместо изображений игральных карт). Конечно, возможно, кто-то скажет мне, что Модель не должна даже иметь концепции игры в Блэкджек, и что это следует обрабатывать в ViewModel?
OnBust
, и виртуальная машина может подписаться на него. Думаю, вы также можете использовать подход ИЭА.Ответы:
Если вы хотите, чтобы ваши модели предупреждали ViewModels об изменениях, они должны реализовать INotifyPropertyChanged , а ViewModels должны подписаться на получение уведомлений PropertyChange.
Ваш код может выглядеть примерно так:
Но обычно это необходимо только в том случае, если несколько объектов будут вносить изменения в данные модели, что обычно не так.
Если у вас когда-либо был случай, когда у вас фактически нет ссылки на свойство модели, чтобы присоединить к нему событие PropertyChanged, вы можете использовать систему обмена сообщениями, такую как Prism
EventAggregator
или MVVM LightMessenger
.В моем блоге есть краткий обзор систем обмена сообщениями, однако, чтобы подвести итог, любой объект может транслировать сообщение, и любой объект может подписаться на прослушивание определенных сообщений. Таким образом, вы можете транслировать a
PlayerScoreHasChangedMessage
от одного объекта, а другой объект может подписаться на прослушивание этих типов сообщений и обновлять егоPlayerScore
свойство, когда он слышит одно.Но я не думаю, что это нужно для описанной вами системы.
В идеальном мире MVVM ваше приложение состоит из ваших ViewModels, а ваши модели - это всего лишь блоки, используемые для создания вашего приложения. Обычно они содержат только данные, поэтому у них не будет таких методов, как
DrawCard()
(это было бы в ViewModel)Таким образом, у вас, вероятно, будут простые объекты данных модели, подобные этим:
и у вас будет объект ViewModel, например
(Все вышеперечисленные объекты должны быть реализованы
INotifyPropertyChanged
, но я оставил это для простоты)источник
DrawCard()
метод будет во ViewModel вместе с другой игровой логикой. В идеальном приложении MVVM вы должны иметь возможность запускать свое приложение полностью без пользовательского интерфейса, просто создав ViewModels и запустив их методы, например, с помощью тестового сценария или окна командной строки. Модели обычно представляют собой только модели данных, содержащие необработанные данные и проверку основных данных.DrawCardCommand()
должны быть во ViewModel, но я думаю, у вас может бытьBlackjackGameModel
объект, содержащийDrawCard()
метод, который вызывается командой, если вы хотитеКороткий ответ: это зависит от специфики.
В вашем примере модели обновляются «сами по себе», и эти изменения, конечно, необходимо каким-то образом распространить на представления. Поскольку представления могут только напрямую обращаться к моделям представления, это означает, что модель должна сообщать эти изменения соответствующей модели представления. Установленный механизм для этого, конечно
INotifyPropertyChanged
, есть , что означает, что вы получите такой рабочий процесс:PropertyChanged
событие моделиDataContext
, свойства привязаны и т. Д.PropertyChanged
иPropertyChanged
в ответ поднимает свои собственныеС другой стороны, если ваши модели содержат мало (или не содержат) бизнес-логики, или если по какой-либо другой причине (например, для получения транзакционных возможностей) вы решили позволить каждой модели представления «владеть» своей обернутой моделью, тогда все модификации модели будут проходить через модель просмотра, поэтому в таком расположении нет необходимости.
Я описываю такой дизайн в другом вопросе MVVM здесь .
источник
Ваш выбор:
На мой взгляд,
INotifyPropertyChanged
это фундаментальная часть .Net. т.е. его вSystem.dll
. Реализация этого в вашей «Модели» сродни реализации структуры событий.Если вам нужен чистый POCO, вам нужно эффективно манипулировать своими объектами через прокси / сервисы, а затем ваша ViewModel уведомляется об изменениях, слушая прокси.
Лично я просто реализую INotifyPropertyChanged, а затем использую FODY, чтобы делать за меня грязную работу. Это выглядит и ощущается POCO.
Пример (использование FODY для IL Weave the PropertyChanged Raisers):
тогда вы можете заставить вашу ViewModel прослушивать PropertyChanged на предмет любых изменений; или изменения, связанные с недвижимостью.
Прелесть маршрута INotifyPropertyChanged в том, что вы связываете его с расширенной коллекцией ObservableCollection . Итак, вы сбрасываете свои близкие объекты poco в коллекцию и слушаете коллекцию ... если что-то изменится, вы узнаете об этом где угодно.
Честно говоря, это могло бы присоединиться к обсуждению «Почему INotifyPropertyChanged автоматически не обрабатывался компилятором», который переходит к следующему: Каждый объект в C # должен иметь возможность уведомлять, если какая-либо его часть была изменена; т.е. реализовать INotifyPropertyChanged по умолчанию. Но это не так, и лучший способ, требующий наименьших усилий, - использовать IL Weaving (в частности, FODY ).
источник
Довольно старый поток, но после долгих поисков я придумал собственное решение: PropertyChangedProxy
С помощью этого класса вы можете легко зарегистрироваться в чьем-то другом NotifyPropertyChanged и предпринять соответствующие действия, если он запущен для зарегистрированного свойства.
Вот пример того, как это могло бы выглядеть, когда у вас есть свойство модели «Статус», которое может изменяться само по себе, а затем должно автоматически уведомлять ViewModel о запуске собственного PropertyChanged для его свойства «Статус», чтобы представление также получало уведомление: )
а вот и сам класс:
источник
-= my_event_handler
), потому что это легче отследить, чем редкую и непредсказуемую проблему зомби, которая может или не может когда-либо произойти.Я нашел эту статью полезной: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum = wpf
Мое резюме:
Идея организации MVVM состоит в том, чтобы упростить повторное использование представлений и моделей, а также обеспечить независимое тестирование. Ваша модель представления - это модель, которая представляет сущности представления, ваша модель представляет бизнес-объекты.
Что, если вы захотите сыграть в покер позже? Большую часть пользовательского интерфейса следует использовать повторно. Если ваша игровая логика привязана к вашей модели представления, будет очень сложно повторно использовать эти элементы без необходимости перепрограммировать модель представления. Что делать, если вы хотите изменить свой пользовательский интерфейс? Если ваша игровая логика связана с логикой вашей модели представления, вам нужно будет перепроверить, что ваша игра все еще работает. Что, если вы хотите создать рабочий стол и веб-приложение? Если ваша модель представления содержит логику игры, будет сложно поддерживать эти два приложения бок о бок, поскольку логика приложения неизбежно будет связана с бизнес-логикой в модели представления.
Уведомления об изменении данных и проверка данных происходят на каждом уровне (представление, модель представления и модель).
Модель содержит ваши представления данных (сущности) и бизнес-логику, специфичную для этих сущностей. Колода карт - это логическая «вещь» с присущими ей свойствами. В хорошую колоду нельзя класть повторяющиеся карты. Он должен показать способ получения верхней карты (ов). Ему нужно знать, что нельзя выдавать больше карточек, чем осталось. Такое поведение колоды является частью модели, потому что оно присуще колоде карт. Также будут модели дилеров, модели игроков, модели рук и т.д. Эти модели могут и будут взаимодействовать.
Модель представления будет состоять из логики представления и приложения. Вся работа, связанная с отображением игры, отделена от логики игры. Это может включать отображение рук в виде изображений, запросы карт к модели дилера, настройки отображения пользователя и т. Д.
Внутренности статьи:
источник
Уведомление на основе INotifyPropertyChanged и INotifyCollectionChanged именно то , что вам нужно. Чтобы упростить себе жизнь с подпиской на изменения свойств, проверкой имени свойства во время компиляции и предотвращением утечек памяти, я бы посоветовал вам использовать PropertyObserver от Josh Smith's MVVM Foundation . Поскольку это проект с открытым исходным кодом, вы можете добавить только этот класс в свой проект из источников.
Чтобы понять, как пользоваться PropertyObserver, прочтите эту статью .
Кроме того, обратите внимание на Reactive Extensions (Rx) . Вы можете открыть IObserver <T> из своей модели и подписаться на него в модели представления.
источник
Ребята проделали потрясающую работу, отвечая на это, но в подобных ситуациях я действительно чувствую, что шаблон MVVM - это боль, поэтому я бы пошел и использовал подход Supervising Controller или Passive View и отпустил систему привязки, по крайней мере, для объектов модели, которые генерируют изменения сами по себе.
источник
Я уже давно защищаю направленную Модель -> Модель просмотра -> Просмотр потока изменений, как вы можете видеть в разделе « Поток изменений » моей статьи о MVVM от 2008 года. Это требует реализации
INotifyPropertyChanged
в модели. Насколько я могу судить, с тех пор это стало обычной практикой.Поскольку вы упомянули Джоша Смита, взгляните на его класс PropertyChanged . Это вспомогательный класс для подписки на
INotifyPropertyChanged.PropertyChanged
событие модели .На самом деле вы можете пойти дальше в этом подходе, поскольку недавно я создал свой класс PropertiesUpdater . Свойства в модели представления вычисляются как сложные выражения, которые включают в себя одно или несколько свойств модели.
источник
Нет ничего плохого в том, чтобы реализовать INotifyPropertyChanged внутри модели и слушать его внутри ViewModel. Фактически, вы можете даже указать свойство модели прямо в XAML: {Binding Model.ModelProperty}
Что касается зависимых / вычисляемых свойств, доступных только для чтения, то я пока не видел ничего лучше и проще: https://github.com/StephenCleary/CalculatedProperties . Это очень просто, но невероятно полезно, на самом деле это «формулы Excel для MVVM» - работает так же, как Excel распространяет изменения в ячейки формул без дополнительных усилий с вашей стороны.
источник
Вы можете вызывать события из модели, на которые модель просмотра должна подписаться.
Например, я недавно работал над проектом, для которого мне нужно было создать древовидное представление (естественно, модель имела иерархический характер). В модели у меня была наблюдаемая коллекция под названием
ChildElements
.В модели просмотра я сохранил ссылку на объект в модели и подписался на
CollectionChanged
событие наблюдаемой коллекции, например:ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)
...Затем ваша модель просмотра получает автоматическое уведомление об изменении в модели. Вы можете использовать ту же концепцию
PropertyChanged
, но вам нужно будет явно вызывать события изменения свойств из вашей модели, чтобы это работало.источник
Мне кажется, что это действительно важный вопрос - даже когда нет никакого давления. Я работаю над тестовым проектом, который включает TreeView. Есть пункты меню и тому подобное, которые отображаются на команды, например, Удалить. В настоящее время я обновляю и модель, и модель представления из модели представления.
Например,
Это просто, но, похоже, имеет очень серьезный недостаток. Типичный модульный тест выполняет команду, а затем проверяет результат в модели представления. Но это не означает, что обновление модели было правильным, поскольку они обновляются одновременно.
Поэтому, возможно, лучше использовать такие методы, как PropertyObserver, чтобы позволить обновлению модели запускать обновление модели представления. Один и тот же модульный тест теперь будет работать, только если оба действия будут успешными.
Я понимаю, что это не потенциальный ответ, но, похоже, его стоит опубликовать.
источник