Reputation: 55
I have a Button as accessory in my TabControl where 5 TabItem resides. This button copies the different object used in the tabs. Each TabItem has a CustomView (where all its components reside, TextView, TextBox, etc.) I want to be able to implement a different functionality to that Button for each tab, in one command, so I implemented a general Copy Command that checks the current tab index to see we're currently in what tab, but now what is the best way to handle my Cannot execute conditions without repeating my switch case in the Conditions method? For example, the first tab button cannot be executed (disabled) if its object is null, the second tab button cannot be execute when its object is null.
The first tab 'Users' has a CustomerView
The second tab "Activity" has a ActivityView
Code:
public RelayCommandWithCannotExecuteReason Copy
{
get
{
if (_copy == null)
{
_copy = new RelayCommandWithCannotExecuteReason(
x =>
{
switch (SelectedTabIndex) {
case 1:
Clipboard.SetData("First", object1);
break;
case 2:
Clipboard.SetData("Second", object2;
break;
}
}, CanCopyConditions);
}
return _copy;
}
}
Upvotes: 2
Views: 142
Reputation: 12847
Why not subclass the type RelayCommandWithCannotExecuteReason
and create a dedicated command object? All the logic is self contained in the command, all you would be doing is instantiating multiple commands in all your view models.
There is nothing wrong in creating a dedicated command class to handle VM specific logic, that is what programmers do. Creating a specific implementation of an abstraction like ICommand
or your RelayCommandWithCannotExecuteReason
is SOLID.
public class CopyCommand : RelayCommandWithCannotExecuteReason
{
ViewModel _vm;
public CopyCommand( ViewModel vm)
{
_vm = vm;
}
public void Execute(object parameter)
{
switch (_vm.SelectedTabIndex)
{
case 1:
_vm.Clipboard.SetData("First", object1);
break;
case 2:
_vm.Clipboard.SetData("Second", object2;
break;
}
}
public bool CanExecute(object parameter)
{
return true;
}
}
Now you use this command in your VM. There are many easy ways, here is an example, similar to how you have it now...
public CopyCommand Copy
{
get
{
if (_copy == null)
{
_copy = new CopyCommand(this);
}
return _copy;
}
Or create the instance in the constructor or initializer of your main VM:
public ViewModel() //constructor
{
_copy = new CopyCommand(this);
}
public CopyCommand Copy
{
get
{
return _copy;
}
}
If you have a view model instance for each tab and you want to delegate the CanExecute() logic to the "SelectedTab" then you'll need something like this:
Let's assume all your view models inherit a base class like this one:
public abstract class BaseTabViewModel
{
public abstract bool CanCopy();
}
Then you would sub class this base class in your tab view models. You would implement the abstraction:
public class Tab1ViewModel : BaseTabViewModel
{
…
public override bool CanCopy(){
//custom logic....
return true;
}
Now we need to change order CopyCommand to reference the abstraction, I originally coded this command to use ViewModel
, now we will instead define the type as the abstraction. So change the ViewModel class:
public class CopyCommand : RelayCommandWithCannotExecuteReason
{
BaseTabViewModel _vm;
public CopyCommand( BaseTabViewModel vm)
{
_vm = vm;
}
Now the CanExecute() method can call the appropriate logic:
public bool CanExecute(object parameter)
{
return _vm.CanCopy();
}
This only works if:
BaseTabViewModel
type.This mechanism is much more OO and more flexible. Good luck.
Upvotes: 1