Implementing command objects
In DEM apps, you typically invoke some action in response to a user action (such as a button tap) that can be implemented by creating an event handler in the code-behind file. However, in the MVVM pattern, the responsibility for implementing the action lies with the view model, and you should try to avoid placing code in the code-behind file.
Commands provide a convenient way to represent actions that can be easily bound to controls in the UI. They encapsulate the actual code that implements the action or operation and help to keep it decoupled from its actual visual representation in the view.
The Xamrin.Forms includes controls that can be declaratively connected to a command. These controls will invoke the specified command when the user interacts with the control in a specific way.
DelegateCommand
View models typically expose command properties, for binding from the view, that are object instances that implement the ICommand interface. The Command class from Xamarin.Forms defines an Execute method, which encapsulates the operation itself, and a CanExecute method, which indicates whether the command can be invoked at a particular time. The DEM library provides the DelegateCommand methods to implement commands from base ViewModel class.
Thes methods encapsulates two delegates that each reference a method implemented within your view model class. You specify the delegates to your view model methods in the DelegateCommand metohod, which is defined as follows.
public virtual Command<T> CreateDelegateCommand<T>(Func<T, Task> execute, Func<T, bool> canExecute = null, CommandContext context = null)
{
if (context == null)
context = new CommandContext();
if (context.HandleBusyIndicator)
{
var output = new Command<T>(
RunTask<T>(execute, context.ExceptionHandler, context.LoadingText),
(x) => (canExecute?.Invoke(x) ?? true) && IsBusy);
return output;
}
else
{
var output = new Command<T>(
RunTask<T>(execute, context.ExceptionHandler, context.LoadingText, false),
(x) => (canExecute?.Invoke(x) ?? true));
return output;
}
}
public virtual Command CreateDelegateCommand(Func<Task> execute, CommandContext context = null)
{
if (context == null)
context = new CommandContext();
if (context.HandleBusyIndicator)
{
var output = new Command(RunTask(execute, context.ExceptionHandler, context.LoadingText),
() => IsBusy);
return output;
}
else
{
return new Command(RunTask(execute, context.ExceptionHandler, context.LoadingText, false));
}
}
When the Execute method is called on the Command object, it simply forwards the call to the method in the view model class via the delegate that you specified in method parametars. Similarly, when the CanExecute method is called, the corresponding method in the view model class is called. The delegate to the CanExecute method is optional. If a delegate is not specified, the Command will always return true for CanExecute.
For example, the following code shows how to initalize command in DEM ViewModel, in method paramtar you specify delegate to the Do and CanDo view model methods. The command is then exposed to the view through a public read-only property that returns a reference to an Command.
public Command DoCommand { get; protected set; }
protected override void InitiateCommands()
{
base.InitiateCommands();
DoCommand = CreateDelegateCommand<object>(Do, CanDo);
}
protected bool CanDo(object arg)
{
return arg != null;
}
public async Task<bool> Do(object args)
{
return await Task.FromResult( true);
}
This implementation uses async
/await
, for calling asynchronous methods inside of the Execute
.
There is a implementation for synchronous calls
Command
public virtual Command CreateCommand(Action execute, CommandContext context = null)
{
return new Command(execute);
}
public virtual Command CreateCommand(Action<object> execute, CommandContext context = null)
{
return new Command(execute);
}
public virtual Command<T> CreateCommand<T>(Action<T> execute, CommandContext context = null)
{
return new Command<T>(execute);
}
AsyncCommand
And for some cases we use AsyncCommand
, becouse ICommand
by nature is synchronous.
public class AsyncCommand : Command, IAsyncCommand
{
Func<object, Task> execute;
public AsyncCommand(Func<object, Task> execute)
: base(param => execute(param).ConfigureAwait(false))
{
this.execute = execute;
}
public async Task ExecuteAsync(object parametar)
{
await execute(parametar).ConfigureAwait(false);
}
}
Becouse that we have this implementation and you can call ExecuteAsync
from beheviors for example.
public virtual Command CreateAsyncCommand(Func<object, Task> execute)
{
return new AsyncCommand(execute);
}
Invoking commands from a view
The following code example shows how the Button in the view binds to the DoCommand in the SampleViewModel class.
<Button Text="Click me" Command="{Binding DoCommand}" CommandParameter="{Binding SampleProperty}" />
A command parameter can also be optionally defined using the CommandParameter property. The type
of the expected argument is specified in the CreateDelegateCommand<T>
generic declaration.
RaiseCanExecuteChanged
Use the RaiseCanExecuteChanged
method whenever you need to manually update the state of the bound UI elements.
public virtual void RaiseCanExecuteChanged(Command command)
{
command?.ChangeCanExecute();
}
Behaviors
Behaviors also allow you to connect a control to a command declaratively. However, behaviors can be used to invoke an action that is associated with a range of events raised by a control. Therefore, behaviors address many of the same scenarios as command-enabled controls, while providing a greater degree of flexibility and control. In addition, behaviors can also be used to associate command objects or methods with controls that were not specifically designed to interact with commands. For more info see using the EventToCommandBehavior