使用MVVM在单个窗口中的多个视图-需要更多信息

本文关键字:视图 信息 使用 单个 窗口 MVVM | 更新日期: 2023-09-27 17:50:11

好的,所以我找到了WPF MVVM导航视图,Sheridan对如何在单个窗口中导航多个视图做了(大部分)很好的解释。根据代码和解释,我能够使用后面的代码获得视图切换,但是我无法获得他的XAML Command=""版本的视图切换工作。

我不得不四处寻找与他的输入相匹配的ActionCommand()版本,但我甚至不确定它是正确的,而且我根本找不到IsViewModelOfType<T>()方法上的任何东西。这意味着给出的最终解是部分破碎的。

如果没有第三方库,我如何使最后一部分工作?

不幸的是,因为StackOverflow讨厌新用户参与任何事情,所以我不能在同一个线程中询问。

从上面的帖子,这是不工作的部分,因为这两个部分- ActionCommand和IsViewModelOfType -似乎不存在任何地方,没有他们的信息搜索在线

最后,我们如何从其他视图更改视图?是有的有几种可能的方法,但最简单的方法是加a控件中的iccommand直接与子视图绑定MainViewModel。我使用relaycommand的自定义版本,但您可以使用任何你喜欢的类型,我猜你会得到图片:

public ICommand DisplayPersonView
{
    get { return new ActionCommand(action => ViewModel = new PersonViewModel(), 
        canExecute => !IsViewModelOfType<Person>()); }
}

子视图XAML:

<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource=
    {RelativeSource AncestorType={x:Type MainViewModel}}, Mode=OneWay}" />

更新:我试着从我自己的IsViewModelOfType<T>()(以及实际测试)中返回TrueFalse,但无济于事。问题的关键似乎是ActionCommand()方法,我没有任何基础可以继续,或者XAML本身是不正确的。

我能找到的ActionCommand()最接近的匹配来自Microsoft.Expression.Interactivity.Core,但它只接受一个参数,并且在这种用法中表现不正确。

我也尝试了我在网上找到的这个代码片段,但是当使用Sheridan的代码时,它没有做任何事情

class ActionCommand : ICommand
{
    private readonly Action<object> _executeHandler;
    private readonly Func<object, bool> _canExecuteHandler;
    public ActionCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("Execute cannot be null");
        _executeHandler = execute;
        _canExecuteHandler = canExecute;
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter)
    {
        _executeHandler(parameter);
    }
    public bool CanExecute(object parameter)
    {
        if (_canExecuteHandler == null)
            return true;
        return _canExecuteHandler(parameter);
    }
}

使用MVVM在单个窗口中的多个视图-需要更多信息

我会说,从我在答案中看到的代码,你参考的IsViewModelOfType<Person>()方法是使用某种类型的映射,从给定的对象类型获得视图模型类型。所以代码

public ICommand DisplayPersonView
{
    get { return new ActionCommand(action => ViewModel = new PersonViewModel(), 
        canExecute => !IsViewModelOfType<Person>()); }
}

将引用类似于

的泛型方法
public bool IsViewModelOfType<T>()
{
    Type type = typeof(T);
    if (!viewModelMapping.ContainsKey(type))
        return false;
    return viewModelMapping[type] == typeof(this);
}

上面的this指的是当前的视图模型,你可能有视图模型类型到object类型映射字典,如

public Dictionary<Type, Type> viewModelMapping = 
    new Dictionary<Type, Type>()
{
    { typeof(Person), typeof(PersonViewModel) }, 
    ...
};

Sheriden在上面的命令中所说的是,如果类型不是Person,那么不允许显示PersonViewModel。你可以随心所欲地强加这种逻辑,而不必遵循Sheriden的方法。

我希望这对你有帮助。


阅读你的编辑,我会首先尝试删除RelativeSource代码,我不认为这是必要的。好了,现在进入命令类。这是我在开始使用MVVM框架之前使用的:

using System;
using System.Diagnostics;
using System.Windows.Input;
namespace ResourceStudio.Commands
{
   /// <summary>
   /// A command whose sole purpose is to relay its functionality to other
   /// objects by invoking delegates. The default return value for the CanExecute
   /// method is 'true'.
   /// </summary>
   public class RelayCommand : ICommand
   {
      readonly Action<object> execute;
      readonly Predicate<object> canExecute;
      #region Constructors
      /// <summary>
      /// Creates a new command that can always execute.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      public RelayCommand(Action<object> execute)
         : this(execute, null)
      {
      }
      /// <summary>
      /// Creates a new command.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      /// <param name="canExecute">The execution status logic.</param>
      public RelayCommand(Action<object> execute, Predicate<object> canExecute)
      {
         if (execute == null)
            throw new ArgumentNullException("execute");
         this.execute = execute;
         this.canExecute = canExecute;
      }
      #endregion // Constructors
      #region ICommand Members
      [DebuggerStepThrough]
      public bool CanExecute(object parameter)
      {
         return this.canExecute == null ? true : this.canExecute(parameter);
      }
      /// <summary>
      /// Can execute changed event handler.
      /// </summary>
      public event EventHandler CanExecuteChanged
      {
         add { CommandManager.RequerySuggested += value; }
         remove { CommandManager.RequerySuggested -= value; }
      }
      public void Execute(object parameter)
      {
         this.execute(parameter);
      }
      #endregion // ICommand Members
   }
}

MenuItem点击的用法如下:

<MenuItem Header="E_xit" 
          Command="{Binding CloseApplicationCommand}"/>

其中CloseApplicationCommand

private RelayCommand closeApplicationCommand;
public RelayCommand CloseApplicationCommand
{
    get
    {
        return closeApplicationCommand ?? (closeApplicationCommand =
            new RelayCommand(o => this.CloseApplication(), o => CanCloseApplication));
    }
}

其中CanCloseApplication是布尔属性。

好了,现在,如果你想将命令设置为触发一个特定的事件,比如按钮MouseOver事件,那么你必须写一些像

这样的东西
<Button Content="SomeButton">
    <Interactivity:Interaction.Triggers>
      <Interactivity:EventTrigger EventName="MouseOver">
         <Interactivity:InvokeCommandAction Command="{Binding SomeButtonMouseOverCommand}" />
      </Interactivity:EventTrigger>
   </Interactivity:Interaction.Triggers>
</Button>

使用xmlns:Interactivity="http://schemas.microsoft.com/expression/2010/interactivity"命名空间。然而,一个更好的方法是使用AttachedCommandBehaviours(如果你反对切换到MVVM框架(例如Claiburn))。微等人))。你可以在这里下载AttachedCommandBehavious。然后你可以写

<Button Content="Select" 
        AttachedCommand:CommandBehavior.Event="Click" // Or what ever event you want!
        AttachedCommand:CommandBehavior.Command="{Binding DoSomethingCommand}" />

我会认真考虑使用MVVM框架。然而,在开始之前理解这些东西是很好的。

问题就在这里

子视图XAML:

<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource=
    {RelativeSource AncestorType={x:Type MainViewModel}}, Mode=OneWay}" />

这根本没有发射任何东西。根据Killercam的反馈使用

<Button Command="{Binding DisplayPersonView}" />

其中DisplayPersonView()也触发一个事件OnViewModelChanged,该事件包含在所有视图模型继承的BaseViewModel中。

不幸的是,我不得不稍微打破MVVM的做法,通过在

后面的窗口代码中添加一个处理程序
private void UpdateView(object sender, EventArgs e)
{
    DataContext = (BaseViewModel)sender;
    ((BaseViewModel)DataContext).OnViewModelChange += UpdateView;
}

在窗口的构造函数中,我调用UpdateView()来设置初始的ViewModel

无论上面描述的ActionCommand()RelayCommand()都很好,有或没有canExecute()都很好。