You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中StateContainer切换状态后ListView需滑动才显示问题

.NET MAUI StateContainer切换状态后ListView不显示的解决方法

问题现象

实现Patrick McCurley的StateContainer组件后,首次进入Success状态时ListView显示正常,但状态切换回Loading再切回Success后,ListView完全消失,必须滑动屏幕才会重新出现。尝试在ListView所在视图添加其他元素(Label、Button等)无效;将StateContainer放入带其他元素的Grid中能解决,但页面不允许有额外元素,此方案不可行。

复现代码

以下是完整的复现代码:

States.cs

public enum States
{
    Loading, 
    Success
}

StateCondition.cs

[ContentProperty("Content")]
public class StateCondition : View
{
    public object State { get; set; }
    public View Content { get; set; }
}

StateContainer.cs

[ContentProperty("Conditions")]
public class StateContainer : ContentView
{
    public List<StateCondition> Conditions { get; set; } = new();

    public static readonly BindableProperty StateProperty = 
        BindableProperty.Create(nameof(State), typeof(object), typeof(StateContainer), null, BindingMode.Default, null, StateChanged);

    private static void StateChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var parent = bindable as StateContainer;
        if (parent != null)
            parent.ChooseStateProperty(newValue);
    }

    public object State
    {
        get { return GetValue(StateProperty); }
        set { SetValue(StateProperty, value); }
    }

    private void ChooseStateProperty(object newValue)
    {
        if (Conditions == null && Conditions?.Count == 0) return;

        var stateCondition = Conditions
            .FirstOrDefault(condition =>
                condition.State != null &&
                condition.State.ToString().Equals(newValue.ToString()));

        if (stateCondition == null) return;

        Content = stateCondition.Content;
    }
}

MainPage.xaml

<ContentPage ...>

    <state:StateContainer State="{Binding State}">

        <state:StateCondition State="Loading">
            <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
                <ActivityIndicator IsRunning="True" />
                <Label Text="Updating data..." />
            </StackLayout>
        </state:StateCondition>

        <state:StateCondition State="Success">
            <ListView ItemsSource="{Binding SomeData}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Label Text="{Binding . }" />
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </state:StateCondition>

    </state:StateContainer>

</ContentPage>

MainPage.xaml.cs

public partial class MainPage : ContentPage
{
    private States _state;
    private int[] _someData;

    public MainPage()
    {
        InitializeComponent();
        this.BindingContext = this;

        SomeData = new[] { 1, 2, 3, 4, 5 };
        State = States.Success;

        // it can be executed from outside the page
        _ = Task.Run(ExecuteSomeWorkAsync);
    }

    public States State
    {
        get => _state;
        private set
        {
            if (_state != value)
            {
                _state = value;
                OnPropertyChanged();
            }
        }
    }
    public int[] SomeData
    {
        get => _someData;
        private set
        {
            if (_someData != value)
            {
                _someData = value;
                OnPropertyChanged();
            }
        }
    }

    public async Task ExecuteSomeWorkAsync()
    {
        await Task.Delay(2000);

        State = States.Loading;

        await Task.Delay(2000);

        // generate new data for displaying
        Random rnd = new();
        var data = Enumerable.Range(0, 5).Select(n => rnd.Next(0, 5)).ToArray();

        SomeData = data;
        State = States.Success;
    }
}

解决方法

方法1:修改StateContainer,强制触发布局刷新

在StateContainer的ChooseStateProperty方法中,设置完Content后主动调用布局刷新方法,确保新Content被正确测量和渲染:

private void ChooseStateProperty(object newValue)
{
    if (Conditions == null || Conditions.Count == 0) return;

    var stateCondition = Conditions
        .FirstOrDefault(condition =>
            condition.State != null &&
            condition.State.ToString().Equals(newValue.ToString()));

    if (stateCondition == null) return;

    Content = stateCondition.Content;
    
    // 强制刷新布局,解决ListView不显示问题
    InvalidateMeasure();
    ForceLayout();
}

方法2:给ListView添加外层布局并设置填充属性

在Success状态的ListView外层包裹一个Grid或StackLayout,并设置HorizontalOptions和VerticalOptions为FillAndExpand,确保布局空间被正确分配:

<state:StateCondition State="Success">
    <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <ListView ItemsSource="{Binding SomeData}" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Label Text="{Binding . }" />
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</state:StateCondition>

方法3:使用CollectionView替代ListView(推荐)

MAUI中的CollectionView是ListView的现代化替代方案,布局逻辑更稳定,不易出现这类渲染问题:

<state:StateCondition State="Success">
    <CollectionView ItemsSource="{Binding SomeData}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding . }" Padding="10" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</state:StateCondition>

问题原因

StateContainer切换Content时,MAUI的布局系统可能没有正确触发新Content(ListView)的测量和渲染流程。滑动屏幕会手动触发布局刷新,因此ListView会显示。通过主动调用布局刷新方法、优化布局结构或使用更稳定的CollectionView,都能解决这个问题。

内容的提问来源于stack exchange,提问作者Artiom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 00:31:19