SkillAgentSearch skills...

Sharpnado.TaskLoaderView

Free yourself from IsBusy=true! The `TaskLoaderView` is a UI component that handles all your UI loading state (Loading, Error, Result, Notification), and removes all the pain of async loading from your view models (try catch / async void / IsBusy / HasErrors / base view models / ...) thanks to its brother the `TaskLoaderNotifier`.

Install / Use

/learn @roubachof/Sharpnado.TaskLoaderView
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

TaskLoaderView 2.0: Let's burn IsBusy=true!

The TaskLoaderView is a UI component that handles all your UI loading state (Loading, Error, Result, Notification), and removes all the pain of async loading from your view models (try catch / async void / IsBusy / HasErrors / base view models / ...) thanks to its brother the TaskLoaderNotifier.

| MAUI Supported platforms | |----------------------------| | <img src="Docs/maui_logo.png" height="100" /> | | Nuget | | :heavy_check_mark: Android | | :heavy_check_mark: iOS | | :heavy_check_mark: Windows | | :heavy_check_mark: macOS |

Featuring:

  • Default layout for all loading states (Loading, Error, Success, Notification, Refresh)
  • Stylable layouts including fonts, accent colors, error images, ...
  • Support for async ICommand with TaskLoaderCommand and CompositeTaskLoaderNotifier
  • Any states are overridable with user custom views and easily positioned with AbsoluteLayout properties
  • Support for Xamarin.Forms.Skeleton nuget package
  • Support for refresh scenarios, and error while refreshing with the ErrorNotificationView
  • Support loading task on demand with the NotStarted state
  • TaskLoaderNotifier for the ViewModel side taking care of all the error handling and the IsBusy nonsense

banner

It has been tested on Android, iOS, Windows and MacOS platforms through the Retronado sample app.

It uses the Sharpnado's TaskMonitor.

Installation

MAUI

Add the Sharpnado.Maui.TaskLoaderView nuget package.

And call the initializer in your MauiProgram.cs file:

public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureTaskLoader(true) // logger enabled
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		return builder.Build();
	}
}

2.5.0 MAUI support \o/ and TemplatedTaskLoader

Version 2.5.0 now supports .Net MAUI.

New TemplatedTaskLoader: it does the same job as the TaskLoaderView but using ControlTemplate instead of a absolute layout of views.

 <cv:TemplatedTaskLoader x:Name="LoaderView"
                         Grid.Row="1"
                         Style="{StaticResource TemplatedLoaderLongLoading}"
                         TaskLoaderNotifier="{Binding Loader}">
    <cv:TemplatedTaskLoader.ResultControlTemplate>
        <ControlTemplate>
            ...
        </ControlTemplate>
    </cv:TemplatedTaskLoader.ResultControlTemplate>
    <cv:TemplatedTaskLoader.ErrorControlTemplate>
        <ControlTemplate>
            ...
        </ControlTemplate>
    </cv:TemplatedTaskLoader.ErrorControlTemplate>
    <cv:TemplatedTaskLoader.LoadingControlTemplate>
        <ControlTemplate>
            ...
        </ControlTemplate>
    </cv:TemplatedTaskLoader.LoadingControlTemplate>
</cv:TemplatedTaskLoader>

...

<Style x:Key="TemplatedLoaderLongLoading"
       TargetType="customViews:TemplatedTaskLoader">
    <Setter Property="LoadingControlTemplate" Value="{StaticResource LottieRocketControlTemplate}" />
    <Setter Property="ErrorControlTemplate" Value="{StaticResource ErrorViewControlTemplate}" />
</Style>

<ControlTemplate x:Key="LottieRocketControlTemplate">
    <forms:AnimationView HorizontalOptions="Center"
                         VerticalOptions="Center"
                         HeightRequest="200"
                         WidthRequest="200"
                         Animation="delivery_truck_animation.json"
                         IsAnimating="{Binding Source={RelativeSource AncestorType={x:Type customViews:TemplatedTaskLoader}},
                                                       Path=TaskLoaderNotifier.ShowLoader}"
                         RepeatMode="Infinite" />

<ControlTemplate x:Key="ErrorViewControlTemplate">
    <StackLayout HorizontalOptions="Center"
                 VerticalOptions="Center"
                 BindingContext="{Binding Source={RelativeSource AncestorType={x:Type customViews:TemplatedTaskLoader}},
                                          Path=TaskLoaderNotifier}"
                 IsVisible="False"
                 Orientation="Vertical"
                 Spacing="10">
        <Frame Style="{StaticResource FrameCircle}"
               WidthRequest="{StaticResource SizeTaskLoaderIcon}"
               HeightRequest="{StaticResource SizeTaskLoaderIcon}"
               Margin="0,0,0,10"
               BackgroundColor="{StaticResource ColorPrimary}">
            <Image HorizontalOptions="Center"
                   VerticalOptions="Center"
                   Source="{Binding Error,
                                    Converter={converters:ExceptionToImageSourceConverter}}" />
        </Frame>
        <Label Style="{StaticResource TextBodySecondary}"
               WidthRequest="300"
               Margin="0,0,0,20"
               HorizontalTextAlignment="Center"
               LineBreakMode="WordWrap"
               MaxLines="2"
               Text="{Binding Error,
                              Converter={converters:ExceptionToErrorMessageConverter}}" />
        <sho:Shadows CornerRadius="10"
                     Shades="{StaticResource ShadowAccentBottom}">
            <Button Style="{StaticResource ButtonAccent}"
                    HorizontalOptions="Center"
                    VerticalOptions="End"
                    Command="{Binding ReloadCommand}"
                    Text="{x:Static loc:GlobalResources.Common_Retry}" />
        </sho:Shadows>
    </StackLayout>
</ControlTemplate>

For all new developments, I recommend now to use the TemplatedTaskLoader, and use it with a Snackbar to handle the error notifications.

There is an detailed example on how to use it here:

https://github.com/roubachof/Sharpnado.TaskLoaderView/tree/master/Retronado.Maui/Views/CommandsPage.xaml

and here:

https://github.com/roubachof/Sharpnado.TaskLoaderView/tree/master/Retronado.Maui/ViewModels/CommandsPageViewModel.cs

New CompositeTaskLoaderNotifier builder and simplified wiring

You can use the new builder for the CompositeTaskLoader that will wire for you the ShowErrorNotification of your loaders, and the errors from your TaskLoaderCommand:

CompositeNotifier = CompositeTaskLoaderNotifier.ForCommands()
    .WithLoaders(Loader)
    .WithCommands(BuyGameCommand, PlayTheGameCommand)
    .Build();

And bind the ShowLastError and LastError property to your Snackbar:

    <ContentPage.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="60" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <views:NavigationToolBar Title="{loc:Translate Commands_Title}"
                                     Grid.Row="0"
                                     BackgroundColor="{StaticResource TopElementBackground}"
                                     Theme="Standard" />

            <RefreshView Grid.Row="1"
                         IsRefreshing="{Binding Loader.ShowRefresher}"
                         RefreshColor="{StaticResource AccentColor}"
                         Command="{Binding Loader.RefreshCommand}">
                <ScrollView>
                    <tlv:TemplatedTaskLoader TaskLoaderNotifier="{Binding Loader}">
                        <tlv:TemplatedTaskLoader.ResultControlTemplate>
                            <ControlTemplate>
                                <Grid RowDefinitions="300,*"
                                      x:DataType="viewModels:CommandsPageViewModel"
                                      BindingContext="{Binding Source={RelativeSource
                                            AncestorType={x:Type viewModels:CommandsPageViewModel}}}">
                                    <Image Grid.Row="0"
                                           skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
                                           skeleton:Skeleton.IsBusy="{Binding Loader.ShowLoader}"
                                           Aspect="Fill"
                                           Source="{Binding Loader.Result.ScreenshotUrl}" />

                                    <sho:MaterialFrame Grid.Row="0"
                                                       Padding="15,5,15,30"
                                                       VerticalOptions="End"
                                                       CornerRadius="0"
                                                       MaterialBlurStyle="Dark"
                                                       MaterialTheme="AcrylicBlur">
                                        <Grid Padding="0"
                                              ColumnDefinitions="*,60"
                                              RowDefinitions="40,20,20"
                                              RowSpacing="0">
                                            <Label Grid.Row="0"
                                                   Grid.Column="0"
                                                   Style="{StaticResource GameName}"
                                                   Margin="0,0,10,0"
                                                   skeleton:Skeleton.BackgroundColor="{StaticResource GreyBackground}"
                                                   skeleton:Skeleton.IsBusy="{Binding Loader.ShowLoader}"
                                                   Text="{Binding Loader.Result.Name}" />
View on GitHub
GitHub Stars245
CategoryDevelopment
Updated1mo ago
Forks15

Languages

C#

Security Score

95/100

Audited on Feb 8, 2026

No findings