子视图模型不应该知道主视图模型。
相反,他们应该引发诸如 Forward 或 Back 等名称的事件。 ChangeView 是您提供的唯一示例,因此我们将继续使用它。
我们将让子视图模型公开导致引发事件的命令。子视图的 XAML 中的按钮或 MenuItem 可以绑定到命令以让用户调用它们。您也可以通过 Click 事件处理程序在后面的子视图代码中调用 viewmodel 方法来做到这一点,但是命令更“正确”,因为以在 viewmodel 中进行更多工作为代价,它们使视图创建者的生活变得更加简单.
主视图模型处理这些事件并相应地更改活动页面视图模型。因此,子进程不会调用 _mainVM.ChangeView(),而是会引发自己的 ChangeView 事件,并且子进程上的主 VM 处理该事件的处理程序会调用自己的方法 this.ChangeView()。主虚拟机是组织者虚拟机,所以它拥有导航。
让代码尽可能不知道它的使用方式和使用地点是一个很好的规则。这适用于控件和视图模型。想象一下,如果 ListBox 类要求父类是某个特定的类;这将是令人沮丧的,也是不必要的。事件帮助我们编写有用的子类,不需要知道或关心哪个父类使用它们。即使不可能重用,这种方法也可以帮助您编写干净、分离良好的类,这些类易于编写和维护。
如果您在细节方面需要帮助,请提供更多代码,我们可以将这个设计应用到您的项目中。
示例
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
FooViewModel = new FooViewModel();
FooViewModel.Back += (object sender, EventArgs e) => Back();
}
public FooViewModel FooViewModel { get; private set; }
public void Back()
{
// Change selected page property
}
}
public class FooViewModel : ViewModelBase
{
public event EventHandler Back;
private ICommand _backCommand;
public ICommand BackCommand {
get {
if (_backCommand == null)
{
// It has to give us a parameter, but we don't have to use it.
_backCommand = new DelegateCommand(parameter => OnBack());
}
return _backCommand;
}
}
// C#7 version
public void OnBack() => Back?.Invoke(this, EventArgs.Empty);
// C# <= 5
//protected void OnBack()
//{
// var handler = Back;
// if (handler != null)
// {
// handler(this, EventArgs.Empty);
// }
//}
}
// I don't know if you already have a DelegateCommand or RelayCommand class.
// Whatever you call it, if you don't have it, here's a quick and dirty one.
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> exec, Func<object, bool> canExec = null)
{
_exec = exec;
_canExec = canExec;
}
Action<object> _exec;
Func<object, bool> _canExec;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExec == null || _canExec(parameter);
}
public void Execute(object parameter)
{
if (_exec != null)
{
_exec(parameter);
}
}
}
如何从子 XAML 调用 BackCommand:
<Button Content="Back" Command="{Binding BackCommand}" />