在实际开发过程中,经常会遇到多个窗口对象,随之而来的就是对应的多个ViewModel对象,这些对象在一定条件下会发生相互访问的情况,例如VM与不同窗口交互、VM与不同VM交互,这些不同模块对象之间的交互,就是跨模块交互。
MVVM模式下跨模块交互解决方案
面对跨模块交互,常规的解决方案是创建一个“第三方”来作为交互的中转站。
创建一个窗体管理类型,作为第三方
public class WindowManager
{
private static Dictionary<string, WinEntity> _windows = new Dictionary<string, WinEntity>();
/// <summary>
/// 窗体类型注册
/// </summary>
/// <param name="type"></param>
/// <param name="owner"></param>
public static void Register(Type type, Window owner)
{
_windows.Add(type.Name, new WinEntity {
Type = type,
Owner = owner
});
}
//展示对应的窗口
public static bool ShowDialog(string winKey, object dataContext)
{
if (_windows.ContainsKey(winKey))
{
Type type = _windows[winKey].Type;
var win = (Window)Activator.CreateInstance(type);
//指定所展示的窗口对象的拥有者,设置后子窗口跟主窗口会关联展示
win.Owner = _windows[winKey].Owner;
//传递上下文参数
win.DataContext = dataContext;
return win.ShowDialog()==true?true:false;
}
return false;
}
}
class WinEntity
{
public Type Type { get; set; }
public Window Owner { get; set; }
}
主窗口做窗口类型注册
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//进行窗体对象类型的注册,后面通过反射获得对应的窗体对象,这里的SecondWindow只是一个简单的显示Hello的窗口
WindowManager.Register(typeof(SecondWindow), this);
}
}
命令类型创建
public class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
public void DoCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public Func<object, bool> DoCanExecute { get; set; }
public bool CanExecute(object parameter)
{
return DoCanExecute(parameter);
}
public Action<object> DoExecute { get; set; }
public void Execute(object parameter)
{
DoExecute(parameter);
}
}
ViewModel层
public class MainViewModel
{
public MainModel MainModel { get; set; }
public CommandBase CommandBase { get; set; }
public MainViewModel()
{
CommandBase = new CommandBase
{
DoExecute = DoExecute,
DoCanExecute = DoCanExecute
};
MainModel = new MainModel();
}
private void DoExecute(object obj)
{
//创建并设置VM对象,传递给所访问的窗体对象
MainViewModel mainViewModel = new MainViewModel();
mainViewModel.MainModel.Value = 123;
//跨模块调用窗体对象
if (WindowManager.ShowDialog("SecondWindow", mainViewModel))
{
//根据窗口的操作结果来进行不同的业务逻辑
}
else
{
}
}
private bool DoCanExecute(object obj)
{
Debug.WriteLine("DoCanExecute......");
return true;
}
}
Model层
public class MainModel
{
public int Value { get; set; }
}
以上的做法,将对应的窗口类型剥离出来,由WindowManager统一管理,实现各个模块之间的相互隔离。这个方式不仅仅用于ViewModel层访问窗口对象,还能针对不同的跨模块访问情况做出不同的调整。例如跨模块访问方法的ActionManager可以如下编写:
public class MethodManager<T>
{
private Dictionary<string, Func<T>> _methods = new Dictionary<string, Func<T>>();
public void Register(string name, Func<T> func)
{
_methods.Add(name, func);
}
public T Invoke(string name)
{
return _methods[name].Invoke();
}
}