WPF: Commands and ViewModels relation in MVVM
I am trying to call the same command from two different view models, but I got stuck while designing them (both command and view models).
First, I created a ViewModel1
view model class:
public class ViewModel1 : Dependen开发者_C百科cyObject
{
...
// 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...
...
}
And the ProcessMyString
command class:
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();
}
}
Then, I created the second view model class ViewModel2
, but when I realized that this view model will also need to use the same command, the command's constructor
public ProcessMyString(ViewModel1 viewModel)
will not work because it takes ViewModel1
parameter and I need to be able to pass both view models. Then, I decided to create ViewModelBase
class, and to make both view models extend from it. I modified the command's constructor as well, of course:
// Constructor's parameter is now ViewModelBase
public ProcessMyString(ViewModelBase viewModel)
But that meant that command's method Execute(object parameter)
called a method from ViewModelBase
now. That is not a good appproach because ViewModel's calls for ProcessMyString()
should be reserved for ViewModel1
and ViewModel2
classes only. If I had class ViewModel3
I wouldn't want it to call ProcessMyString()
and if I don't extend it from the ViewModelBase
that would be fine.
But what happens if I need a command that is shared between ViewModel2
and ViewModel3
?
The summation question is: How should I organize my commands and view models to be able to make the view models share the same commands?
First off, just as a personal preference, I tend to minimize the amount of inheritance I use with my ViewModel's. Complex UI code in non-trivial applications can be tricky enough to follow by anyone besides the original author, the last thing you want to do is make it harder by including a complex object model.
The beauty of WPF using the ICommand interface is that you should be able to favor a more compositional approach, rather an an inheritance model, and use an interface to share common properties.
Here's just a quick run-down of how I might approach this scenario:
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.
}
I will post this answer with the assumption that the ProcessMyString
class is unnecessary and should be replaced by a generic command.
First of all, download the library MVVM Light. After that unpack it somewhere and add reference to this library:
(folder with the library)\Mvvm Light Toolkit\Binaries\WPF4\GalaSoft.MvvmLight.WPF4.dll
It contains the RelayCommand
class which is that what you need.
At first create a base class which contains your command:
public abstract class ProcessStringViewModel : DependencyObject
{
// The command property
public RelayCommand ProcessMyStringCommand { get; set; }
}
I would remove the inheritance from the DependencyObject
class, but maybe you use it somehow, so let it be.
The ViewModel1
class can be rewritten in this way:
public class ViewModel1 : ProcessStringViewModel
{
public ViewModel1()
{
// Command gets instantiated
this.ProcessMyStringCommand = new RelayCommand(() => this.ProcessMyString());
}
internal void ProcessMyString()
{
}
}
The ViewModel2
class can call a different function, but the command is the same:
public class ViewModel2 : ProcessStringViewModel
{
public ViewModel2()
{
this.ProcessMyStringCommand = new RelayCommand(SomeOtherFunction);
}
private void SomeOtherFunction()
{
MessageBox.Show("Call of some function");
}
}
If you decide to not use the base class and inheritance - you can remove the base class, copy the property to each derived class, and it will work.
精彩评论