63

StateLayout with Collections in Xamarin Forms/MAUI

 2 years ago
source link: https://xamgirl.com/statelayout-with-collections-in-xamarin-forms-maui/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

A time ago Steven Thewissen created a great plugin called StateSquid which allowed displaying a specific view when a page is in a specific state. It has evolved and now it’s part of the Xamarin Community Toolkit.

In this article, I’m going to show you how to use and integrate it to load data into your collections.

Let’s start with the basics

First, install the XamarinCommunityToolkit Package. After that, you will be able to use the StateLayout.

This control allows you to have different views according to a specific state. The view will change automatically according to the current state. For example:

<StakLayout xct:StateLayout.CurrentState="{Binding CurrentState}"> <xct:StateLayout.StateViews> <xct:StateView StateKey="Loading"> <!--Add View for loading here --> </xct:StateView> <xct:StateView StateKey="Error"> <!--Add View for error here --> </xct:StateView> <xct:StateView StateKey="Success"> <!--Add View for success here --> </xct:StateView> </xct:StateLayout.StateViews> </StakLayout>

There’s a list of pre-defined states that you can use (Loading, Error, Success, Saving, etc), but you can also define your own custom state:

<StackLayout xct:StateLayout.CurrentState="{Binding CurrentState}"> <xct:StateLayout.StateViews> <xct:StateView StateKey="Custom" CustomStateKey="ThisIsACustomState"> <!-- Add your view here--> </xct:StateView> </xct:StateLayout.StateViews>

Some extra functionalities are:

StateToBooleanConverter

It will return true if the specified state matches the bound state. To use it you have to pass the state you want to check as the ConverterParameter. For example:

<ContentPage.Resources> <xct:StateToBooleanConverter x:Key="StateToBooleanConverter" /> </ContentPage.Resources>

<ActivityIndicator IsRunning="{Binding CurrentState, Converter={StaticResource StateToBooleanConverter}, ConverterParameter={x:Static xct:LayoutState.Loading}}" />

AnimateStateChanges

Indicates if you want an animated transition when switching between states.

<StackLayout xct:StateLayout.CurrentState="{Binding CurrentState}" xct:StateLayout.AnimateStateChanges="False"> </StackLayout>

How to use it with Collections

1. Create your Collection

Add your CollectionView with your custom template UI. Let’s have the following UI as our example:

CollectionView should be inside a layout, where we will specify the CurrentState.

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="StateLayoutSample.MainPage" xmlns:xct="http://xamarin.com/schemas/2020/toolkit"> <StackLayout xct:StateLayout.CurrentState="{Binding CurrentState}" xct:StateLayout.AnimateStateChanges="False"> <CollectionView ItemsSource="{Binding Users}"> <CollectionView.ItemTemplate> <DataTemplate> <Grid ColumnDefinitions="Auto, *" RowDefinitions="Auto, Auto, Auto, 1" ColumnSpacing="10" RowSpacing="5" Padding="0,10">

<Image Source="{Binding Photo}" Style="{StaticResource Style}" Grid.RowSpan="3" Grid.Row="0" Grid.Column="0"> <Image.Clip> <EllipseGeometry Center="35,35" RadiusX="35" RadiusY="35"/> </Image.Clip> </Image>

<Label Text="{Binding Name}" FontAttributes="Bold" Grid.Column="1" Grid.Row="0"/>

<Label Text="{Binding Email}" Grid.Column="1" Grid.Row="1"/>

<Label Text="{Binding Phone}" Grid.Column="1" Grid.Row="2"/>

<BoxView Style="{StaticResource SeparatorLine}" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2"/> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> </ContentPage>

2. Create an EmptyTemplate

This template will define how the view looks without data. In order to create it we can use BoxViews with grey color and make it look like this:

Screen-Shot-2022-02-25-at-2.57.59-PM.png?resize=473%2C108&ssl=1Empty Template
Original Template

If we want an animation to make it look as if it was loading, you can add a view called SkeletonView (created by Steven) to your project.

public class SkeletonView : BoxView { public SkeletonView() { Device.StartTimer(TimeSpan.FromSeconds(1.5), () => { this.FadeTo(0.5, 750, Easing.CubicInOut).ContinueWith((x) => { this.FadeTo(1, 750, Easing.CubicInOut); });

return true; }); } }

This view is a BoxView with animation, so you can use this view instead of the BoxViews. Also, you can set extra properties to customize it:

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="StateLayoutSample.MainPage" xmlns:xct="http://xamarin.com/schemas/2020/toolkit" xmlns:controls="clr-namespace:StateLayoutSample.Controls" Title="Users"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="controls:SkeletonView"> <Setter Property="BackgroundColor" Value="#E4E4E4"/> <Setter Property="CornerRadius" Value="4" /> <Setter Property="HorizontalOptions" Value="Start" /> </Style>

<Style TargetType="BoxView" x:Key="SeparatorLine"> <Setter Property="Color" Value="LightGray"/> <Setter Property="HorizontalOptions" Value="FillAndExpand" /> <Setter Property="HeightRequest" Value="1" /> </Style>

<DataTemplate x:Key="EmptyUserTemplate"> <Grid ColumnDefinitions="Auto, *" RowDefinitions="Auto, Auto, Auto, 1" ColumnSpacing="10" RowSpacing="5" Padding="0,10">

<controls:SkeletonView Grid.Column="0" Grid.Row="0" Grid.RowSpan="3" Margin="20,0,0,10" HeightRequest="70" WidthRequest="70" HorizontalOptions="Center" VerticalOptions="Center"> <controls:SkeletonView.Clip> <EllipseGeometry Center="35,35" RadiusX="35" RadiusY="35"/> </controls:SkeletonView.Clip>

</controls:SkeletonView>

<controls:SkeletonView Grid.Column="1" Grid.Row="0" HeightRequest="16" WidthRequest="150"/>

<controls:SkeletonView Grid.Column="1" Grid.Row="1" HeightRequest="16" WidthRequest="250"/>

<controls:SkeletonView Grid.Column="1" Grid.Row="2" WidthRequest="150" HeightRequest="16" VerticalOptions="Start"/>

<BoxView Style="{StaticResource SeparatorLine}" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2"/> </Grid> </DataTemplate> </ResourceDictionary> </ContentPage.Resources> </ContentPage>

3. Set the EmptyTemplate to the StateView

The StateView supports setting a template and also you can specify how many times you want to repeat this view. Is important to mention that this is not part of the CollectionView, it is a separate view that will be visible for a specific state.

Set the template to the StateView:

<StackLayout xct:StateLayout.CurrentState="{Binding CurrentState}" xct:StateLayout.AnimateStateChanges="False">

<xct:StateLayout.StateViews> <xct:StateView StateKey="Loading" RepeatCount="8" Template="{StaticResource EmptyUserTemplate}"> </xct:StateView> </xct:StateLayout.StateViews> </StackLayout>

The full code looks like this:

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="StateLayoutSample.MainPage" xmlns:xct="http://xamarin.com/schemas/2020/toolkit" xmlns:controls="clr-namespace:StateLayoutSample.Controls" Title="Users"> <StackLayout xct:StateLayout.CurrentState="{Binding CurrentState}" xct:StateLayout.AnimateStateChanges="False">

<xct:StateLayout.StateViews> <xct:StateView StateKey="Loading" RepeatCount="8" Template="{StaticResource EmptyUserTemplate}"> </xct:StateView> </xct:StateLayout.StateViews> <CollectionView ItemsSource="{Binding Users}"> <CollectionView.ItemTemplate> <DataTemplate> <Grid> ... </Grid> </DataTemplate> </CollectionView.ItemTemplate> </StackLayout> </ContentPage>

Check the full XAML here.

Using it for lazy loading

We can use StateView for data lazy loading, I took this idea from the MVP Sample, which has a great sample for lazy loading.

We will use the Footer of the CollectionView, and make it visible according to if LoadingMoreItem is true. Also, use a BindableLayout.ItemTemplate to set the EmptyUserTemplate that we used before. This will simulate adding a random element by using a Static ItemSource.

<CollectionView ItemsSource="{Binding Users}" RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}" RemainingItemsThreshold="{Binding ItemThreshold}"> <CollectionView.Footer> <StackLayout BindableLayout.ItemTemplate="{DynamicResource EmptyUserTemplate}" IsVisible="{Binding IsLoadingMore}"> <BindableLayout.ItemsSource> <x:Array Type="{x:Type x:Int32}"> <x:Int32>0</x:Int32> </x:Array> </BindableLayout.ItemsSource> </StackLayout> </CollectionView.Footer> </CollectionView>

That’s all for now, you can check the full sample here.

Happy coding!

Like this:

Loading...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK