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()
的调用应该是为ViewModel1
和ViewModel2
类保留的。如果我有类ViewModel3
,我不希望它调用ProcessMyString()
,如果我不从ViewModelBase
扩展它,那就好了。
但是如果我需要一个在ViewModel2
和ViewModel3
之间共享的命令会发生什么呢?
总结问题是:我应该如何组织我的命令和视图模型,以便能够使视图模型共享相同的命令?
首先,作为个人偏好,我倾向于最小化我在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");
}
}
如果您决定不使用基类和继承-您可以删除基类,将属性复制到每个派生类,它将工作。