.NET MAUI安卓端CollectionView选择变更事件工作异常
问题根因
两个异常均由.NET MAUI CollectionView的虚拟化复用机制、VisualState状态同步逻辑和Xamarin.Forms存在差异导致,核心问题是原有代码手动维护选中状态和CollectionView内置选中状态双轨不同步,加上安卓端CollectionView初始加载、项复用时不会主动刷新子元素绑定状态。
修复方案
1. 移除冗余的手动选中状态维护逻辑
CollectionView的SelectedItem本身已经双向绑定了选中状态,不需要额外在ViewModel里遍历集合修改IsSelected属性,双状态同步必然出现不一致。同时LINQ的All()方法语义是做集合条件判断,不适合用来遍历修改集合元素属性。
修正后的ViewModel代码:
// TabViewModel 移除冗余的IsSelected属性,补上缺失的颜色属性(也可直接在XAML写固定值) public class TabViewModel : BaseViewModel { private int _tabId; public int TabId { get => _tabId; set { _tabId = value; OnPropertyChanged(nameof(TabId)); } } private string _tabTitle; public string TabTitle { get => _tabTitle; set { _tabTitle = value; OnPropertyChanged(nameof(TabTitle)); } } public Color IndicatorColor { get; } = Colors.Red; } // MainPageViewModel public class MainPageViewModel : BaseViewModel { public MainPageViewModel() { LoadTabs(); // 初始化触发内容区切换 SwitchTabContent(Tabs.First()); } private bool _isParentRecordTabVisible = true; private bool _isAdditionalInfoTabVisible; public ObservableCollection<TabViewModel> Tabs { get; set; } = new(); private TabViewModel _selectedTab; public TabViewModel SelectedTab { get => _selectedTab; set { _selectedTab = value; OnPropertyChanged(nameof(SelectedTab)); if (value != null) SwitchTabContent(value); } } public bool IsParentRecordTabVisible { get => _isParentRecordTabVisible; set { _isParentRecordTabVisible = value; OnPropertyChanged(nameof(IsParentRecordTabVisible)); } } public bool IsAdditionalInfoTabVisible { get => _isAdditionalInfoTabVisible; set { _isAdditionalInfoTabVisible = value; OnPropertyChanged(nameof(IsAdditionalInfoTabVisible)); } } private void LoadTabs() { Tabs.Add(new TabViewModel { TabId = 1, TabTitle = "Parent record" }); Tabs.Add(new TabViewModel { TabId = 2, TabTitle = "Additional Info" }); Tabs.Add(new TabViewModel { TabId = 3, TabTitle = "Contacts" }); Tabs.Add(new TabViewModel { TabId = 4, TabTitle = "Previous inspections" }); Tabs.Add(new TabViewModel { TabId = 5, TabTitle = "Attachments" }); SelectedTab = Tabs.FirstOrDefault(); } // 只负责切换内容区显隐,不再维护选中状态 private void SwitchTabContent(TabViewModel tab) { IsParentRecordTabVisible = tab.TabId == 1; IsAdditionalInfoTabVisible = tab.TabId == 2; // 其余Tab对应内容显隐按需求补充即可 } }
2. 新增相等判断值转换器
所有选中相关的UI状态全部绑定到CollectionView内置的SelectedItem属性,通过转换器判断当前项是否为选中项,从根源避免复用导致的状态错乱:
public class ObjectEqualityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value?.Equals(parameter) ?? false; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在页面资源字典中注册转换器:
<ContentPage.Resources> <ResourceDictionary> <local:ObjectEqualityConverter x:Key="ObjectEqualityConverter" /> </ResourceDictionary> </ContentPage.Resources>
3. 修正XAML布局逻辑
移除冗余的命令绑定,指示器显隐、VisualState全部关联内置选中状态,同时加Loaded事件修复安卓端初始选中状态不生效问题:
<ContentPage.Content> <Grid RowDefinitions="50, *" RowSpacing="0"> <CollectionView x:Name="TabsView" Grid.Row="0" ItemsSource="{Binding Tabs}" ItemsUpdatingScrollMode="KeepItemsInView" ItemSizingStrategy="MeasureAllItems" SelectedItem="{Binding SelectedTab, Mode=TwoWay}" SelectionMode="Single" Loaded="TabsView_OnLoaded"> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Horizontal"/> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate x:DataType="local:TabViewModel"> <Grid x:Name="TabItemRoot" RowSpacing="0" RowDefinitions="*, 3" BackgroundColor="Navy"> <Label x:Name="TabLabel" Grid.Row="0" Text="{Binding TabTitle}" TextColor="White" Padding="20,0" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" FontSize="12" /> <!-- 指示器直接绑定SelectedItem判断显隐 --> <BoxView Grid.Row="1" Color="{Binding IndicatorColor}" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type CollectionView}}, Path=SelectedItem, Converter={StaticResource ObjectEqualityConverter}, ConverterParameter={Binding .}}"/> <!-- VSM状态直接作用于根容器,同步修改背景和文字颜色 --> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="CommonStates"> <VisualState Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Navy" /> <Setter TargetName="TabLabel" Property="TextColor" Value="White" /> </VisualState.Setters> </VisualState> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="White" /> <Setter TargetName="TabLabel" Property="TextColor" Value="Navy" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> <tabs:ParentRecordTabView Grid.Row="1" IsVisible="{Binding IsParentRecordTabVisible}" VerticalOptions="FillAndExpand"/> <tabs:AdditionalInfoTabView Grid.Row="1" IsVisible="{Binding IsAdditionalInfoTabVisible}" VerticalOptions="FillAndExpand" /> </Grid> </ContentPage.Content>
4. 后台代码添加安卓端兼容处理
private void TabsView_OnLoaded(object sender, EventArgs e) { // 安卓端初始加载时主动触发一次选中状态刷新,修复VSM不生效问题 if (DeviceInfo.Platform == DevicePlatform.Android) { var selectedCache = TabsView.SelectedItem; TabsView.SelectedItem = null; TabsView.SelectedItem = selectedCache; } }
补充说明
不推荐通过关闭CollectionView虚拟化解决问题,Tab数量较多时关闭虚拟化会导致页面加载卡顿、内存占用升高。所有选中态展示逻辑统一绑定CollectionView内置的SelectedItem,避免多状态源同步问题,即可兼容iOS、安卓双端运行。
内容的提问来源于stack exchange,提问作者Divyesh
相关产品推荐
相关产品推荐

