I am searching a pattern to keep SOLID principles in my application when I use ICommand. Basically my problem is the command execution has a dependency with the view model but at the same time the view model has a dependency with the command (I inject them by constructor). I would like to keep the viewmodel with properties only, so this is an example of my current implementation:
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel()
{
}
public void SetCommand(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand();
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
private readOnly MyViewModel myViewModel;
public MyViewModelCommandManager(MyViewModel myViewModel)
{
this.myViewModel = myViewModel;
}
public ExectueMyCommand()
{
MessageBox.Show(this.myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
MyViewModel viewModel = new MyViewModel();
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("viewModel", viewModel) });
ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);
viewModel.SetCommand(myCommand);
return viewModel;
}
}
So, to avoid use the SetCommand method. I have thought two solutions but I don't know if they are elegant.
The first one is to move the viewmodel dependency from the constructor to the method updating the code in this way:
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand(MyViewModel viewModel);
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
public MyViewModelCommandManager()
{
....
}
public ExectueMyCommand(MyViewModel viewModel)
{
MessageBox.Show(myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(..);
ICommand myCommand = new DelegateCommand<MyViewModel>(manager.ExecuteMyCommand);
MyViewModel viewModel = new MyViewModel(myCommand);
return viewModel;
}
}
Of course, the xaml code will use CommandParameter:
<Button Content="Show Message" Command="{Binding MyCommand}" CommandParameter="{Binding .}" />
Other solution I have thought is to use a trick creating a Wrapper of the viewModel and the commandManager have a dependency with the Wrapper instead of the viewModel:
internal class MyViewModelCommandContext
{
public MyViewModel ViewModel { get; set; }
}
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand();
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
private readonly MyViewModelCommandContext context;
public MyViewModelCommandManager(MyViewModelCommandContext context)
{
this.context = context;
....
}
public ExectueMyCommand()
{
MessageBox.Show(this.context.myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
MyViewModelCommandContext context = new MyViewModelCommandContext();
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("context", context) });
ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);
MyViewModel viewModel = new MyViewModel(myCommand);
context.ViewModel = viewModel;
return viewModel;
}
}
In my opinion the first one is the best solution for this problem, what do you think is the best solution. Would you apply another solution?