TaskLoaderView comes back in Style with DataTemplate

![]() |
https://github.com/roubachof/Sharpnado.TaskLoaderView |
Since version 2.0, the TaskLoaderView
supports custom views for your Tasks
states.
So you can define, for example, really cool Lottie
views with great animations.
<customViews:TaskLoaderView Grid.Row="2"
Style="{StaticResource TaskLoaderStyle}"
TaskLoaderNotifier="{Binding Loader}">
<customViews:TaskLoaderView.LoadingView>
<lottie:AnimationView x:Name="LoadingLottie"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 120, 120"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="{Binding Loader.ShowLoader,
Converter={StaticResource CyclicLoadingLottieConverter}}"
AutoPlay="True"
Loop="True" />
</customViews:TaskLoaderView.LoadingView>
<customViews:TaskLoaderView.EmptyView>
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 300, 180">
<lottie:AnimationView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="empty_state.json"
AutoPlay="True"
Loop="True" />
<Label Style="{StaticResource TextBody}"
HorizontalOptions="Center"
VerticalOptions="Center"
Text="{loc:Translate Empty_Screen}"
TextColor="White" />
<Button Style="{StaticResource TextBody}"
HeightRequest="40"
Margin="0,20,0,0"
Padding="25,0"
HorizontalOptions="Center"
BackgroundColor="{StaticResource TopElementBackground}"
Command="{Binding Loader.ReloadCommand}"
Text="{loc:Translate ErrorButton_Retry}"
TextColor="White" />
</StackLayout>
</customViews:TaskLoaderView.EmptyView>
<customViews:TaskLoaderView.ErrorView>
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 300, 180">
<lottie:AnimationView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="{Binding Loader.Error,
Converter={StaticResource ExceptionToLottieConverter}}"
AutoPlay="True"
Loop="True" />
<Label Style="{StaticResource TextBody}"
HorizontalOptions="Center"
VerticalOptions="Center"
Text="{Binding Loader.Error, Converter={StaticResource ExceptionToErrorMessageConverter}}"
TextColor="White" />
<Button Style="{StaticResource TextBody}"
HeightRequest="40"
Margin="0,20,0,0"
Padding="25,0"
HorizontalOptions="Center"
BackgroundColor="{StaticResource TopElementBackground}"
Command="{Binding Loader.ReloadCommand}"
Text="{loc:Translate ErrorButton_Retry}"
TextColor="White" />
</StackLayout>
</customViews:TaskLoaderView.ErrorView>
...
</customViews:TaskLoaderView>
So this is great! But we have to copy paste all the custom views each time we want to use the TaskLoaderView
.
It kinds of pollute our view files with the same xaml.
So what if we could just declare our custom views once and apply them transparently to all our TaskLoaderView
?
Styles and DataTemplate
This is when the DataTemplate
comes to the party.
Since 2.1.0 TaskLoaderView
custom views properties supports now classic View
but also DataTemplate
.
We can now declare our custom views as DataTemplate
and reference them in our Styles
:
<ResourceDictionary>
<DataTemplate x:Key="LoadingLottieDataTemplate">
<lottie:AnimationView x:Name="LoadingLottie"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 120, 120"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="{Binding Loader.ShowLoader,
Converter={StaticResource CyclicLoadingLottieConverter}}"
IsPlaying="True"
Loop="True" />
</DataTemplate>
<DataTemplate x:Key="EmptyLottieDataTemplate">
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 300, 180"
BindingContext="{Binding
Source={RelativeSource AncestorType={x:Type customViews:TaskLoaderView}},
Path=TaskLoaderNotifier}">
<lottie:AnimationView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="empty_state.json"
IsPlaying="True"
Loop="True" />
<Label Style="{StaticResource TextBody}"
HorizontalOptions="Center"
VerticalOptions="Center"
Text="{loc:Translate Empty_Screen}"
TextColor="White" />
<Button Style="{StaticResource TextBody}"
HeightRequest="40"
Margin="0,20,0,0"
Padding="25,0"
HorizontalOptions="Center"
BackgroundColor="{StaticResource TopElementBackground}"
Command="{Binding ReloadCommand}"
Text="{loc:Translate ErrorButton_Retry}"
TextColor="White" />
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="ErrorLottieDataTemplate">
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 300, 180"
BindingContext="{Binding
Source={RelativeSource AncestorType={x:Type customViews:TaskLoaderView}},
Path=TaskLoaderNotifier}">
<lottie:AnimationView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Animation="{Binding Error,
Converter={StaticResource ExceptionToLottieConverter}}"
IsPlaying="True"
Loop="True" />
<Label Style="{StaticResource TextBody}"
HorizontalOptions="Center"
VerticalOptions="Center"
Text="{Binding Error, Converter={StaticResource ExceptionToErrorMessageConverter}}"
TextColor="White" />
<Button Style="{StaticResource TextBody}"
HeightRequest="40"
Margin="0,20,0,0"
Padding="25,0"
HorizontalOptions="Center"
BackgroundColor="{StaticResource TopElementBackground}"
Command="{Binding ReloadCommand}"
Text="{loc:Translate ErrorButton_Retry}"
TextColor="White" />
</StackLayout>
</DataTemplate>
<Style x:Key="TaskLoaderStyle" TargetType="customViews:TaskLoaderView">
<Setter Property="FontFamily" Value="{StaticResource FontAtariSt}" />
<Setter Property="NotificationBackgroundColor" Value="{StaticResource TosWindows}" />
<Setter Property="NotificationTextColor" Value="{StaticResource TextPrimaryColor}" />
<Setter Property="LoadingView" Value="{StaticResource LoadingLottieDataTemplate}" />
<Setter Property="EmptyView" Value="{StaticResource EmptyLottieDataTemplate}" />
<Setter Property="ErrorView" Value="{StaticResource ErrorLottieDataTemplate}" />
</Style>
</ResourceDictionary>
We could even create an implicit style, and we won't have to reference any style in our TaskLoaderView
:
<Style TargetType="customViews:TaskLoaderView">
<Setter Property="FontFamily" Value="{StaticResource FontAtariSt}" />
<Setter Property="NotificationBackgroundColor" Value="{StaticResource TosWindows}" />
<Setter Property="NotificationTextColor" Value="{StaticResource TextPrimaryColor}" />
<Setter Property="LoadingView" Value="{StaticResource LoadingLottieDataTemplate}" />
<Setter Property="EmptyView" Value="{StaticResource EmptyLottieDataTemplate}" />
<Setter Property="ErrorView" Value="{StaticResource ErrorLottieDataTemplate}" />
</Style>
I would also like to highlight an unknown feature of Xamarin.Forms
: Relative bindings
Thanks to them, you can easily bind your parent's control properties from anywhere.
It's a perfect fit for our DataTemplates
:
<DataTemplate x:Key="ErrorLottieDataTemplate">
<StackLayout AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5, 0.4, 300, 180"
BindingContext="{Binding
Source={RelativeSource AncestorType={x:Type customViews:TaskLoaderView}},
Path=TaskLoaderNotifier}">
You can find the code here: LottieViewsPage.xaml
But also
- You can set the
ReloadCommand
and theRefreshCommand
, if you have specific scenarios.
<customViews:TaskLoaderView Style="{StaticResource TaskLoaderStyle}"
RefreshCommand="{Binding RefreshCommand}"
ReloadCommand="{Binding ReloadCommand}"
TaskLoaderNotifier="{Binding Loader}">
- The underlying
TaskMonitor
is now exposed, so you can await on theTask
if you have to chain some async code in your view models.
private async Task ChainTasks()
{
await Task.Delay(1000);
// The TaskCompleted property will never raise an exception
await Loader.CurrentLoadingTask.TaskCompleted;
await Task.Delay(1000);
}