WPF:命令和视图模型在MVVM中的关系

本文关键字:MVVM 关系 模型 命令 视图 WPF | 更新日期: 2023-09-27 17:49:38

我试图从两个不同的视图模型调用相同的命令,但我在设计它们(命令和视图模型)时卡住了。

首先,我创建了一个ViewModel1视图模型类:
public class ViewModel1 : DependencyObject
{
    ...
    // The command property
    public ProcessMyString ProcessMyStringCommand { get; set; }
    public ViewModel1()
    {
        // Command gets instantiated 
        this.ProcessMyStringCommand = new ProcessMyString(this);
    }
    internal void ProcessMyString()
    {
        // This is where the actual processing method is called
        // somewhere from the business logic...
        ...
    }

ProcessMyString命令类:

public class ProcessMyString : ICommand
{
    private ViewModel1 viewModel;
    public ProcessMyString(ViewModel1 viewModel)
    {
        this.viewModel = viewModel;
    }
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter)
    {
        viewModel.ProcessMyString();
    }
}
然后,我创建了第二个视图模型类ViewModel2,但是当我意识到这个视图模型也需要使用相同的命令时,命令的构造函数
public ProcessMyString(ViewModel1 viewModel)

将不起作用,因为它需要ViewModel1参数,我需要能够传递两个视图模型。然后,我决定创建ViewModelBase类,并使两个视图模型从它扩展。当然,我也修改了命令的构造函数:

// Constructor's parameter is now ViewModelBase
public ProcessMyString(ViewModelBase viewModel)

但这意味着命令的方法Execute(object parameter)现在调用ViewModelBase的方法。这不是一个好方法,因为ViewModel对ProcessMyString()的调用应该是ViewModel1ViewModel2类保留的。如果我有类ViewModel3,我不希望它调用ProcessMyString(),如果我不从ViewModelBase扩展它,那就好了。

但是如果我需要一个在ViewModel2ViewModel3之间共享的命令会发生什么呢?

总结问题是:我应该如何组织我的命令和视图模型,以便能够使视图模型共享相同的命令?

WPF:命令和视图模型在MVVM中的关系

首先,作为个人偏好,我倾向于最小化我在ViewModel中使用的继承量。重要应用程序中的复杂UI代码可能非常棘手,除了原作者之外的任何人都无法理解,您最不想做的就是通过包含复杂的对象模型来使其变得更加困难。

使用iccommand接口的WPF的美妙之处在于,您应该能够偏爱更组合的方法,而不是继承模型,并使用接口来共享公共属性。

下面是我如何处理这种情况的一个快速概述:

public class ProcessStringCommand : ICommand
{
   private readonly IProcessStringViewModel m_viewModel;
   public ProcessStringCommand(IProcessStringViewModel vm)
   {
      m_viewModel = vm;
   }
   public void Execute(object param)
   {
      ProcessString(m_viewModel.ProcessString);
   }
   public bool CanExecute(object param)
   { 
      return true; 
   }
   private void ProcessString(string processString)
   {
      // Put logic here
   }
}
public interface IProcessStringViewModel
{
   public string ProcessString { get; }
}
public class ViewModel1 : ViewModelBase, IProcessStringViewModel
{
   private readonly ICommand m_command;
   private readonly string m_processString;
   public ViewModel1()
   {
      m_command = new ProcessStringCommand(this);
   }
   public string ProcessString
   {
      get { return m_processString; }
   }
   public ICommand ProcessStringCommand
   {
      get { return m_command; }
   }
}
public class ViewModel2 : ViewModelBase, IProcessStringViewModel
{
   private readonly ICommand m_command;
   private readonly string m_processString;       
   public ViewModel2()
   {
      m_command = new ProcessStringCommand(this);
   }
   public string ProcessString
   {
      get { return m_processString; }
   }
   public ICommand ProcessStringCommand
   {
      get { return m_command; }
   }
}
public class ViewModel3 : ViewModelBase
{
   // Whatever you need here.
}

我将发布这个答案,假设ProcessMyString类是不必要的,应该用通用命令代替。

首先,下载库MVVM Light。然后将其解包到某个地方,并添加对该库的引用:

(文件夹与库)'Mvvm Light工具箱'二进制文件' WPF4 ' GalaSoft.MvvmLight.WPF4.dll

它包含RelayCommand类,这是你需要的。

首先创建一个包含命令的基类:

public abstract class ProcessStringViewModel : DependencyObject
{
    // The command property
    public RelayCommand ProcessMyStringCommand { get; set; }
}

我会从DependencyObject类中删除继承,但也许你会以某种方式使用它,所以让它去吧。

ViewModel1类可以这样重写:

public class ViewModel1 : ProcessStringViewModel
{
    public ViewModel1()
    {
        // Command gets instantiated 
        this.ProcessMyStringCommand = new RelayCommand(() => this.ProcessMyString());
    }
    internal void ProcessMyString()
    {
    }
}

ViewModel2类可以调用不同的函数,但命令是相同的:

public class ViewModel2 : ProcessStringViewModel
{
    public ViewModel2()
    {
        this.ProcessMyStringCommand = new RelayCommand(SomeOtherFunction);
    }
    private void SomeOtherFunction()
    {
        MessageBox.Show("Call of some function");
    }
}

如果您决定不使用基类和继承-您可以删除基类,将属性复制到每个派生类,它将工作。