TaskLoaderCommand: a bindable AsyncCommand for Xamarin.Forms
https://github.com/roubachof/Sharpnado.TaskLoaderView | |
A short blog here to showcase a undocumented feature of the TaskLoaderView
: the TaskLoaderCommand
.
You probably read many blogs about how you should implement a AsyncCommand
instead of a regular Command
for your Task
based code.
Of course, like all async mvvm patterns, the original idea comes from Stephen Cleary
But what if we could merge the power of TaskLoaderNotifier
with the classic ICommand
interface?
The TaskLoaderCommand
will take as parameter a function returning a Task
, and will wrap it in a NotifyTaskNotifier
. You can then bind your TaskLoaderView
to the NotifyTaskNotifier
exposed by your TaskLoaderCommand
.
In the Retronado sample, it is used for loading on demand a specific game view.
public class LoadOnDemandViewModel : Bindable
{
private readonly IRetroGamingService _retroGamingService;
public LoadOnDemandViewModel(IRetroGamingService retroGamingService)
{
_retroGamingService = retroGamingService;
RandomGameLoaderCommand = new TaskLoaderCommand<object, Game>(_ => GetRandomGame());
}
public TaskLoaderCommand<object, Game> RandomGameLoaderCommand { get; }
private async Task<Game> GetRandomGame()
{
await Task.Delay(TimeSpan.FromSeconds(4));
if (DateTime.Now.Millisecond % 2 == 0)
{
throw new NetworkException();
}
return await _retroGamingService.GetRandomGame();
}
}
<sharpnado:TaskLoaderView Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
AccentColor="{StaticResource AccentColor}"
ErrorImageConverter="{StaticResource ExceptionToImageSourceConverter}"
ErrorMessageConverter="{StaticResource ExceptionToErrorMessageConverter}"
FontFamily="{StaticResource FontAtariSt}"
TaskLoaderNotifier="{Binding RandomGameLoaderCommand.Notifier}"
TaskLoaderType="ResultAsLoadingView"
TextColor="Black">
<sharpnado:TaskLoaderView.NotStartedView>
<Button AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 120, 50"
Style="{StaticResource ButtonTextIt}"
Command="{Binding RandomGameLoaderCommand}" />
</sharpnado:TaskLoaderView.NotStartedView>
<Frame Style="{StaticResource CardStyle}"
Margin="-15,0,-15,-15"
Padding="0"
skeleton:Skeleton.Animation="Fade"
skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
skeleton:Skeleton.IsParent="True"
BackgroundColor="{DynamicResource CellBackgroundColor}"
BindingContext="{Binding RandomGameLoaderCommand.Notifier}"
CornerRadius="10"
IsClippedToBounds="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="180" />
<RowDefinition Height="40" />
<RowDefinition Height="20" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
Aspect="Fill"
Source="{Binding Result.ScreenshotUrl}" />
<Label Grid.Row="1"
Style="{StaticResource GameName}"
Margin="15,0"
skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
Text="{Binding Result.Name}" />
<Label Grid.Row="2"
Style="{StaticResource GameCompany}"
Margin="15,0"
skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
skeleton:Skeleton.IsBusy="{Binding ShowLoader}"
Text="{Binding Result.MajorCompany}" />
<Label Grid.Row="3"
Style="{StaticResource GameGenre}"
Margin="15,0"
Text="{Binding Result.MajorGenre}" />
</Grid>
</Frame>
</sharpnado:TaskLoaderView>
You can see that the TaskLoaderView
is simply bound to the exposed TaskLoaderNotifier
of the command:
TaskLoaderNotifier="{Binding RandomGameLoaderCommand.Notifier}"
Here the TaskLoaderView
uses the special ResultAsLoadingView
loader type.
It forces the TaskLoaderView
to display the result content instead of the classic LoadingView
while in the loading state. This is the way to go to use the Horus Xamarin.Forms.Skeleton component.