WPF MVVM中关闭窗口和打开新窗口

若是登录成功,我们一般会执行的操作是关闭当前窗口,然后打开一个新的窗口。但为了比较理想地实现MVVM,我们被禁止在ViewModel里面访问View的元素。那我们该如何实现上面的功能呢?.

首先是打开窗口的功能,我们使用的方法是:

(1)窗口初始化的时候即注册需要访问的新窗口。

(2)ViewModel在需要打开新窗口时,使用注册过的窗口。

我们先定义一个WindowManager类:

using System;using System.Collections;using System.Windows; namespace LoginDemo.ViewModel.Common{    /// <summary>    /// 窗口管理器    /// </summary>    public static class WindowManager    {        private static Hashtable _RegisterWindow = new Hashtable();         public static void Register<T>(string key)        {            if (!_RegisterWindow.Contains(key))            {                _RegisterWindow.Add(key, typeof(T));            }        }         public static void Register(string key, Type t)        {            if (!_RegisterWindow.Contains(key))            {                _RegisterWindow.Add(key, t);            }        }         public static void Remove(string key)        {            if (_RegisterWindow.ContainsKey(key))            {                _RegisterWindow.Remove(key);            }        }         public static void Show(string key, object VM)        {            if (_RegisterWindow.ContainsKey(key))            {                var win = (Window)Activator.CreateInstance((Type)_RegisterWindow[key]);                win.DataContext = VM;                win.Show();            }        }    }}

代码比较简单,就不解释了。然后我们在LoginWindow的构造函数里添加代码,变成如下所示:

using LoginDemo.ViewModel.Common;using LoginDemo.ViewModel.Login;using System.Windows; namespace LoginDemo{    /// <summary>    /// LoginWindow.xaml 的交互逻辑    /// </summary>    public partial class LoginWindow : Window    {        public LoginWindow()        {            InitializeComponent();             this.DataContext = new LoginViewModel();             WindowManager.Register<MainWindow>("MainWindow");        }    }}

是不是发现这里说好只加一行,现在又加一行代码了?实在没有办法,打开窗口就是要这么做。

然后我们在ViewModel需要打开窗口的地方写下面一行代码:

WindowManager.Show("MainWindow", null);

这样新的窗口就能在ViewModel里面被打开了。

我们接下来说关闭窗口。要做到这一功能,我们又要借助System.Windows.Interacivity里面的Behavior。它可以把ViewModel里面的一个属性,关联到View层的一个事件(我们这里当然是要关联Window.Close())。

我们先来定义这个关闭行为:

using System.Windows;using System.Windows.Interactivity; namespace LoginDemo.ViewModel.Common{    /// <summary>    /// 窗口行为    /// </summary>    public class WindowBehavior : Behavior<Window>    {        /// <summary>        /// 关闭窗口        /// </summary>        public bool Close        {            get { return (bool)GetValue(CloseProperty); }            set { SetValue(CloseProperty, value); }        }        public static readonly DependencyProperty CloseProperty =            DependencyProperty.Register("Close", typeof(bool), typeof(WindowBehavior), new PropertyMetadata(false, OnCloseChanged));        private static void OnCloseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){            var window = ((WindowBehavior)d).AssociatedObject;            var newValue = (bool)e.NewValue;            if (newValue)            {                window.Close();            }        }     }}

然后我们在XAML文件里增加以下内容:

<i:Interaction.Behaviors>    <c:WindowBehavior Close="{Binding ToClose}"/></i:Interaction.Behaviors>

这样的话,窗口的关闭事件就绑定到了ViewModel里面的ToClose属性了。但这个属性还没有呢,定义一个:

private bool toClose = false;/// <summary>/// 是否要关闭窗口/// </summary>public bool ToClose{    get    {        return toClose;    }    set    {        toClose = value;        if (toClose)        {            this.RaisePropertyChanged("ToClose");        }    }}

如此,只要我们在ViewModel里面执行ToClose=true;,当前窗口就会关闭。这节的内容体现在点击登录按钮上,大体如下:

private BaseCommand loginClick;/// <summary>/// 登录事件/// </summary>public BaseCommand LoginClick{    get    {        if (loginClick == null)        {            loginClick = new BaseCommand(new Action<object>(o =>            {                //执行登录逻辑                WindowManager.Show("MainWindow", null);                ToClose = true;            }));        }        return loginClick;    }}