WPF:MVVM中的命令和ViewModels关系

时间:2011-04-09 11:50:55

标签: c# wpf mvvm

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

首先,我创建了一个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调用了一个方法。这不是一个好的appproach,因为ViewModel对ProcessMyString()的调用只应为ViewModel1ViewModel2保留。如果我上课ViewModel3我就不希望它调用ProcessMyString(),如果我不从ViewModelBase扩展它就可以了。

但如果我需要在ViewModel2ViewModel3之间共享的命令会怎样?

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

2 个答案:

答案 0 :(得分:7)

首先,作为个人偏好,我倾向于最小化我使用ViewModel的继承量。除了原始作者之外,非平凡应用程序中的复杂UI代码可能非常棘手,除了原作者之外,最后一件事就是通过包含复杂的对象模型来使其变得更难。

使用ICommand接口的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.
}

答案 1 :(得分:1)

我将发布这个答案,假设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");
    }
}

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