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 : 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...
...
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With