清除ViewModel中ObservableCollection后CollectionView不再更新
MAUI中CollectionView切换选中项后视图不更新的问题
问题场景
从MainPage导航至SecondPage,首次选择列表项时,右侧网格能正常加载对应属性;但切换选择另一项后,虽然已清空并重新填充属性集合,右侧的CollectionView视图却不再更新。
预期效果
选择新的列表项后,视图能同步更新显示对应的属性信息。
效果截图
- 选择Data1时:

- 选择Data2时:

相关代码
Model.cs
using CommunityToolkit.Mvvm.ComponentModel; namespace Model; public class Data : ObservableObject { public string Number {get;set;} public string Group {get;set;} public string DateTime {get;set;} public Data(){} public Data(List<object> objList) { int i=0; foreach (System.Reflection.PropertyInfo property in typeof(Data).GetProperties().ToList()) { property.SetValue(this, objList[i]); i++; } } } public class PropertiesList : ObservableObject { public string Key {get;set;} public string Value {get;set;} public PropertiesList(string key, string value) { Key = key; Value = value; } }
SecondPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Test.SecondPage" xmlns:model="clr-namespace:Model" xmlns:viewmodel="clr-namespace:Viewmodel" xmlns:collections="clr-namespace:CommunityToolkit.Mvvm.Collections;assembly=CommunityToolkit.Mvvm" x:DataType="viewmodel:SecondPageViewModel"> <ContentPage.Resources> <Style TargetType="Grid"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="LightGray" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources> <Grid Margin="20" RowSpacing="20" RowDefinitions="*,Auto" ColumnDefinitions="*"> <Grid Grid.Row="0" Margin="20" RowSpacing="20" RowDefinitions="*" ColumnDefinitions="*,2*"> <Frame Grid.Column="0" BorderColor="Gray" CornerRadius="10" HasShadow="True"> <CollectionView ItemsSource="{Binding ObsDataList}" IsGrouped="True" SelectionMode="Single" SelectedItem="{Binding SelectedData, Mode=TwoWay}" SelectionChanged="OnDataSelectionChanged"> <CollectionView.GroupHeaderTemplate> <DataTemplate x:DataType="collections:IReadOnlyObservableGroup"> <Grid RowDefinitions="Auto" ColumnDefinitions="*"> <Label Text="{Binding Key}" BackgroundColor="LightGray" FontSize="18" FontAttributes="Bold" /> </Grid> </DataTemplate> </CollectionView.GroupHeaderTemplate> <CollectionView.ItemTemplate> <DataTemplate x:DataType="model:Data"> <Grid Padding="10" RowDefinitions="Auto" ColumnDefinitions="Auto,Auto,Auto,*" ColumnSpacing="50"> <Label Grid.Column="0" Text="{Binding Number}" FontSize="20" HorizontalOptions="Center" VerticalOptions="Center" FontAttributes="Bold"/> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="CommonStates"> <VisualState Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="LightBlue" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Frame> <Frame Grid.Column="1" BorderColor="Gray" CornerRadius="10" HasShadow="True"> <Grid RowDefinitions="*" ColumnDefinitions="*"> <CollectionView Grid.Column="0" ItemsSource="{Binding ObsDataProperties}" SelectionMode="None"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="model:PropertiesList"> <Frame BorderColor="Gray" CornerRadius="10"> <Grid RowDefinitions="Auto" ColumnDefinitions="200,200" RowSpacing="2"> <Label Grid.Column="0" Text="{Binding Key}" FontSize="10" HorizontalOptions="Start" VerticalOptions="Center" FontAttributes="Bold"/> <Label Grid.Column="1" Text="{Binding Value}" FontSize="10" HorizontalOptions="Start" VerticalOptions="Center" /> </Grid> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Grid> </Frame> </Grid> <Frame Grid.Row="1" BorderColor="Gray" CornerRadius="10" HasShadow="True"> <Grid RowDefinitions="Auto" ColumnDefinitions="*,*,*" ColumnSpacing="10"> <Button TextColor="White" FontAutoScalingEnabled="True" Text="Clear" Command="{Binding ClearDataInfoCommand}" HeightRequest="33" HorizontalOptions="Start"/> </Grid> </Frame> </Grid> </ContentPage>
SecondPage.xaml.cs
using Model; using Viewmodel; namespace Test; public partial class SecondPage : ContentPage { public SecondPage(SecondPageViewModel viewmodel) { InitializeComponent(); BindingContext = viewmodel; } protected override void OnNavigatedTo(NavigatedToEventArgs args) { ((SecondPageViewModel)this.BindingContext).GetDataCommand.Execute(null); base.OnNavigatedTo(args); } void OnDataSelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.Count() != 0) ((SecondPageViewModel)this.BindingContext).ShowDataInfoCommand.Execute(e.CurrentSelection.FirstOrDefault() as Data); } }
SecondPageViewModel.cs
using Model; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Collections; using System.Collections.ObjectModel; namespace Viewmodel; public partial class SecondPageViewModel : BaseViewModel { private IEnumerable<IGrouping<string, Data>> groupedDataList; [ObservableProperty] public Data selectedData; public ObservableCollection<PropertiesList> ObsDataProperties {get;} = new(); public ObservableGroupedCollection<string, Data> ObsDataList {get;} = new(); public SecondPageViewModel(IServiceProvider provider) : base(provider){} [RelayCommand] public async Task GetDataAsync() { if (ObsDataList.Count is not 0) ObsDataList.Clear(); if (ObsDataProperties.Count is not 0) ObsDataProperties.Clear(); await Task.Delay(0); List<Data> dataList = new List<Data>() { new Data ( new List<object> { "DATA1", "GROUP1", "DATETIME" } ), new Data ( new List<object> { "DATA2", "GROUP2", "DATETIME" } ) }; groupedDataList = dataList.GroupBy(data => data.Group); foreach(var dataGroup in groupedDataList) { ObsDataList.AddGroup(dataGroup); } } [RelayCommand] public void ShowDataInfo(Data data) { if (ObsDataProperties.Count is not 0) ObsDataProperties.Clear(); foreach (System.Reflection.PropertyInfo property in data.GetType().GetProperties()) { ObsDataProperties.Add ( new PropertiesList ( property.Name, property.GetValue(data).ToString() ) ); } } [RelayCommand] public void ClearDataInfo() { if (ObsDataProperties.Count is not 0) ObsDataProperties.Clear(); SelectedData = null; } }
问题原因及解决方案
原因分析
- 绑定冲突:同时使用
SelectedItem双向绑定和SelectionChanged事件,易导致逻辑混乱,干扰视图更新流程。 - 集合更新方式:直接对固定的
ObservableCollection执行Clear()+Add()操作,在部分MAUI场景下无法可靠触发视图更新通知。
解决方案步骤
1. 优化ViewModel属性与更新逻辑
将ObsDataProperties改为可观察属性,每次更新时重新创建集合,确保绑定系统感知变化:
using Model; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Collections; using System.Collections.ObjectModel; namespace Viewmodel; public partial class SecondPageViewModel : BaseViewModel { private IEnumerable<IGrouping<string, Data>> groupedDataList; [ObservableProperty] public Data selectedData; // 改为可观察属性,自动生成属性变更通知 [ObservableProperty] private ObservableCollection<PropertiesList> obsDataProperties = new(); public ObservableGroupedCollection<string, Data> ObsDataList {get;} = new(); public SecondPageViewModel(IServiceProvider provider) : base(provider){} [RelayCommand] public async Task GetDataAsync() { if (ObsDataList.Count is not 0) ObsDataList.Clear(); ObsDataProperties = new ObservableCollection<PropertiesList>(); await Task.Delay(0); List<Data> dataList = new List<Data>() { new Data ( new List<object> { "DATA1", "GROUP1", "DATETIME" } ), new Data ( new List<object> { "DATA2", "GROUP2", "DATETIME" } ) }; groupedDataList = dataList.GroupBy(data => data.Group); foreach(var dataGroup in groupedDataList) { ObsDataList.AddGroup(dataGroup); } } [RelayCommand] public void ShowDataInfo(Data data) { var newProperties = new ObservableCollection<PropertiesList>(); foreach (System.Reflection.PropertyInfo property in data.GetType().GetProperties()) { newProperties.Add ( new PropertiesList ( property.Name, property.GetValue(data).ToString() ) ); } ObsDataProperties = newProperties; } [RelayCommand] public void ClearDataInfo() { ObsDataProperties = new ObservableCollection<PropertiesList>(); SelectedData = null; } // 利用属性变更回调处理选中项切换逻辑 partial void OnSelectedDataChanged(Data value) { if (value != null) { ShowDataInfoCommand.Execute(value); } else { ClearDataInfoCommand.Execute(null); } } }
2. 移除XAML中的SelectionChanged事件绑定
修改SecondPage.xaml中的CollectionView,删除事件绑定:
<CollectionView ItemsSource="{Binding ObsDataList}" IsGrouped="True" SelectionMode="Single" SelectedItem="{Binding SelectedData, Mode=TwoWay}">
3. 删除代码后台的事件处理方法
移除SecondPage.xaml.cs中的OnDataSelectionChanged方法:
// 移除此方法 // void OnDataSelectionChanged(object sender, SelectionChangedEventArgs e) // { // if (e.CurrentSelection.Count() != 0) // ((SecondPageViewModel)this.BindingContext).ShowDataInfoCommand.Execute(e.CurrentSelection.FirstOrDefault() as Data); // }
修改完成后,选中项变更会通过属性回调触发属性加载,每次更新集合时都会创建新的ObservableCollection,确保视图能同步更新。
内容的提问来源于stack exchange,提问作者tvarela
相关产品推荐
相关产品推荐

