KessEkhtak
KessEkhtak

Reputation: 55

How to handle Execute and Can Execute Conditions in dynamic button in MVVM

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.

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

Answers (1)

T McKeown
T McKeown

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:

  • All the tab view models are instances of BaseTabViewModel type.

This mechanism is much more OO and more flexible. Good luck.

Upvotes: 1

Related Questions