从ViewModelclosures窗口

我使用window control创build一个login,以允许用户login到我创build的WPF应用程序。

到目前为止,我已经创build了一个方法来检查用户是否在login屏幕上的textboxinput了usernamepassword的正确凭据,并binding两个properties

我通过创build一个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到我的button在xaml像这样;

 <Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" /> 

当我input用户名和密码时,它会执行相应的代码,无论是正确还是错误。 但是,如果用户名和密码都正确,我怎么能从ViewModelclosures这个窗口?

我以前曾尝试使用dialog modal但它并没有完全解决。 此外,在我的app.xaml中,我做了如下的事情,首先加载login页面,然后加载实际的应用程序。

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

问题:如何从ViewModelclosuresloginWindow control

提前致谢。

您可以简单地使用CommandParameter将窗口传递给您的ViewModel。 看到我的例子如下:

我已经实现了一个CloseWindow方法,它将Windows作为参数并closures它。 该窗口通过CommandParameter传递给ViewModel。 请注意,您需要为应该closures的窗口定义一个x:Name 。 在我的XAML窗口中,我通过Command调用这个方法,并使用CommandParameter将窗口本身作为parameter passing给ViewModel。

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

这是一个符合MVVM编程范例的干净而简单的解决scheme。

视图模型

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

XAML

 <Window x:Class="ClientLibTestTool.ErrorView" x:Name="TestWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages" DataContext="{Binding Main, Source={StaticResource Locator}}" Title="{x:Static localization:localization.HeaderErrorView}" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"> <Grid> <Button Content="{x:Static localization:localization.ButtonClose}" Height="30" Width="100" Margin="0,0,10,10" IsCancel="True" VerticalAlignment="Bottom" HorizontalAlignment="Right" Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}"/> </Grid> </Window> 

请注意,我使用MVVM轻型框架,但主体是相同的每个WPF应用程序。

编辑:

您的loginbutton(添加CommandParameter):

 <Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/> 

你的代码:

  public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution! public MainViewModel() { //initialize the CloseWindowCommand. Again, mind the <Window> //you don't have to do this in your constructor but it is good practice, thought this.CloseWindowCommand = new RelayCommand<Window>(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(); } } 

EDIT2:用户Crono在评论部分提到了一个有效的点:

将Window对象传递给视图模型会打破MVVM模式恕我直言,因为它迫使你的虚拟机知道它正在查看什么。

你可以通过引入一个包含close方法的接口来解决这个问题。

接口:

 public interface IClosable { void Close(); } 

您重构的ViewModel将如下所示:

视图模型

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

你需要参考/实现IClosable接口你的看法以及查看(代码后面)

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

当我需要这样做时,我通常会在视图模型上放置一个事件,然后在将视图模型绑定到窗口时将其绑定到Window.Close()

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

而当创buildlogin窗口

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

保持MVVM,我认为使用Blend SDK中的行为(System.Windows.Interactivity)或Prism的自定义交互请求可以很好地处理这种情况。

如果去行为路线,这是一般的想法:

 public class CloseWindowBehavior : Behavior<Window> { 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绑定到一个布尔值,当你想要closures窗口的时候就可以设置它。

 <Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:TestApp" Title="MainWindow" Height="350" Width="525"> <i:Interaction.Behaviors> <local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" /> </i:Interaction.Behaviors> <Grid> </Grid> </Window> 

最后,你的DataContext / ViewModel将有一个属性,当你想要这个窗口closures时,你会设置:

 public class MainWindowViewModel : INotifyPropertyChanged { private bool closeTrigger; /// <summary> /// Gets or Sets if the main window should be closed /// </summary> 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())

那么这里是我在几个项目中使用的东西。 它可能看起来像一个黑客,但它工作正常。

 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将closures。

 <!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult --> <Window someNs:AttachedProperties.DialogResult={Binding DialogResult} /> 

这是我们的生产环境中运行的一个摘要

 <Window x:Class="AC.Frontend.Controls.DialogControl.Dialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" xmlns:hlp="clr-namespace:AC.Frontend.Helper" MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Title="{Binding Title}" hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True" Language="{Binding UiCulture, Source={StaticResource Strings}}"> <!-- A lot more stuff here --> </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 } 

这是一个很简单的方法:

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

我没有看到你select的答案有什么问题,我只是认为这可能是一个更简单的方法来做到这一点!

简单的方法

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

添加一般窗口pipe理器助手

 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中像这样closures它

 WindowManager.CloseWindow(ViewID); 

您可以像这样在ViewModel中创build新的事件处理程序。

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

然后为ExitCommand定义RelayCommand。

 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文件集中

 <Button Command="{Binding CloseCommand}" /> 

在xaml.cs文件中设置DataContext并订阅我们创build的事件。

 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中声明事件,并使用混合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(); }); } } 

我可以closures的界面如下,但不需要执行此操作。 ICloseable将帮助创build通用视图服务,所以如果你通过dependency injection来构造view和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。

 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFRx" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}"> <i:Interaction.Triggers> <i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" > <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/> </Grid> 

这很简单。 您可以为Login – LoginViewModel创build您自己的ViewModel类。 你可以创build视图var dialog = new UserView(); 在你的LoginViewModel里面。 你可以设置Command LoginCommand到button。

 <Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" /> 

 <Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" /> 

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

只需使用以下代码即可closures当前窗口:

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

以下是使用MVVM Light Messenger代替事件的简单示例。 视图模型在单击button时发送closures消息:

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

然后在窗口后面的代码中收到。

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

您可以使用MVVMLight工具包中的Messenger 。 在你的ViewModel发送这样的消息:
Messenger.Default.Send(new NotificationMessage("Close"));
然后在你的windows代码后面,在InitializeComponent之后,注册这样的消息:

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

您可以在这里find更多关于MVVMLight工具包的信息: Codeplex上的MVVMLight工具包

请注意,MVVM中没有“所有规则都没有代码隐藏”,您可以在代码隐藏视图中注册消息。

这个怎么样?

视图模型:

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

在您的ViewModel中,使用CloseAction()来closures窗口,就像上面的例子一样。

视图:

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

System.Environment.Exit(0); 在视图模型将工作。