Friday, 5 April 2019

Xamarin's Command implementation of ICommand

When using Xamarin forms Command class which Implements the ICommand interface, there is one strange caveat to look out for, and that is that the CanExecute func must be declared within the constructor of the ViewModel or have a "Lazy" implementation otherwise it will never fire. What i mean is if you have the following xaml


<StackLayout Grid.Row="2" Orientation="Horizontal" Margin="10,5">
    <Entry Placeholder="Task name" Text="{Binding TaskName, Mode=TwoWay}"
        HorizontalOptions="FillAndExpand" />
    <Button Text="+" FontSize="Large" FontAttributes="Bold" WidthRequest="70"
        BackgroundColor="{StaticResource ContrastColor}"
        TextColor="{StaticResource Backgorund}"
        Command="{Binding AddTaskCommand}" />
</StackLayout>


and it's coupled with this codebehind


string taskName;
public string TaskName
{
    get => taskName;
    set
    {
        base.SetProperty(ref taskNamevalue);
        ((Command)AddTaskCommand).ChangeCanExecute();
    }
}

public ICommand AddTaskCommand { get => new Command(
    execute: () => TaskName = string.Empty,
    canExecute: () => true);
}


Then the canExecute func is evaluated on the initial page load and the execute action is executed whenever the command is called, however it is never called again, namely when the ChangeCanExecute method is called from the TaskName's setter the condition is never reevaluated and the add button remains in whatever state it was on the initial page load.

Let's add some logic into the can execute func


string taskName;
public string TaskName
{
    get => taskName;
    set
    {
        base.SetProperty(ref taskNamevalue);
        ((Command)AddTaskCommand).ChangeCanExecute();
    }
}

public ICommand AddTaskCommand
{
    get => new Command(
        execute: () => TaskName = string.Empty,
        canExecute: () => !String.IsNullOrEmpty(TaskName));
}


now under these conditions the "Add" button shall never become enabled. Now if we simply assign our command in the constructor of the view model everything works just fine.


string taskName;
public string TaskName {
    get => taskName;
    set {
        base.SetProperty(ref taskNamevalue);
        ((Command)AddTaskCommand).ChangeCanExecute();
    }
}

public ICommand AddTaskCommand { getprivate set; }

public ProjectPageViewModel()
{
    AddTaskCommand = new Command(
        execute: () => TaskName = string.Empty,
        canExecute: () => !String.IsNullOrEmpty(TaskName));
}


bizarre yes, why you may ask? well a kind soul pointed it out to me that in the above implementation every single time you call the AddTaskCommand you receive a new instance of the Command implementation, which is why it works when you assign it in your constructor but not when you define it inline. So another alternative to defining all your Commands in your constructor is to use a backing field for the AddTaskCommand property and use a lazy implementation for your commands.


string taskName;
public string TaskName
{
    get => taskName;
    set {
        base.SetProperty(ref taskNamevalue);
        ((Command)AddTaskCommand).ChangeCanExecute();
    }
}

ICommand addTaskCommand = null;
public ICommand AddTaskCommand {
    get {
        this.addTaskCommand = this.addTaskCommand ?? new Command(
            execute: () => TaskName = string.Empty,
            canExecute: () => !String.IsNullOrEmpty(TaskName));
        return addTaskCommand;
    }
}


in the above we check if the addTaskCommand backing field is null, if yes then we assign an instance of command to it, if not then we just return it, this keeps us from having to define all of our commands within the constructor.