Я пытаюсь изучить WPF и проблему MVVM, но столкнулся с проблемой. Этот вопрос похож, но не совсем такой, как этот (обработка-диалогов-в-wpf-с-mvvm) ...
У меня есть форма «Логин», написанная с использованием шаблона MVVM.
Эта форма имеет ViewModel, которая содержит имя пользователя и пароль, которые связаны с представлением в XAML с использованием обычных привязок данных. Он также имеет команду «Войти», которая связана с кнопкой «Войти» в форме, кроме того, используя обычную привязку данных.
Когда запускается команда «Войти», она вызывает функцию в ViewModel, которая отключается и отправляет данные по сети для входа в систему. Когда эта функция завершается, есть 2 действия:
Логин был неверный - мы просто показываем MessageBox и все в порядке
Логин был действителен, нам нужно закрыть форму входа в систему и вернуть ее как
DialogResult
...
Проблема в том, что ViewModel ничего не знает о реальном представлении, так как он может закрыть представление и сказать ему, чтобы он возвращал определенный DialogResult? Я мог бы вставить некоторый код в CodeBehind и / или передать View через ViewModel, но похоже, что он полностью победил бы весь смысл MVVM ...
Обновить
В конце концов я просто нарушил «чистоту» шаблона MVVM и заставил View опубликовать Closed
событие и предоставить Close
метод. ViewModel тогда просто позвонит view.Close
. Представление известно только через интерфейс и подключено через контейнер IOC, поэтому тестирование и ремонтопригодность не теряются.
Кажется довольно глупым, что принятый ответ в -5 голосов! В то время как я хорошо осведомлен о хороших чувствах, которые можно получить, решая проблему, будучи «чистым», Конечно, я не единственный, кто думает, что 200 строк событий, команд и поведений просто для того, чтобы избежать однострочного метода в Название «узоры» и «чистота» немного смешно ....
Close
метод все еще является лучшим решением. Все остальное в других более сложных диалогах - это MVVM и привязка к данным, но было бы глупо реализовывать здесь огромные «решения» вместо простого метода ...Ответы:
Я был вдохновлен ответом Теджуана, чтобы написать более простую прикрепленную собственность. Нет стилей, нет триггеров; вместо этого вы можете просто сделать это:
Это почти так же чисто, как если бы команда WPF все правильно поняла и сделала DialogResult свойством зависимости в первую очередь. Просто поместите
bool? DialogResult
свойство в вашу ViewModel и реализуйте INotifyPropertyChanged, и вуаля, ваша ViewModel может закрыть окно (и установить его DialogResult), просто установив свойство. MVVM в порядке.Вот код для DialogCloser:
Я также разместил это в своем блоге .
источник
С моей точки зрения, вопрос довольно хороший, так как тот же подход будет использоваться не только для окна «Вход», но и для любого вида окна. Я рассмотрел много предложений, и ни один не подходит для меня. Пожалуйста, просмотрите мое предложение, которое было взято из статьи шаблона проектирования MVVM .
Каждый класс ViewModel должен наследовать от того,
WorkspaceViewModel
что имеетRequestClose
событие иCloseCommand
свойствоICommand
типа. РеализацияCloseCommand
свойства по умолчанию вызоветRequestClose
событие.Чтобы закрыть окно,
OnLoaded
метод вашего окна должен быть переопределен:или
OnStartup
метод вашего приложения:Я предполагаю, что реализация
RequestClose
событий иCloseCommand
свойств вWorkspaceViewModel
довольно ясна, но я покажу, что они последовательны:И исходный код
RelayCommand
:PS Не относитесь ко мне плохо из-за этих источников! Если бы они были у меня вчера, это спасло бы меня несколько часов ...
PPS Любые комментарии или предложения приветствуются.
источник
customer.RequestClose
в коде вашего XAML-файла, не нарушает ли он шаблон MVVM? Вы также можете привязатьClick
обработчик событий к кнопке закрытия в первую очередь, увидев, что вы все равно коснулись кода и сделали athis.Close()
! Правильно?Я использовал прикрепленное поведение, чтобы закрыть окно. Свяжите свойство «signal» в вашей ViewModel с прикрепленным поведением (на самом деле я использую триггер). Когда оно установлено в true, поведение закрывает окно.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
источник
Здесь много комментариев, аргументирующих плюсы и минусы MVVM. Для меня я согласен с Нир; это вопрос правильного использования шаблона, а MVVM не всегда подходит. Люди, похоже, готовы пожертвовать всеми наиболее важными принципами разработки программного обеспечения, просто чтобы он соответствовал MVVM.
Тем не менее, ... я думаю, что ваше дело может быть хорошо с небольшим рефакторингом.
В большинстве случаев, с которыми я сталкивался, WPF позволяет вам обходиться без нескольких
Window
с. Может быть, вы можете попробовать использоватьFrame
s иPage
s вместо Windows сDialogResult
s.В вашем случае мое предложение будет
LoginFormViewModel
обрабатыватьLoginCommand
и, если логин неверен, установить для свойстваLoginFormViewModel
соответствующее значение (false
или какое-либо значение перечисления, напримерUserAuthenticationStates.FailedAuthentication
). Вы сделали бы то же самое для успешного входа в систему (true
или другого значения enum). Затем вы используете a,DataTrigger
который отвечает на различные состояния аутентификации пользователя и может использовать простойSetter
для измененияSource
свойстваFrame
.Возвращение окна входа в систему,
DialogResult
я думаю, это то, где вы запутались; этоDialogResult
действительно свойство вашей ViewModel. В моем, по общему признанию, ограниченном опыте работы с WPF, когда что-то не так, как обычно, потому что я думаю о том, как бы я сделал то же самое в WinForms.Надеюсь, это поможет.
источник
Предполагая, что ваш диалог входа в систему является первым окном, которое создается, попробуйте это внутри вашего класса LoginViewModel:
источник
Это простое и чистое решение - вы добавляете событие в ViewModel и указываете окну закрыться, когда это событие сработало.
Для получения более подробной информации смотрите мой пост в блоге, Закрыть окно из ViewModel .
XAML:
ViewModel:
Примечание. В этом примере используется Prism
DelegateCommand
(см. Prism: Commanding ), ноICommand
для этого можно использовать любую реализацию.Вы можете использовать поведения из этого официального пакета.
источник
Я бы справился с этим, добавив обработчик событий в свою ViewModel. Когда пользователь успешно вошел в систему, я бы запустил событие. В моем представлении я прикрепляюсь к этому событию, а когда оно срабатывает, я закрываю окно.
источник
Вот то, что я изначально сделал, и это работает, однако это выглядит довольно скучно и некрасиво (глобальная статика ничего не дает)
1: App.xaml.cs
2: LoginForm.xaml
3: LoginForm.xaml.cs
4: LoginFormViewModel.cs
Позже я удалил весь этот код и просто
LoginFormViewModel
вызвал метод Close в своем представлении. Это оказалось намного приятнее и легче следовать. ИМХО точка моделей, чтобы дать людям простой способ понять , что делает ваше приложение, и в этом случае, MVVM делал это гораздо труднее понять , чем если бы я не использовал его, и был теперь анти -pattern.источник
К вашему сведению, я столкнулся с этой же проблемой, и мне кажется, что я нашел решение, которое не требует глобальных или статических вычислений, хотя, возможно, это не лучший ответ. Я позволю вам, ребята, решить это для себя.
В моем случае ViewModel, который создает окно для отображения (давайте назовем его ViewModelMain), также знает о LoginFormViewModel (используя приведенную выше ситуацию в качестве примера).
Поэтому я создал свойство LoginFormViewModel, имеющее тип ICommand (назовем его CloseWindowCommand). Затем, прежде чем я вызову .ShowDialog () в Window, я установил свойство CloseWindowCommand в LoginFormViewModel в метод window.Close () окна, который я создал. Затем внутри LoginFormViewModel все, что мне нужно сделать, это вызвать CloseWindowCommand.Execute (), чтобы закрыть окно.
Полагаю, это что-то вроде обходного пути, но оно работает хорошо, не нарушая шаблон MVVM.
Не стесняйтесь критиковать этот процесс столько, сколько хотите, я могу принять это! :)
источник
Это, вероятно, очень поздно, но я столкнулся с той же проблемой, и я нашел решение, которое работает для меня.
Я не могу понять, как создать приложение без диалогов (возможно, это просто блок разума). Так что я был в тупике с MVVM и показывал диалог. Итак, я наткнулся на эту статью CodeProject:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
Это UserControl, который в основном позволяет окну находиться внутри визуального дерева другого окна (не разрешено в xaml). Он также предоставляет логическое свойство DependencyProperty под названием IsShowing.
Вы можете установить стиль, как правило, в resourcedictionary, который в основном отображает диалог всякий раз, когда свойство Content элемента управления! = Null через триггеры:
В представлении, где вы хотите отобразить диалоговое окно, просто имейте это:
И в вашей ViewModel все, что вам нужно сделать, это установить для свойства значение (Примечание: класс ViewModel должен поддерживать INotifyPropertyChanged, чтобы представление узнало, что что-то произошло).
вот так:
Чтобы сопоставить ViewModel с View, у вас должно быть что-то вроде этого в Resourcedictionary:
При этом вы получаете однострочный код для отображения диалога. Проблема, которую вы получаете, заключается в том, что вы не можете закрыть диалоговое окно только с помощью приведенного выше кода. Вот почему вы должны поместить событие в базовый класс ViewModel, который наследует DisplayViewModel, и вместо кода выше, напишите это
Затем вы можете обработать результат диалога с помощью обратного вызова.
Это может показаться немного сложным, но как только закладывается фундамент, все довольно просто. Опять же это моя реализация, я уверен, что есть другие :)
Надеюсь, это поможет, это спасло меня.
источник
Итак, этому вопросу уже почти 6 лет, и я до сих пор не могу найти здесь, что я думаю, что это правильный ответ, поэтому позвольте мне поделиться своими "2 центами" ...
На самом деле у меня есть 2 способа сделать это, первый простой ... второй справа, так что если вы ищете правильный, просто пропустите # 1 и перейдите к # 2 :
1. Быстрый и легкий (но не полный)
Если у меня небольшой проект, я иногда просто создаю действие CloseWindowAction во ViewModel:
И кто бы ни создал View, или в коде View, я просто установил метод, который будет вызывать действие:
(помните, что MVVM - это разделение View и ViewModel ... Код View здесь остается View и до тех пор, пока существует правильное разделение, вы не нарушаете шаблон)
Если какая-то ViewModel создает новое окно:
Или, если вы хотите, чтобы это было в вашем главном окне, просто поместите его под конструктор вашего представления:
когда вы хотите закрыть окно, просто вызовите Action на вашей ViewModel.
2. Правильный путь
Теперь правильный способ сделать это - использовать Prism (IMHO), и обо всем этом можно узнать здесь .
Вы можете сделать запрос на взаимодействие , заполнить его любыми данными, которые вам понадобятся в вашем новом окне, запустить его, закрыть и даже получить данные обратно . Все это инкапсулировано и одобрено MVVM. Вы даже получаете статус того, как окно было закрыто , например, пользователь
Canceled
илиAccepted
(кнопка ОК) окно и данные обратно, если вам это нужно . Это немного сложнее и ответ № 1, но это гораздо более полный и рекомендуемый шаблон от Microsoft.Ссылка, которую я дал, содержит все фрагменты кода и примеры, поэтому я не буду помещать здесь какой-либо код, просто прочитайте статью о загрузке Prism Quick Start и запустите ее, это действительно просто понять, немного более многословно заставить его работать, но преимущества больше, чем просто закрытие окна.
источник
+=
для добавления делегата и вызова действия, оно запустит их все ... Или вы будете Я должен создать специальную логику для вашей виртуальной машины, чтобы она знала, какое окно закрывать (возможно, иметь набор закрывающих действий) .... Но я думаю, что несколько представлений, привязанных к одной виртуальной машине, не лучшая практика, это лучше иметь один экземпляр View и один экземпляр виртуальной машины, связанные друг с другом и, возможно, родительскую виртуальную машину, которая управляет всеми дочерними виртуальными машинами, которые связаны со всеми представлениями.источник
Вы можете сделать так, чтобы ViewModel выставлял событие, на которое регистрируется View. Затем, когда ViewModel решает, что пора закрывать представление, оно запускает это событие, которое вызывает закрытие представления. Если вы хотите, чтобы конкретное значение результата было передано обратно, тогда у вас будет свойство в ViewModel для этого.
источник
Просто чтобы добавить к огромному количеству ответов, я хочу добавить следующее. Предполагая, что у вас есть ICommand в вашей ViewModel, и вы хотите, чтобы эта команда закрыла свое окно (или любое другое действие в этом отношении), вы можете использовать что-то вроде следующего.
Он не идеален и может быть сложным для тестирования (так как трудно подделать / заглушить статическое электричество), но он чище (ИМХО), чем другие решения.
Erick
источник
Я реализовал решение Джо Уайта, но столкнулся с проблемами со случайными ошибками « DialogResult может быть установлен только после того, как окно создано и показано как диалог ».
Я держал ViewModel после закрытия View и иногда позже открывал новый View, используя ту же виртуальную машину. Похоже, что закрытие нового View до того, как старый View был собран мусором, привело к тому, что DialogResultChanged попытался установить свойство DialogResult в закрытом окне, что спровоцировало ошибку.
Моим решением было изменить DialogResultChanged для проверки свойства окна IsLoaded :
После внесения этого изменения любые вложения в закрытые диалоги игнорируются.
источник
В итоге я смешал ответ Джо Уайта и некоторый код из ответа Адама Миллса , поскольку мне нужно было показать пользовательский элемент управления в программно созданном окне. Таким образом, DialogCloser не должен быть в окне, он может быть на самом пользовательском элементе управления
И DialogCloser найдет окно пользовательского элемента управления, если оно не было прикреплено к самому окну.
источник
Поведение - самый удобный способ здесь.
С одной стороны, он может быть привязан к заданной модели представления (которая может сигнализировать «закрыть форму!»)
С другой стороны, он имеет доступ к самой форме, поэтому может подписаться на необходимые события, связанные с формой, или показать диалог подтверждения, или что-то еще.
Написание необходимого поведения может показаться скучным с первого раза. Однако теперь вы можете использовать его в каждой нужной форме, используя точный однострочный фрагмент XAML. И при необходимости вы можете извлечь его как отдельную сборку, чтобы его можно было включить в любой следующий проект, который вы хотите.
источник
Почему бы просто не передать окно в качестве параметра команды?
C #:
XAML:
источник
Window
типом, который не совсем «чистый» MVVM. Посмотрите этот ответ, где виртуальная машина не ограниченаWindow
объектом.Другое решение - создать свойство с INotifyPropertyChanged в View Model, например DialogResult, а затем в Code Behind написать:
Самый важный фрагмент
_someViewModel_PropertyChanged
.DialogResultPropertyName
может быть какой-то общедоступной константной строкой вSomeViewModel
.Я использую этот вид трюка, чтобы внести некоторые изменения в View Controls в случае, когда это трудно сделать во ViewModel. OnPropertyChanged во ViewModel вы можете делать все, что вы хотите в View. ViewModel по-прежнему «тестируется модулем», и некоторые небольшие строки кода в коде не имеют значения.
источник
Я бы пошел по этому пути:
источник
Я прочитал все ответы, но должен сказать, что большинство из них просто недостаточно хороши или даже хуже.
Вы могли бы обработать это красиво с классом DialogService, ответственность которого состоит в том, чтобы показать диалоговое окно и вернуть результат диалога. У меня есть пример проекта демонстрирующий его реализацию и использование.
Вот самые важные части:
Разве это не проще? более прямолинейным, более читаемым и последним, но не менее простым в отладке, чем EventAggregator или другие подобные решения?
Как вы можете видеть, в моих моделях представления я использовал первый подход ViewModel, описанный в моем посте здесь: Лучшая практика вызова View из ViewModel в WPF
Конечно, в реальном мире
DialogService.ShowDialog
необходимо иметь больше возможностей для настройки диалога, например кнопок и команд, которые они должны выполнять. Есть разные способы сделать это, но это выходит за рамки :)источник
Хотя это не отвечает на вопрос о том, как сделать это с помощью модели представления, это показывает, как это сделать, используя только XAML + blend SDK.
Я решил загрузить и использовать два файла из Blend SDK, оба из которых вы можете использовать в качестве пакета от Microsoft через NuGet. Файлы:
System.Windows.Interactivity.dll и Microsoft.Expression.Interactions.dll
Microsoft.Expression.Interactions.dll предоставляет вам хорошие возможности, такие как возможность устанавливать свойства или вызывать метод для вашей модели представления или другой цели, а также имеет другие виджеты внутри.
Немного XAML:
Обратите внимание, что если вы просто используете простое поведение «ОК / Отмена», вы можете уйти без использования свойств IsDefault и IsCancel, если окно показано с помощью Window.ShowDialog ().
У меня лично были проблемы с кнопкой, для свойства IsDefault которой было установлено значение true, но она была скрыта при загрузке страницы. Казалось, он не хочет хорошо играть после его показа, поэтому я просто устанавливаю свойство Window.DialogResult, как показано выше, и оно работает для меня.
источник
Вот простое решение без ошибок (с исходным кодом), оно работает для меня.
Получите вашу ViewModel из
INotifyPropertyChanged
Создайте наблюдаемое свойство CloseDialog во ViewModel
}
Прикрепить обработчик в представлении для этого изменения свойства
Теперь вы почти закончили. В обработчике события сделай
DialogResult = true
источник
Создайте
Dependency Property
в своемView
/ любомUserControl
(илиWindow
вы хотите закрыть). Как ниже:И свяжите это со свойством ViewModel :
Недвижимость в
VeiwModel
:Теперь запустите операцию закрытия, изменив
CloseWindow
значение в ViewModel. :)источник
Там, где вам нужно закрыть окно, просто поместите это в модель представления:
та-да
источник
Достаточно!
источник