StateContainer
当应用处于特定状态时显示特定视图是任何移动应用中的常见模式。 示例包括:创建加载视图以覆盖在屏幕上或屏幕的子部分上。 当没有要显示的数据时,可以创建空状态视图,并可在发生错误时显示错误状态视图。
入门
StateContainer
附加属性使用户能够将任何布局元素(如 VerticalStackLayout
、HorizontalStackLayout
或 Grid
)转换为状态感知布局。 每个状态感知布局都包含视图派生元素的集合。 这些元素可用作用户针对不同状态定义的模板。 只要将 CurrentState
字符串属性设置为与某一视图元素的 StateKey
属性相匹配的值,就会显示其内容,而不是主要内容。 当 CurrentState
设置为 null
或空字符串时,将显示主要内容。
注意
将 StateContainer
与 Grid
一起使用时,其中定义的任何状态都会自动跨越 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
StateContainer 属性可用于任何继承 Layout
的元素。
properties | 类型 | 描述 |
---|---|---|
StateViews | IList<View> |
要用作状态模板的可用 View 元素。 |
CurrentState | string |
确定应显示哪个包含相应 StateKey 的 View 元素。 警告:状态更改正在进行时无法更改 CurrentState |
CanStateChange | bool |
当值为 true 时,可以更改 CurrentState 属性。 当值为 false 时,无法更改,因为当前正在更改。 警告:如果当 CanStateChanged 为 false 时更改 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
的源代码