Fermer la fenêtre de ViewModel

Je crée un login en utilisant un window control pour permettre à un utilisateur de se connecter à une application WPF que je crée.

Jusqu’à présent, j’ai créé une méthode qui vérifie si l’utilisateur a entré les informations d’identification correctes pour le username et le password de password dans une textbox de textbox sur l’écran de connexion, binding deux properties .

J’y suis parvenu en créant une méthode bool , comme ça;

 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.ToSsortingng() == 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(); } 

J’ai aussi une command que je bind à mon bouton dans le xaml comme ça;

 

Lorsque j’entre le nom d’utilisateur et le mot de passe, il exécute le code approprié, qu’il soit correct ou non. Mais comment puis-je fermer cette fenêtre à partir de ViewModel lorsque le nom d’utilisateur et le mot de passe sont corrects?

J’ai déjà essayé d’utiliser un dialog modal mais cela n’a pas vraiment fonctionné. De plus, dans mon app.xaml, j’ai fait quelque chose comme ceci, qui charge d’abord la page de connexion, puis une fois vrai, charge l’application réelle.

 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); } } 

Question: Comment puis-je fermer le Window control de la Window control connexion à partir de ViewModel?

Merci d’avance.

Vous pouvez passer la fenêtre à votre ViewModel en utilisant le CommandParameter . Voir mon exemple ci-dessous.

J’ai implémenté une méthode CloseWindow qui prend un Windows comme paramètre et le ferme. La fenêtre est transmise à ViewModel via CommandParameter . Notez que vous devez définir un nom x:Name pour la fenêtre qui doit être proche. Dans ma fenêtre XAML, j’appelle cette méthode via la Command et passe la fenêtre elle-même en tant que paramètre à ViewModel à l’aide de 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(); } } 

Vue

      

Notez que j’utilise le framework léger MVVM, mais le principe s’applique à chaque application wpf.

Cette solution viole le modèle MVVM, car le modèle de vue ne devrait rien savoir de l’implémentation de l’interface utilisateur. Si vous souhaitez suivre ssortingctement le paradigme de la programmation MVVM, vous devez extraire le type de la vue avec une interface.

Solution compatible MVVM (ancien EDIT2)

l’utilisateur Crono mentionne un point valide dans la section commentaire:

Passer l’object Window au modèle de vue interrompt le modèle IMVM, car il force votre machine virtuelle à savoir dans quoi elle est affichée.

Vous pouvez résoudre ce problème en introduisant une interface contenant une méthode close.

Interface:

 public interface IClosable { void Close(); } 

Votre ViewModel refactorisé ressemblera à ceci:

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(); } } 

Vous devez référencer et implémenter l’interface IClosable à votre avis

Voir (Code derrière)

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

Réponse à la question initiale: (ancien EDIT1)

Votre bouton de connexion (paramètre de commande ajouté):

  

Votre code:

  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.ToSsortingng() == 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(); } } 

Rester MVVM, je pense que l’utilisation de Behaviors du Blend SDK (System.Windows.Interactivity) ou une demande d’interaction personnalisée de Prism pourrait très bien fonctionner pour ce type de situation.

En allant sur la route du comportement, voici l’idée générale:

 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 closesortinggger is true, close the window if (this.CloseTrigger) { this.AssociatedObject.Close(); } } } 

Ensuite, dans votre fenêtre, vous souhaitez simplement lier le CloseTrigger à une valeur booléenne définie lorsque vous souhaitez fermer la fenêtre.

        

Enfin, votre DataContext / ViewModel aurait une propriété que vous définiriez lorsque vous souhaitiez que la fenêtre se ferme comme ceci:

 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(ssortingng propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; } 

(définissez votre Window.DataContext = new MainWindowViewModel ())

Je mets généralement un événement sur le modèle de vue lorsque j’ai besoin de le faire, puis je le Window.Close() à Window.Close() lors de la liaison du modèle de vue à la fenêtre.

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

Et lors de la création de la fenêtre de connexion

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

Eh bien voici quelque chose que j’ai utilisé dans plusieurs projets. Cela peut ressembler à un hack, mais ça marche bien.

 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; } } 

Vous pouvez maintenant lier DialogResult à une machine virtuelle et définir sa valeur de propriété. La Window se fermera lorsque la valeur est définie.

   

Ceci est un résumé de ce qui fonctionne dans notre environnement de production

    

Comme vous pouvez le voir, je déclare l’espace de noms xmlns:hlp="clr-namespace:AC.Frontend.Helper" abord et ensuite la liaison hlp:AttachedProperties.DialogResult="{Binding DialogResult}" .

La AttachedProperty ressemble à ceci. Ce n’est pas la même chose que j’ai posté hier, mais à mon humble avis, cela ne devrait pas avoir d’effet.

 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 } 

Moyen facile

 public interface IRequireViewIdentification { Guid ViewID { get; } } 

Implémenter à ViewModel

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

Ajouter un assistant de gestionnaire de fenêtres général

 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(); } } } } 

Et fermez-le comme ça dans viewmodel

 WindowManager.CloseWindow(ViewID); 

C’est un moyen que j’ai fait assez simplement:

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(); } 

Je ne vois rien de mal avec la réponse que vous avez choisie, je pensais juste que cela pourrait être une façon plus simple de le faire!

Comment ça ?

ViewModel:

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

Dans votre ViewModel, utilisez CloseAction () pour fermer la fenêtre comme dans l’exemple ci-dessus.

Vue:

 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()); } 

il peut être en retard, mais voici ma réponse

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

Vous pouvez créer un nouveau gestionnaire d’événements dans ViewModel comme celui-ci.

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

Ensuite, définissez RelayCommand pour 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(); } 

Puis dans le fichier XAML

  

Définissez le DataContext dans le fichier xaml.cs et abonnez-vous à l’événement que nous avons créé.

 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(); }; } } 

Ma méthode proposée est l’événement Declare dans ViewModel et utilise le mélange InvokeMethodAction comme ci-dessous.

Sample 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 L’interface fermable est comme ci-dessous mais n’a pas besoin d’effectuer cette action. ICloseable vous aidera à créer un service de vue générique, donc si vous construisez view et ViewModel par dependency injection, ce que vous pouvez faire est de

 internal interface ICloseable { event EventHandler CloseRequested; } 

Utilisation de 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(); } 

Et Ci-dessous, Xaml, vous pouvez utiliser ce xaml même si vous n’implémentez pas d’interface, il ne vous faudra que votre modèle de vue pour relancer CloseRquested.

          

C’est simple. Vous pouvez créer votre propre classe ViewModel pour Login – LoginViewModel. Vous pouvez créer la vue var dialog = new UserView (); dans votre LoginViewModel. Et vous pouvez configurer Command LoginCommand dans le bouton.

  

et

  

Classe 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; } } 

Vous pouvez fermer la fenêtre actuelle simplement en utilisant le code suivant:

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

Voici un exemple simple d’utilisation de MVVM Light Messenger au lieu d’un événement. Le modèle de vue envoie un message de fermeture lorsqu’un bouton est cliqué:

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

Ensuite, il est reçu dans le code derrière la fenêtre.

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

Vous pouvez utiliser Messenger depuis la boîte à outils MVVMLight. dans votre ViewModel envoyez un message comme celui-ci:
Messenger.Default.Send(new NotificationMessage("Close"));
puis dans votre code Windows, après InitializeComponent , enregistrez-vous pour ce message comme ceci:

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

Vous trouverez plus d’informations sur la boîte à outils MVVMLight ici: Toolkit MVVMLight sur Codeplex

Notez qu’il n’y a pas de règle “pas de code derrière tout” dans MVVM et que vous pouvez vous enregistrer pour les messages dans un code de vue.

System.Environment.Exit (0); dans la vue modèle fonctionnerait.