В шаблоне MVVM для WPF обработка диалогов является одной из более сложных операций. Так как ваша модель представления ничего не знает о представлении, диалоговое общение может быть интересным. Я могу показать, ICommand
что когда представление вызывает его, может появиться диалоговое окно.
Кто-нибудь знает хороший способ обработки результатов из диалогов? Я говорю о диалоговых окнах, таких как MessageBox
.
Одним из способов сделать это было событие в модели представления, на которое представление будет подписываться, когда потребуется диалог.
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
Это нормально, но это означает, что представление требует кода, от которого я хотел бы остаться в стороне.
Ответы:
Я предлагаю отказаться от модальных диалогов 1990-х годов и вместо этого реализовать элемент управления в качестве наложения (холст + абсолютное позиционирование) с видимостью, связанной с логическим значением обратно в ВМ. Ближе к контролю типа AJAX.
Это очень полезно:
как в:
Вот как я реализовал один пользовательский элемент управления. Нажатие на «х» закрывает элемент управления в строке кода в коде пользовательского элемента управления. (Так как у меня есть Views в .exe и ViewModels в dll, я не чувствую себя плохо в коде, который манипулирует UI.)
источник
Вы должны использовать посредника для этого. Посредник - это распространенный шаблон проектирования, также известный как Messenger в некоторых его реализациях. Это парадигма типа Register / Notify, которая позволяет вашим ViewModel и Views взаимодействовать через механизм обмена сообщениями с низкой связью.
Вы должны проверить группу учеников Google WPF и просто искать посредника. Вы будете очень довольны ответами ...
Однако вы можете начать с этого:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
Наслаждайтесь !
Изменить: вы можете увидеть ответ на эту проблему с MVVM Light Toolkit здесь:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
источник
Хороший диалог MVVM должен:
К сожалению, WPF не предоставляет эти функции. Отображение диалога требует вызова кода для
ShowDialog()
. Класс Window, который поддерживает диалоги, не может быть объявлен в XAML, поэтому его нельзя легко привязать к базе данныхDataContext
.Чтобы решить эту проблему, я написал элемент-заглушку XAML, который находится в логическом дереве и передает данные, привязанные к a,
Window
и обрабатывает отображение и скрытие диалога. Вы можете найти его здесь: http://www.codeproject.com/KB/WPF/XAMLDialog.aspxОн действительно прост в использовании и не требует каких-либо странных изменений в вашей ViewModel и не требует событий или сообщений. Основной вызов выглядит так:
Вы, вероятно, хотите добавить стиль, который устанавливает
Showing
. Я объясняю это в моей статье. Я надеюсь, это поможет вам.источник
"Showing a dialog requires a code-behind"
ммм, вы можете назвать это во ViewModelЯ использую этот подход для диалогов с MVVM.
Все, что мне нужно сделать сейчас, это вызвать следующее из моей модели представления.
источник
Мое текущее решение решает большинство упомянутых вами проблем, но оно полностью абстрагировано от платформенных вещей и может быть использовано повторно. Кроме того, я не использовал связывание кода только с DelegateCommands, которые реализуют ICommand. Диалог в основном представляет собой View - отдельный элемент управления, который имеет свою собственную ViewModel, и он отображается из ViewModel основного экрана, но запускается из пользовательского интерфейса через привязку DelagateCommand.
Смотрите полное решение Silverlight 4 здесь Модальные диалоги с MVVM и Silverlight 4
источник
Я действительно боролся с этой концепцией некоторое время, изучая (все еще изучая) MVVM. То, что я решил, и то, что я думаю, что другие уже решили, но что мне не было понятно, это:
Моя первоначальная мысль заключалась в том, что ViewModel нельзя разрешать вызывать диалоговое окно напрямую, так как он не имеет никакого дела, решая, как должен выглядеть диалог. Из-за этого я начал думать о том, как я мог бы передавать сообщения так же, как и в MVP (т.е. View.ShowSaveFileDialog ()). Однако я считаю, что это неправильный подход.
Это нормально для ViewModel для непосредственного вызова диалога. Тем не менее, когда вы тестируете ViewModel, это означает, что диалоговое окно будет либо всплывать во время вашего теста, либо потерпеть неудачу все вместе (на самом деле никогда не пробовал это).
Итак, во время тестирования необходимо использовать «тестовую» версию вашего диалога. Это означает, что для любого имеющегося у вас диалога вам нужно создать интерфейс и либо смоделировать ответ диалога, либо создать тестовый макет, который будет иметь поведение по умолчанию.
Вы уже должны использовать какой-либо Service Locator или IoC, который вы можете настроить для предоставления вам правильной версии в зависимости от контекста.
Используя этот подход, ваша ViewModel по-прежнему тестируема и в зависимости от того, как вы макетируете свои диалоги, вы можете контролировать поведение.
Надеюсь это поможет.
источник
Есть два хороших способа сделать это: 1) сервис диалога (простой, чистый) и 2) вспомогательный просмотр. View Assisted предоставляет некоторые полезные функции, но, как правило, не стоит.
ДИАЛОГ СЕРВИС
а) интерфейс службы диалога, такой как конструктор или некоторый контейнер зависимостей:
interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }
б) Ваша реализация IDialogService должна открыть окно (или внедрить некоторый элемент управления в активное окно), создать представление, соответствующее имени данного типа dlgVm (используйте регистрацию контейнера или соглашение или ContentPresenter с типом, связанным с DataTemplates). ShowDialogAsync должен создать TaskCompletionSource и вернуть его .Task proptery. Класс DialogViewModel сам нуждается в событии, которое вы можете вызвать в производном классе, когда хотите закрыть, и посмотреть в диалоговом окне, чтобы фактически закрыть / скрыть диалоговое окно и завершить TaskCompletionSource.
б) Для использования просто вызовите await this.DialogService.ShowDialog (myDlgVm) в вашем экземпляре некоторого производного от DialogViewModel класса. После возвращения await посмотрите на свойства, которые вы добавили в диалоговую виртуальную машину, чтобы определить, что произошло; вам даже не нужен обратный звонок.
ВИД ПОМОЩЬ
Это ваш взгляд на прослушивание события на модели представления. Все это может быть заключено в Blend Behavior, чтобы избежать выделения кода и использования ресурсов, если вы так склонны (FMI, подкласс класса «Behavior», чтобы увидеть своего рода Blendable присоединенное свойство на стероидах). Сейчас мы сделаем это вручную для каждого вида:
a) Создайте OpenXXXXXDialogEvent с пользовательской полезной нагрузкой (производный класс DialogViewModel).
б) иметь представление подписаться на событие в своем событии OnDataContextChanged. Не забудьте скрыть и отписаться, если старое значение! = Null и в событии Unloaded окна.
c) Когда событие запускается, откройте представление, которое может находиться в ресурсе на вашей странице, или вы можете найти его по соглашению в другом месте (как в подходе службы диалога).
Этот подход является более гибким, но требует больше работы для использования. Я не пользуюсь этим много. Одним приятным преимуществом является, например, возможность размещать представление физически внутри вкладки. Я использовал алгоритм, чтобы поместить его в границы текущего пользовательского элемента управления или, если он недостаточно большой, проходить по визуальному дереву, пока не будет найден достаточно большой контейнер.
Это позволяет диалоговым окнам быть близко к месту, где они фактически используются, только затемняет часть приложения, связанную с текущей активностью, и позволяет пользователю перемещаться внутри приложения без необходимости вручную отталкивать диалоговые окна, даже иметь несколько квази-приложений. модальные диалоги открываются на разных вкладках или под-представлениях.
источник
Используйте команду freezable
источник
Я думаю, что обработка диалога должна быть обязанностью представления, и представление должно иметь код для поддержки этого.
Если вы измените взаимодействие ViewModel - View для обработки диалогов, тогда ViewModel зависит от этой реализации. Самый простой способ справиться с этой проблемой - сделать View ответственным за выполнение задачи. Если это означает показ диалога, тогда все в порядке, но также может быть сообщение о состоянии в строке состояния и т. Д.
Моя точка зрения заключается в том, что весь смысл шаблона MVVM состоит в том, чтобы отделить бизнес-логику от GUI, поэтому не следует смешивать логику GUI (для отображения диалога) на бизнес-уровне (ViewModel).
источник
Интересной альтернативой является использование контроллеров, которые отвечают за отображение представлений (диалогов).
Как это работает, показано в WPF Application Framework (WAF) .
источник
Почему бы просто не поднять событие в виртуальной машине и подписаться на событие в представлении? Это сохранит логику приложения и представление отдельно и все же позволит вам использовать дочернее окно для диалогов.
источник
Я реализовал Поведение, которое слушает Сообщение от ViewModel. Он основан на решении Laurent Bugnion, но, поскольку он не использует код и его можно использовать повторно, я думаю, он более элегантный.
Как заставить WPF вести себя так, как будто MVVM поддерживается из коробки
источник
Я думаю, что представление может иметь код для обработки события из модели представления.
В зависимости от события / сценария, он также может иметь триггер события, который подписывается на просмотр событий модели, и одно или несколько действий для вызова в ответ.
источник
У меня была такая же ситуация и я обернул MessageBox в невидимый элемент управления дизайнера. Подробности в моем блоге
http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
То же самое можно распространить на любые модальные диалоги, управление просмотром файлов и т. Д.
источник
Я свернул свой собственный загрузчик окон, описанный в ответе на этот вопрос:
Управление несколькими представлениями WPF в приложении
источник
Карл Шиффлетт создал образец приложения для отображения диалоговых окон, используя сервисный подход и подход Prism InteractionRequest.
Мне нравится сервисный подход - он менее гибок, поэтому у пользователей меньше шансов что-то сломать :) Это также согласуется с частью моего приложения в WinForms (MessageBox.Show). Но если вы планируете показывать много разных диалогов, тогда InteractionRequest является лучший способ пойти.
http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/
источник
Я знаю, что это старый вопрос, но когда я выполнил этот поиск, я нашел много связанных вопросов, но я не нашел действительно четкого ответа. Поэтому я делаю свою собственную реализацию диалогового окна / сообщения / попина и делюсь этим!
Я думаю, что это «доказательство MVVM», и я пытаюсь сделать его простым и корректным, но я новичок в WPF, поэтому не стесняйтесь комментировать или даже делать запрос на извлечение.
https://github.com/Plasma-Paris/Plasma.WpfUtils
Вы можете использовать это так:
Или как это, если вы хотите более сложный попин:
И это показывает такие вещи:
источник
Стандартный подход
Потратив годы на решение этой проблемы в WPF, я наконец нашел стандартный способ реализации диалогов в WPF. Вот преимущества этого подхода:
Так в чем же ключ. Это DI + IoC .
Вот как это работает. Я использую MVVM Light, но этот подход может быть распространен и на другие фреймворки:
Добавьте интерфейс IDialogService в проект VM:
Предоставьте открытое статическое свойство
IDialogService
типа в вашемViewModelLocator
, но оставьте часть регистрации для слоя View для выполнения. Это ключ .Добавьте реализацию этого интерфейса в проект приложения.
ShowMessage
иAskBooleanQuestion
т. Д.), Другие специфичны для этого проекта и используют пользовательские функцииWindow
. Вы можете добавить больше пользовательских окон таким же образом. Ключ заключается в том, чтобы сохранить специфичные для пользовательского интерфейса элементы в слое View и просто представить возвращенные данные с помощью POCO на уровне VM .Выполните IoC-регистрацию вашего интерфейса в слое View, используя этот класс. Вы можете сделать это в конструкторе вашего основного вида (после
InitializeComponent()
вызова):Вот и ты. Теперь у вас есть доступ ко всем функциям диалога на уровнях VM и View. Ваш уровень VM может вызывать эти функции следующим образом:
Другие бесплатные льготы
IDialogService
в вашем тестовом проекте и зарегистрировать этот класс в IoC в конструкторе вашего тестового класса.Microsoft.Win32
доступ к диалогам открытия и сохранения. Я не учел их, потому что есть также версия этих диалоговых окон для WinForms, плюс кто-то может захотеть создать свою собственную версию. Также обратите внимание, что некоторые из используемых идентификаторовDialogPresenter
являются именами моих собственных окон (напримерSettingsWindow
). Вам нужно будет либо удалить их как из интерфейса, так и из реализации или предоставить свои собственные окна.DispatcherHelper.Initialize()
раннем этапе жизненного цикла вашего приложения.Кроме того,
DialogPresenter
который внедряется в слой View, должны быть зарегистрированы другие ViewModals,ViewModelLocator
а затем должно быть открыто публичное статическое свойство этого типа для использования слоем View. Что-то вроде этого:По большей части ваши диалоги не должны иметь никакого кода для таких вещей, как привязка или установка DataContext и т. Д. Вы даже не должны передавать вещи в качестве параметров конструктора. XAML может сделать все это для вас, вот так:
DataContext
этого способа дает вам все виды преимуществ времени разработки, таких как Intellisense и автозаполнение.Надеюсь, это поможет всем.
источник
Я размышлял над подобной проблемой, когда спрашивал, как должна выглядеть модель представления для задачи или диалога. .
Мое текущее решение выглядит так:
Когда модель представления решает, что пользовательский ввод требуется, она поднимает экземпляр
SelectionTaskModel
с возможными вариантами выбора для пользователя. Инфраструктура заботится о создании соответствующего представления, которое своевременно вызоветChoose()
функцию по выбору пользователя.источник
Я боролся с той же проблемой. Я придумал способ связи между View и ViewModel. Вы можете инициировать отправку сообщения из ViewModel в View, чтобы показать ему окно сообщения, и оно сообщит результат. Затем ViewModel может ответить на результат, возвращенный из View.
Я демонстрирую это в своем блоге :
источник
Я написал довольно обширную статью на эту тему, а также разработал всплывающую библиотеку для MVVM Dialogs. Строгое соблюдение MVVM не только возможно, но и очень чисто при правильной реализации, и его можно легко распространить на сторонние библиотеки, которые сами этого не придерживаются:
https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM
источник
Извините, но я должен вмешаться. Я прошел через несколько предложенных решений, прежде чем нашел пространство имен Prism.Wpf.Interactivity в проекте Prism. Вы можете использовать запросы взаимодействия и действие всплывающего окна, чтобы либо свернуть пользовательское окно, либо для более простых нужд есть встроенные всплывающие окна уведомлений и подтверждений. Они создают настоящие окна и управляются как таковые. Вы можете передать объект контекста с любыми зависимостями, которые вам нужны в диалоге. Мы используем это решение на моей работе, так как я нашел его. У нас здесь много старших разработчиков, и никто не придумал ничего лучшего. Нашим предыдущим решением было использование диалогового сервиса в виде оверлея и использование класса презентатора, чтобы это произошло, но вы должны были иметь фабрики для всех моделей представления диалога и т. Д.
Это не тривиально, но и не очень сложно. И он встроен в Призму и поэтому является лучшей (или лучшей) практикой ИМХО.
Мои 2 цента!
источник
РЕДАКТИРОВАТЬ: да, я согласен, что это не правильный подход MVVM, и сейчас я использую что-то похожее на то, что предлагает вслепую.
Один из способов, которым вы могли бы это
В вашей модели основного вида (где вы открываете модальный):
И в вашем модальном окне View / ViewModel:
XAML:
ViewModel:
или аналогично тому, что выложено здесь WPF MVVM: как закрыть окно
источник