Используя MVVM в WPF, должен ли я запускать дочерние окна из View code позади или ViewModel?
Я был озадачен этим некоторое время. Я пишу довольно большое приложение RibbonWindow
WPF, используя шаблон MVVM. На экране есть меню RibbonBar
вверху, а остальная часть отображает различные виды. Некоторые представления содержат другие виды, и некоторые из них имеют кнопки, запускающие дочерние Windows.
До сих пор я делал это из файла View за файлом, но я знаю, что при использовании MVVM эти файлы должны быть пустыми. Я мог бы переместить код запуска дочернего окна в ViewModel, но тогда мне понадобится ссылка на главный RibbonWindow
(для установки как владелец дочернего окна), и это кажется неправильным.
Было бы полезно получить любые советы или советы о том, как это обычно достигается с помощью MVVM.
Ответы
Ответ 1
Я обычно справляюсь с этим, создавая своего рода WindowViewLoaderService. Когда ваша программа инициализируется, вы регистрируете свое Window и ваши ViewModels с кодом, похожим на этот:
WindowViewLoaderService.Register(TypeOf(MainWindowView), TypeOf(MainWindowViewModel));
WindowViewLoaderService.Register(TypeOf(MyWindowView), TypeOf(MyWindowViewModel));
Затем, когда вы можете, например, позвонить в эту службу из вашей ViewModel, и все, на что вам нужно ссылаться - это ваша другая ViewModel. Например, если вы находитесь в MainWindowViewModel, у вас может быть такой код:
var myChildWindowVM = new MyWindowViewModel();
WindowViewLoaderService.ShowWindow(myChildWindowVM);
Затем WindowViewLoaderService будет искать, какой View связан с указанной ViewModel, которую вы передали. Он создаст это представление, установит его DataContext равным ViewModel, который вы передали, и затем отобразит представление.
Таким образом, ваши ViewModels никогда не узнают ни о каких представлениях.
Вы можете довольно легко свернуть один из этих сервисов. Все, что ему нужно сделать, это сохранить словарь, ключом которого будет ваш ViewModelType, а значением - ваш ViewType. Метод Register добавляет ваш словарь, а метод ShowWindow ищет правильное представление на основе переданной ViewModel, создает представление, устанавливает DataContext и затем вызывает Show для него.
Большинство фреймворков MVVM предоставляют что-то подобное прямо из коробки. Например, у Caliburn есть гладкий, который просто использует соглашение об именах, он называется ViewLocator в этой платформе. Вот ссылка, которая обобщает: http://devlicio.us/blogs/rob_eisenberg/archive/2010/07/04/mvvm-study-segue-introduction-caliburn-micro.aspx
Cinch, с другой стороны, называет его WPFUIVisualizerService, который вы можете увидеть в действии здесь: http://www.codeproject.com/KB/WPF/CinchIII.aspx
Это должно помочь вам начать работу.
Ответ 2
Ну, одно замечание для начала состоит в том, что "отсутствие кода AT ALL в кодировке" на самом деле является "мифом". Если вы хотите быть прагматичным, и вы видите, что наличие некоторого кода (насколько это было бы возможно, было бы лучше), облегчит вам жизнь и решит вашу проблему, тогда вы должны пойти с этим.
Однако в этой ситуации на самом деле существуют некоторые слабо связанные способы сделать это. У вас может быть служба, которая будет взаимодействовать для вас. Вы инициируете взаимодействие с пользователем из ViewModel, служба позаботится об этом (например, показывая ChildWindow) и возвращает ответ пользователю. Эту услугу можно легко издеваться за тестирование. И он может быть протестирован отдельно.
То есть, если вы хотите делать все сами. Если вы хотите, чтобы фреймворк делал тяжелую работу для вас, вы можете проверить функциональность InteractionRequest
, предлагаемую Призмой. Здесь статья MSDN, в которой рассказывается о адаптированных сценариях MVVM, который включает раздел Шаблоны взаимодействия с пользователем. Это так, как я это делаю, и это довольно просто, элегантно и просто.
Надеюсь, что это поможет:)
Ответ 3
Чтобы ответить Мэтту на один шаг дальше, вы можете использовать все свое представление как пользовательский элемент управления. Затем создайте ViewContainer, который представляет собой окно с вашими шаблонами данных (как вы описали).
Затем вы просто отправляете viewmodel, который хотите открыть, в службу окна, которая устанавливает DataContext. Затем служба откроет окно, и contentcontrol разрешит правильное представление для модели просмотра.
Это означает, что вся регистрация выполняется в XAML, и служба окна просто знает, как это сделать... открыть и закрыть окна.
Ответ 4
Это старый пост, но, возможно, это поможет кому-то в пути: я использую MVVM и поднимаю события для открытия дочерних окон из ViewModel обратно в представление. Единственный код позади - это обработка события, открытие окна, установка владельца дочернего окна и многое другое. В viewmodel, если обработчик события имеет значение NULL, он не подписывается на представление и не запускается. VM не знает о представлении. Код также довольно прост и занимает всего несколько строк.
Ответ 5
В этой ситуации View должен обрабатывать открытие дочерних окон.
Тем не менее, ViewModel может управлять созданием окон, но вызывать в View для создания новой Windows.
Это сохранит логику шаблона MVVM: ViewModel имеет "мозги", но не участвует в создании определенного окна.
Ответ 6
ViewModel используется только для представления состояния системы и логики пользовательского интерфейса. На одну модель представления можно ссылаться несколькими видами. Он не знает специфического кода пользовательского интерфейса, такого как отношения между родителями и дочерними элементами, положение, макет, размер и т.д. Поэтому лучше всплывать дочернее окно с учетом кода с измененным состоянием состояния ViewModel или командами и аргументами события. Таким образом, вы можете указать, какой из них является родительским представлением в слое пользовательского интерфейса.