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

.NET MAUI中CollectionView绑定Expander的交互异常问题

CollectionView绑定Expander的交互问题修复

需求

将CollectionView绑定对象集合,ItemTemplate为单个Expander,实现:

  • 默认所有Expander处于收起状态;
  • 点击Expander头部时,展开当前项、收起其他已展开项,并滚动至当前Expander使其尽可能置顶。

当前实现代码

XAML

<CollectionView x:Name="CV">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="x:Int32">
            <Border>
                <toolkit:Expander ExpandedChanged="Expander_ExpandedChanged">
                    <toolkit:Expander.Header>
                        <!--Header content-->
                    </toolkit:Expander.Header>
                    <!--Expander content-->
                </toolkit:Expander>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

后台C#

private void Expander_ExpandedChanged(object sender, CommunityToolkit.Maui.Core.ExpandedChangedEventArgs e)
{
    if (sender is Expander ex && e.IsExpanded)
    {
        List<Expander> expanders = CV.GetVisualTreeDescendants().OfType<Expander>().ToList();

        // Close all expanders but the clicked one
        foreach (var expander in expanders)
        {
            if (expander != ex)
            {
                expander.IsExpanded = false;
            }
        }

        // Scroll to the clicked expander
        CV.ScrollTo(expanders.IndexOf(ex), position: ScrollToPosition.Start);

    }
}

遇到的问题

  • 滚动位置异常,例如点击第1个Expander后再点击第2个,滚动过度;
  • 集合项较多时,展开第n个Expander会同时展开第n+15个(CollectionView视图重用导致状态复用)。

解决方案

1. 修复视图重用问题:绑定Expander状态到数据模型

CollectionView的视图复用会导致未绑定到数据的视图状态被重复使用,因此需要给数据项添加IsExpanded属性并绑定到Expander的对应属性。

步骤1:创建带状态的数据模型

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class ExpanderItem : INotifyPropertyChanged
{
    private int _value;
    public int Value
    {
        get => _value;
        set => SetField(ref _value, value);
    }

    private bool _isExpanded;
    public bool IsExpanded
    {
        get => _isExpanded;
        set => SetField(ref _isExpanded, value);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

步骤2:修改XAML绑定状态

<CollectionView x:Name="CV" ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="local:ExpanderItem">
            <Border>
                <toolkit:Expander IsExpanded="{Binding IsExpanded}" 
                                  ExpandedChanged="Expander_ExpandedChanged">
                    <toolkit:Expander.Header>
                        <!--Header content,示例绑定Value-->
                        <Label Text="{Binding Value}" />
                    </toolkit:Expander.Header>
                    <!--Expander content-->
                    <Label Text="展开内容" />
                </toolkit:Expander>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

2. 修复滚动位置异常:基于数据项滚动

之前通过可视树获取Expander并取索引的方式不准确,因为CollectionView仅加载可视区域项,索引与数据源不匹配。应直接绑定数据项并滚动到该数据项。

修改后台代码

private void Expander_ExpandedChanged(object sender, CommunityToolkit.Maui.Core.ExpandedChangedEventArgs e)
{
    if (sender is Expander ex && e.IsExpanded)
    {
        // 获取当前Expander绑定的数据项
        if (ex.BindingContext is ExpanderItem currentItem)
        {
            // 遍历数据源,收起其他所有Expander
            foreach (var item in CV.ItemsSource.Cast<ExpanderItem>())
            {
                if (item != currentItem)
                {
                    item.IsExpanded = false;
                }
            }

            // 滚动到当前数据项,设置为置顶
            CV.ScrollTo(currentItem, position: ScrollToPosition.Start, animate: true);
        }
    }
}

3. 默认收起状态

初始化数据源时,将所有ExpanderItem的IsExpanded设为false即可:

// 示例初始化数据源
CV.ItemsSource = Enumerable.Range(1, 100).Select(i => new ExpanderItem { Value = i, IsExpanded = false });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:14:52