.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
相关产品推荐
相关产品推荐

