StateContainer

Mostrar uma vista específica quando a sua aplicação está num estado específico é um padrão comum em qualquer aplicação móvel. Os exemplos vão desde a criação de vistas de carregamento até sobreposições no ecrã, ou numa subseção do ecrã. Podem ser criadas vistas de estado vazias quando não há dados para mostrar, e vistas de estado de erro podem ser exibidas quando ocorre um erro.

Introdução

As propriedades associadas StateContainer permitem ao utilizador transformar qualquer elemento de layout, tal como um VerticalStackLayout, HorizontalStackLayout ou Grid, num layout com reconhecimento de estado. Cada disposição sensível ao estado contém uma coleção de elementos derivados de View. Estes elementos podem ser usados como modelos para diferentes estados definidos pelo utilizador. Sempre que a CurrentState propriedade da cadeia é definida para um valor que corresponde à StateKey propriedade de um dos elementos da View, o seu conteúdo será exibido em vez do conteúdo principal. Quando CurrentState está definida como null ou vazia string, o conteúdo principal é exibido.

Observação

Ao usar StateContainer com um Grid, quaisquer estados definidos dentro dele irão automaticamente abranger todas as linhas e colunas do Grid.

Sintaxe

StateContainer as propriedades podem ser usadas em XAML ou C#.

XAML

Incluindo o namespace XAML

Para usar o kit de ferramentas em XAML, a seguinte xmlns precisa ser adicionada à sua página ou vista.

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"

Por conseguinte, o seguinte:

<ContentPage
    x:Class="CommunityToolkit.Maui.Sample.Pages.MyPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

</ContentPage>

Seria modificado para incluir o xmlns da seguinte forma:

<ContentPage
    x:Class="CommunityToolkit.Maui.Sample.Pages.MyPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">

</ContentPage>

Utilização do StateContainer

Abaixo está um exemplo de interface criada usando XAML. Esta interface de exemplo está ligada ao ViewModel abaixo, StateContainerViewModel.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="MyProject.MyStatePage"
             BindingContext="StateContainerViewModel">

    <VerticalStackLayout 
        toolkit:StateContainer.CurrentState="{Binding CurrentState}"
        toolkit:StateContainer.CanStateChange="{Binding CanStateChange}">

        <toolkit:StateContainer.StateViews>
            <VerticalStackLayout toolkit:StateView.StateKey="Loading">
                <ActivityIndicator IsRunning="True" />
                <Label Text="Loading Content..." />
            </VerticalStackLayout>
            <Label toolkit:StateView.StateKey="Success" Text="Success!" />
        </toolkit:StateContainer.StateViews>

        <Label Text="Default Content" />
        <Button Text="Change State" Command="{Binding ChangeStateCommand}" />

    </VerticalStackLayout>

</ContentPage>

Marcação em C#

Abaixo está a mesma interface do XAML, acima, criada usando Marcação C#.

Esta interface de exemplo está ligada ao ViewModel abaixo, StateContainerViewModel.

using CommunityToolkit.Maui.Layouts;
using CommunityToolkit.Maui.Markup;

BindingContext = new StateContainerViewModel();

Content = new VerticalStackLayout()
{
    new Label()
        .Text("Default Content"),
    
    new Button()
        .Text("Change State")
        .Bind(
            Button.CommandProperty,
            static (StateContainerViewModel vm) => vm.ChangeStateCommand,
            mode: BindingMode.OneTime)
}.Bind(
    StateContainer.CurrentStateProperty,
    static (StateContainerViewModel vm) => vm.CurrentState,
    static (StateContainerViewModel vm, string currentState) => vm.CurrentState = currentState)
 .Bind(
    StateContainer.CanStateChangeProperty,
    static (StateContainerViewModel vm) => vm.CanStateChange,
    static (StateContainerViewModel vm, bool canStateChange) => vm.CanStateChange = canStateChange)
 .Assign(out VerticalStackLayout layout);

var stateViews = new List<View>()
{
    //States.Loading
    new VerticalStackLayout()
    {
        new ActivityIndicator() { IsRunning = true },
        new Label().Text("Loading Content")
    },

    //States.Success
    new Label().Text("Success!")
};

StateView.SetStateKey(stateViews[0], States.Loading);
StateView.SetStateKey(stateViews[1], States.Success);

StateContainer.SetStateViews(layout, stateViews);

static class States
{
    public const string Loading = nameof(Loading);
    public const string Success = nameof(Success);
}

Modelo de Visualização

Ao usar um ICommand para mudar CurrentState (por exemplo, ao usar Button.Command para mudar estados), recomendamos usar CanStateBeChanged para ICommand.CanExecute().

Abaixo está um exemplo de MVVM usando o MVVM Community Toolkit:

[INotifyPropertyChanged]
public partial class StateContainerViewModel
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(ChangeStateCommand))]
    bool canStateChange;

    [ObservableProperty]
    string currentState = States.Loading;

    [RelayCommand(CanExecute = nameof(CanStateChange))]
    void ChangeState()
    {
        CurrentState = States.Success;
    }
}

Por defeito, StateContainer muda de estado sem animação. Para adicionar uma animação personalizada, pode usar o ChangeStateWithAnimation método:

async Task ChangeStateWithCustomAnimation()
{
    var targetState = "TargetState";
    var currentState = StateContainer.GetCurrentState(MyBindableObject);
    if (currentState == targetState)
    {
        await StateContainer.ChangeStateWithAnimation(
            MyBindableObject,
            null,
            (element, token) => element.ScaleTo(0, 100, Easing.SpringIn).WaitAsync(token),
            (element, token) => element.ScaleTo(1, 250, Easing.SpringOut).WaitAsync(token),
            CancellationToken.None);
    }
    else
    {
        await StateContainer.ChangeStateWithAnimation(
            MyBindableObject,
            targetState,
            (element, token) => element.ScaleTo(0, 100, Easing.SpringIn).WaitAsync(token),
            (element, token) => element.ScaleTo(1, 250, Easing.SpringOut).WaitAsync(token),
            CancellationToken.None);
    }
}

É assim que funciona no iOS:

Animação de StateContainer

Propriedades

StateContainer

As propriedades StateContainer podem ser usadas em qualquer Layout elemento herdeiro.

Propriedade Tipo Descrição
StateViews IList<View> Os elementos View disponíveis para serem utilizados como modelos de estado.
Estado Atual string Determina qual View elemento com o correspondente StateKey deve ser exibido.

Aviso: CurrentState não pode ser alterado enquanto estiver em curso uma alteração de estado
CanStateChange bool Quando true, a propriedade CurrentState pode ser alterada. Se false, não pode ser alterado porque está neste momento a ser alterado.

Aviso: Se CurrentState for alterado quando CanStateChanged é false, a StateContainerException é lançada.

StateView

As propriedades StateView podem ser usadas em qualquer View elemento herdeiro.

Propriedade Tipo Descrição
StateKey string Nome do estado.

Methods

StateContainer

Método Argumentos Descrição
ChangeStateWithAnimation (estática) BindableObject bindable, string? estado, Animação? antesEstadoMudança, Animação? afterStateChange, token CancellationToken Muda o estado com animação personalizada.
ChangeStateWithAnimation (estática) BindableObject bindable, string? state, Func<VisualElement, CancellationToken, Task>? beforeStateChange, Func<VisualElement, CancellationToken, Task>? afterStateChange, CancellationToken cancellationToken Muda o estado com animação personalizada.
ChangeStateWithAnimation (estática) BindableObject bindable, string? state, CancellationToken token Altere o estado com a animação de esbatimento predefinida.

Exemplos

Você pode encontrar um exemplo desse recurso em ação no .NET MAUI Community Toolkit Sample Application.

API

Você pode encontrar o código-fonte para StateContainer no repositório GitHub do .NET MAUI Community Toolkit.