欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF MVVM从入门到精通7:关闭窗口和打开新窗口

程序员文章站 2022-07-13 22:37:51
...

WPF MVVM从入门到精通1:MVVM模式简介

WPF MVVM从入门到精通2:实现一个登录窗口

WPF MVVM从入门到精通3:数据绑定

WPF MVVM从入门到精通4:命令和事件

WPF MVVM从入门到精通5:PasswordBox的绑定

WPF MVVM从入门到精通6:RadioButton等一对多控件的绑定

WPF MVVM从入门到精通7:关闭窗口和打开新窗口

WPF MVVM从入门到精通8:数据验证

完整示例代码下载LoginDemo

若是登录成功,我们一般会执行的操作是关闭当前窗口,然后打开一个新的窗口。但为了比较理想地实现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;
    }
}