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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:39:19