Закрыть окно из ViewModel

Я создаю вход с помощью window control чтобы позволить пользователю войти в приложение WPF которое я создаю.

До сих пор я создал метод, который проверяет, ввел ли пользователь правильные учетные данные для имени username и password в textbox на экране входа в систему, binding два properties .

Я достиг этого, создав метод bool , например;

 public bool CheckLogin() { var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault(); if (user == null) { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } else if (this.Username == user.Username || this.Password.ToString() == user.Password) { MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in."); return true; } else { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } } public ICommand ShowLoginCommand { get { if (this.showLoginCommand == null) { this.showLoginCommand = new RelayCommand(this.LoginExecute, null); } return this.showLoginCommand; } } private void LoginExecute() { this.CheckLogin(); } 

У меня также есть command которую я bind к своей кнопке внутри xaml например;

 

Когда я вхожу в имя пользователя и пароль, он выполняет присвоенный код, независимо от того, является ли он правильным или неправильным. Но как я могу закрыть это окно из ViewModel, когда верно имя пользователя и пароль?

Я ранее пробовал использовать dialog modal но он не совсем сработал. Кроме того, в моем приложении app.xaml я сделал что-то вроде следующего, которое загружает страницу входа сначала, а затем true, загружает фактическое приложение.

 private void ApplicationStart(object sender, StartupEventArgs e) { Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; var dialog = new UserView(); if (dialog.ShowDialog() == true) { var mainWindow = new MainWindow(); Current.ShutdownMode = ShutdownMode.OnMainWindowClose; Current.MainWindow = mainWindow; mainWindow.Show(); } else { MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK); Current.Shutdown(-1); } } 

Вопрос. Как закрыть Window control входа» из ViewModel?

Заранее спасибо.

Вы можете передать окно в ViewModel с помощью CommandParameter . См. Мой пример ниже.

Я реализовал метод CloseWindow который принимает параметр Windows как параметр и закрывает его. Окно передается в ViewModel через CommandParameter . Обратите внимание, что вам нужно определить x:Name для windows, которое должно быть близко. В моем окне XAML я вызываю этот метод через Command и CommandParameter само окно как параметр в ViewModel с помощью CommandParameter .

 Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}" 

ViewModel

 public RelayCommand CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand(this.CloseWindow); } private void CloseWindow(Window window) { if (window != null) { window.Close(); } } 

Посмотреть

      

Обратите внимание, что я использую структуру MVVM light, но принцип применяется к каждому приложению wpf.

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

Решение MVVM (бывший EDIT2)

пользователь Crono упоминает действительную точку в разделе комментариев:

Передача объекта Window в модель представления ломает шаблон MVVM IMHO, поскольку он заставляет ваш vm знать, что он просматривает.

Вы можете исправить это, введя интерфейс, содержащий метод close.

Интерфейс:

 public interface IClosable { void Close(); } 

Ваш реорганизованный ViewModel будет выглядеть так:

ViewModel

 public RelayCommand CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand(this.CloseWindow); } private void CloseWindow(IClosable window) { if (window != null) { window.Close(); } } 

Вы должны ссылаться и реализовывать интерфейс IClosable в своем представлении

Просмотр (код позади)

 public partial class MainWindow : Window, IClosable { public MainWindow() { InitializeComponent(); } } 

Ответ на исходный вопрос: (бывший EDIT1)

Кнопка входа (добавлен CommandParameter):

  

Ваш код:

  public RelayCommand CloseWindowCommand { get; private set; } // the  is important for your solution! public MainViewModel() { //initialize the CloseWindowCommand. Again, mind the  //you don't have to do this in your constructor but it is good practice, thought this.CloseWindowCommand = new RelayCommand(this.CloseWindow); } public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter { var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault(); if (user == null) { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } else if (this.Username == user.Username || this.Password.ToString() == user.Password) { MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in."); this.CloseWindow(loginWindow); //Added call to CloseWindow Method return true; } else { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } } //Added CloseWindow Method private void CloseWindow(Window window) { if (window != null) { window.Close(); } } пользователя  public RelayCommand CloseWindowCommand { get; private set; } // the  is important for your solution! public MainViewModel() { //initialize the CloseWindowCommand. Again, mind the  //you don't have to do this in your constructor but it is good practice, thought this.CloseWindowCommand = new RelayCommand(this.CloseWindow); } public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter { var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault(); if (user == null) { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } else if (this.Username == user.Username || this.Password.ToString() == user.Password) { MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in."); this.CloseWindow(loginWindow); //Added call to CloseWindow Method return true; } else { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } } //Added CloseWindow Method private void CloseWindow(Window window) { if (window != null) { window.Close(); } } 

Пребывая MVVM, я думаю, что использование Behaviors из Blend SDK (System.Windows.Interactivity) или пользовательский запрос взаимодействия от Prism может работать очень хорошо для такого рода ситуаций.

Если идти по пути поведения, вот общая идея:

 public class CloseWindowBehavior : Behavior { public bool CloseTrigger { get { return (bool)GetValue(CloseTriggerProperty); } set { SetValue(CloseTriggerProperty, value); } } public static readonly DependencyProperty CloseTriggerProperty = DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged)); private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = d as CloseWindowBehavior; if (behavior != null) { behavior.OnCloseTriggerChanged(); } } private void OnCloseTriggerChanged() { // when closetrigger is true, close the window if (this.CloseTrigger) { this.AssociatedObject.Close(); } } } 

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

        

Наконец, ваш DataContext / ViewModel будет иметь свойство, которое вы установили бы, когда бы вы хотели закрыть окно следующим образом:

 public class MainWindowViewModel : INotifyPropertyChanged { private bool closeTrigger; ///  /// Gets or Sets if the main window should be closed ///  public bool CloseTrigger { get { return this.closeTrigger; } set { this.closeTrigger = value; RaisePropertyChanged("CloseTrigger"); } } public MainWindowViewModel() { // just setting for example, close the window CloseTrigger = true; } protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; } 

(установите ваш Window.DataContext = новый MainWindowViewModel ())

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

 public class LoginViewModel { public event EventHandler OnRequestClose; private void Login() { // Login logic here OnRequestClose(this, new EventArgs()); } } 

При создании windows входа в систему

 var vm = new LoginViewModel(); var loginWindow = new LoginWindow { DataContext = vm }; vm.OnRequestClose += (s, e) => loginWindow.Close(); loginWindow.ShowDialog(); 

Хорошо вот что я использовал в нескольких проектах. Это может выглядеть как взломанный, но он отлично работает.

 public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), new PropertyMetaData(default(bool?), OnDialogResultChanged)); public bool? DialogResult { get { return (bool?)GetValue(DialogResultProperty); } set { SetValue(DialogResultProperty, value); } } private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window == null) return; window.DialogResult = (bool?)e.NewValue; } } 

Теперь вы можете связать DialogResult с VM и установить его значение свойства. Window закроется, когда значение будет установлено.

   

Это резюме того, что работает в нашей производственной среде

    

Как вы можете видеть, я сначала объявляю пространство имен xmlns:hlp="clr-namespace:AC.Frontend.Helper" а затем привязывает hlp:AttachedProperties.DialogResult="{Binding DialogResult}" .

AttachedProperty выглядит так. Это не то же самое, что я опубликовал вчера, но ИМХО это не должно иметь никакого эффекта.

 public class AttachedProperties { #region DialogResult public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged)); private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var wnd = d as Window; if (wnd == null) return; wnd.DialogResult = (bool?) e.NewValue; } public static bool? GetDialogResult(DependencyObject dp) { if (dp == null) throw new ArgumentNullException("dp"); return (bool?)dp.GetValue(DialogResultProperty); } public static void SetDialogResult(DependencyObject dp, object value) { if (dp == null) throw new ArgumentNullException("dp"); dp.SetValue(DialogResultProperty, value); } #endregion } 

Простой способ

 public interface IRequireViewIdentification { Guid ViewID { get; } } 

Внедрение в ViewModel

 public class MyViewVM : IRequireViewIdentification { private Guid _viewId; public Guid ViewID { get { return _viewId; } } public MyViewVM() { _viewId = Guid.NewGuid(); } } 

Добавить общий помощник оконного менеджера

 public static class WindowManager { public static void CloseWindow(Guid id) { foreach (Window window in Application.Current.Windows) { var w_id = window.DataContext as IRequireViewIdentification; if (w_id != null && w_id.ViewID.Equals(id)) { window.Close(); } } } } 

И закрыть его так, как в viewmodel

 WindowManager.CloseWindow(ViewID); 

Так я сделал это довольно просто:

YourWindow.xaml.cs

 //In your constructor public YourWindow() { InitializeComponent(); DataContext = new YourWindowViewModel(this); } 

YourWindowViewModel.cs

 private YourWindow window;//so we can kill the window //In your constructor public YourWindowViewModel(YourWindow window) { this.window = window; } //to close the window public void CloseWindow() { window.Close(); } 

Я не вижу ничего плохого в ответе, который вы выбрали, я просто подумал, что это может быть более простой способ сделать это!

Как насчет этого ?

ViewModel:

 class ViewModel { public Action CloseAction { get; set; } private void Stuff() { // Do Stuff CloseAction(); // closes the window } } 

В ViewModel используйте CloseAction (), чтобы закрыть окно, как в приведенном выше примере.

Посмотреть:

 public View() { InitializeComponent(); ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View if (vm.CloseAction == null) vm.CloseAction = new Action(() => this.Close()); } 

это может быть поздно, но вот мой ответ

 foreach (Window item in Application.Current.Windows) { if (item.DataContext == this) item.Close(); } 

Вы можете создать новый обработчик событий в ViewModel следующим образом.

 public event EventHandler RequestClose; protected void OnRequestClose() { if (RequestClose != null) RequestClose(this, EventArgs.Empty); } 

Затем определите RelayCommand для ExitCommand.

 private RelayCommand _CloseCommand; public ICommand CloseCommand { get { if(this._CloseCommand==null) this._CloseCommand=new RelayCommand(CloseClick); return this._CloseCommand; } } private void CloseClick(object obj) { OnRequestClose(); } 

Затем в наборе файлов XAML

  

Установите DataContext в файле xaml.cs и подпишитесь на событие, которое мы создали.

 public partial class MainWindow : Window { private ViewModel mainViewModel = null; public MainWindow() { InitializeComponent(); mainViewModel = new ViewModel(); this.DataContext = mainViewModel; mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); }; } } 

Мой предложенный способ – объявить событие в ViewModel и использовать blend InvokeMethodAction, как показано ниже.

Образец ViewModel

 public class MainWindowViewModel : BindableBase, ICloseable { public DelegateCommand SomeCommand { get; private set; } #region ICloseable Implementation public event EventHandler CloseRequested; public void RaiseCloseNotification() { var handler = CloseRequested; if (handler != null) { handler.Invoke(this, EventArgs.Empty); } } #endregion public MainWindowViewModel() { SomeCommand = new DelegateCommand(() => { //when you decide to close window RaiseCloseNotification(); }); } } 

I Близкий интерфейс, как показано ниже, но не требует выполнения этого действия. ICloseable поможет создать универсальный сервис просмотра, поэтому, если вы создадите представление и ViewModel путем инъекции зависимостей, то что вы можете сделать, это

 internal interface ICloseable { event EventHandler CloseRequested; } 

Использование ICloseable

 var viewModel = new MainWindowViewModel(); // As service is generic and don't know whether it can request close event var window = new Window() { Content = new MainView() }; var closeable = viewModel as ICloseable; if (closeable != null) { closeable.CloseRequested += (s, e) => window.Close(); } 

И ниже Xaml, вы можете использовать этот xaml, даже если вы не реализуете интерфейс, вам понадобится только ваша модель просмотра для создания CloseRquested.

          

Это просто. Вы можете создать свой собственный class ViewModel для Login – LoginViewModel. Вы можете создать диалог вида var = новый UserView (); внутри вашего LoginViewModel. И вы можете настроить команду LoginCommand на кнопку.

  

а также

  

Класс ViewModel:

 public class LoginViewModel { Window dialog; public bool ShowLogin() { dialog = new UserView(); dialog.DataContext = this; // set up ViewModel into View if (dialog.ShowDialog() == true) { return true; } return false; } ICommand _loginCommand public ICommand LoginCommand { get { if (_loginCommand == null) _loginCommand = new RelayCommand(param => this.Login()); return _loginCommand; } } public void CloseLoginView() { if (dialog != null) dialog.Close(); } public void Login() { if(CheckLogin()==true) { CloseLoginView(); } else { // write error message } } public bool CheckLogin() { // ... check login code return true; } } 

Вы можете закрыть текущее окно, используя следующий код:

 Application.Current.Windows[0].Close(); 

Вот простой пример использования MVVM Light Messenger вместо события. Модель просмотра отправляет закрытое сообщение при нажатии кнопки:

  public MainViewModel() { QuitCommand = new RelayCommand(ExecuteQuitCommand); } public RelayCommand QuitCommand { get; private set; } private void ExecuteQuitCommand() { Messenger.Default.Send(new CloseMessage()); } 

Затем он принимается в коде позади windows.

  public Main() { InitializeComponent(); Messenger.Default.Register(this, HandleCloseMessage); } private void HandleCloseMessage(CloseMessage closeMessage) { Close(); } 

Вы можете использовать Messenger из инструментария MVVMLight. в вашей ViewModel отправьте сообщение следующим образом:
Messenger.Default.Send(new NotificationMessage("Close"));
то в вашем коде Windows позади, после InitializeComponent , зарегистрируйтесь для этого сообщения следующим образом:

 Messenger.Default.Register(this, m=>{ if(m.Notification == "Close") { this.Close(); } }); 

вы можете найти больше о MVVMLight toolkit: MVVMLight toolkit на Codeplex

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

System.Environment.Exit (0); на виду модель будет работать.

  • Передача параметров между режимами просмотра
  • WPF Combobox: различные шаблоны в текстовом поле и выпадающем списке
  • Лучший способ использовать векторное изображение в WPF?
  • заполнять дерево из списка путей файла в wpf
  • Прослушать изменения свойства зависимостей
  • Передача значения enums в качестве параметра команды из XAML
  • События WPF в ResourceDictionary для ControlTemplate
  • Как выполнить выбор флажка «Однократный щелчок» в WPF DataGrid?
  • Горизонтальная прокрутка для панели стека не работает
  • WPF C # Путь: как получить из строки с данными пути в геометрию в коде (не в XAML)
  • Неправильно ли использовать Диспетчер в моей модели ViewModel?
  • Давайте будем гением компьютера.