StateContainer

当应用处于特定状态时显示特定视图是任何移动应用中的常见模式。 示例包括:创建加载视图以覆盖在屏幕上或屏幕的子部分上。 当没有要显示的数据时,可以创建空状态视图,并可在发生错误时显示错误状态视图。

入门

StateContainer 附加属性使用户能够将任何布局元素(如 VerticalStackLayoutHorizontalStackLayoutGrid)转换为状态感知布局。 每个状态感知布局都包含视图派生元素的集合。 这些元素可用作用户针对不同状态定义的模板。 只要将 CurrentState 字符串属性设置为与某一视图元素的 StateKey 属性相匹配的值,就会显示其内容,而不是主要内容。 当 CurrentState 设置为 null 或空字符串时,将显示主要内容。

注意

StateContainerGrid 一起使用时,其中定义的任何状态都会自动跨越 Grid 的每一行和每一列。

语法

StateContainer 属性可在 XAML 或 C# 中使用。

XAML

包括 XAML 命名空间

若要在 XAML 中使用工具包,需要将以下 xmlns 添加到页面或视图中:

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

因此,以下内容:

<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>

将被修改为包括 xmlns,如下所示:

<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>

使用 StateContainer

下面是使用 XAML 创建的示例 UI。 此示例 UI 连接到以下 ViewModel - 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>

C# 标记

以下是使用 C# 标记创建的与上述 XAML 相同的 UI。

此示例 UI 连接到以下 ViewModel - 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.CanStateChange,
    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);
}

视图模型

使用 ICommand 更改 CurrentState 时(例如,使用 Button.Command 更改状态时),建议将 CanStateBeChanged 用于 ICommand.CanExecute()

下面是使用 MVVM 社区工具包的 MVVM 示例:

[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;
    }
}

默认情况下,StateContainer 无需动画即可更改状态。 要添加自定义动画,可以使用 ChangeStateWithAnimation 方法:

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);
    }
}

这是其在 iOS 上的工作原理:

StateContainer Animation

属性

StateContainer

StateContainer 属性可用于任何继承 Layout 的元素。

properties 类型​​ 描述
StateViews IList<View> 要用作状态模板的可用 View 元素。
CurrentState string 确定应显示哪个包含相应 StateKeyView 元素。

警告:状态更改正在进行时无法更改 CurrentState
CanStateChange bool 当值为 true 时,可以更改 CurrentState 属性。 当值为 false 时,无法更改,因为当前正在更改。

警告:如果当 CanStateChangedfalse 时更改 CurrentState,则会引发 StateContainerException

StateView

StateView 属性可用于任何继承 View 的元素。

properties 类型​​ 描述
StateKey string 状态的名称。

方法

StateContainer

方法 参数 说明
ChangeStateWithAnimation(静态) BindableObject bindable, string? state, Animation? beforeStateChange, Animation? afterStateChange, CancellationToken token 使用自定义动画更改状态。
ChangeStateWithAnimation(静态) BindableObject bindable, string? state, Func<VisualElement, CancellationToken, Task>? beforeStateChange, Func<VisualElement, CancellationToken, Task>? afterStateChange, CancellationToken cancellationToken 使用自定义动画更改状态。
ChangeStateWithAnimation(静态) BindableObject bindable, string? state, CancellationToken token 使用默认淡化动画更改状态。

示例

可以在 .NET MAUI 社区工具包示例应用程序中找到此功能的示例。

API

可以在 .NET MAUI 社区工具包 GitHub 存储库查看StateContainer 的源代码