若是登錄成功,我們一般會執行的操作是關閉當前窗口,然后打開一個新的窗口。但為了比較理想地實現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;
}
}