Xamarin Android MVVM:分组CollectionView列表变更后UI不更新
问题描述
在Xamarin Android的MVVM架构中使用分组CollectionView,以ObservableCollection作为ItemSource时,列表分组显示正常,但向集合中添加或移除元素时,UI无法自动更新,必须返回并重新进入ProductsPage才能看到变更。尝试在变更后调用CreateGroups方法刷新整个列表,也没有效果。
相关代码片段
ProdutosGroup类
public class ProdutosGroup : ObservableCollection<Produto> { public string Titulo { get; set; } public ProdutosGroup(string titulo, ObservableCollection<Produto> produtos) : base(produtos) { Titulo = titulo; } }
ProductsViewModel类
public ObservableCollection<Produto> Produtos { get; set; } //实际产品列表 public ObservableCollection<ProdutosGroup> ProdutosAgrupadosList { get; set; }//分组后的列表 public ListaDeProdutosViewModel(int idListaDeProdutos) { PopulateList(); CreateGroups(); } public void CarregarProdutos() { this.Produtos = new ObservableCollection<Produto>(App._produtoRepo.GetProdutos); } public void CreateGroups() { var idsCat = Produtos.Select(x => x.IdCategoria).Distinct(); var cats = new ObservableCollection<ProdutoCategoria>(); foreach (var idCat in idsCat) { cats.Add(App._categoriaRepo.GetProdutoCategoriaById(idCat)); } foreach (var item in cats) { ObservableCollection<Produto> produtosDaCategoria = new ObservableCollection<Produto>(); foreach (var prod in Produtos) { if (prod.IdCategoria == item.Id) produtosDaCategoria.Add(prod); } ProdutosAgrupadosList.Add(new ProdutosGroup(item.Descricao, new ObservableCollection<Produto>(produtosDaCategoria))); } }
ProductsPage页面
<ContentPage.Content> <Grid> <ScrollView > <CollectionView ItemsSource="{Binding ProdutosAgrupadosList}" x:Name="Listas" IsGrouped="true"> <CollectionView.GroupHeaderTemplate> <DataTemplate x:DataType="models:ProdutosGroup"> <Label Text="{Binding Titulo}" FontSize="28"/> </DataTemplate> </CollectionView.GroupHeaderTemplate> <CollectionView.ItemTemplate> <DataTemplate x:DataType="models:Produto"> <Label VerticalTextAlignment="Center" Text="{Binding Nome}" FontSize="28"/> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ScrollView> <ImageButton Padding="12" Source="BasketPlus" Grid.Row="1" Command="{Binding AddForm}" HorizontalOptions="End" WidthRequest="68" HeightRequest="68" VerticalOptions="End" CornerRadius="100" Margin="0,0,16,22" BackgroundColor="{StaticResource Verde}"/> </Grid> </ContentPage.Content>
解决方案
问题根源
- ViewModel未实现
INotifyPropertyChanged:集合属性(如Produtos、ProdutosAgrupadosList)直接赋值时,无法通知UI更新。 ProdutosAgrupadosList未清空就添加新分组:调用CreateGroups时旧分组残留,导致UI显示混乱。- 直接替换
Produtos集合:未通过集合的增删方法操作,而是直接赋值新集合,破坏了ObservableCollection的变更通知机制。
具体修复步骤
1. 让ViewModel实现INotifyPropertyChanged
public class ListaDeProdutosViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private ObservableCollection<Produto> _produtos; public ObservableCollection<Produto> Produtos { get => _produtos; set { _produtos = value; OnPropertyChanged(); } } private ObservableCollection<ProdutosGroup> _produtosAgrupadosList; public ObservableCollection<ProdutosGroup> ProdutosAgrupadosList { get => _produtosAgrupadosList; set { _produtosAgrupadosList = value; OnPropertyChanged(); } } public ListaDeProdutosViewModel(int idListaDeProdutos) { // 初始化空集合 Produtos = new ObservableCollection<Produto>(); ProdutosAgrupadosList = new ObservableCollection<ProdutosGroup>(); PopulateList(); CreateGroups(); } public void CreateGroups() { // 先清空原有分组,避免重复 ProdutosAgrupadosList.Clear(); var idsCat = Produtos.Select(x => x.IdCategoria).Distinct(); var cats = new ObservableCollection<ProdutoCategoria>(); foreach (var idCat in idsCat) { cats.Add(App._categoriaRepo.GetProdutoCategoriaById(idCat)); } foreach (var item in cats) { var produtosDaCategoria = new ObservableCollection<Produto>(Produtos.Where(p => p.IdCategoria == item.Id)); ProdutosAgrupadosList.Add(new ProdutosGroup(item.Descricao, produtosDaCategoria)); } } public void CarregarProdutos() { // 清空现有集合再添加新数据,保留ObservableCollection的变更通知 Produtos.Clear(); foreach (var produto in App._produtoRepo.GetProdutos) { Produtos.Add(produto); } // 重新分组 CreateGroups(); } }
2. 单个元素增删时的分组更新
如果是添加/删除单个产品,直接操作对应分组,无需重新生成所有分组:
// 添加产品 public void AdicionarProduto(Produto novoProduto) { Produtos.Add(novoProduto); var categoria = App._categoriaRepo.GetProdutoCategoriaById(novoProduto.IdCategoria); var grupoExistente = ProdutosAgrupadosList.FirstOrDefault(g => g.Titulo == categoria.Descricao); if (grupoExistente != null) { grupoExistente.Add(novoProduto); } else { var novoGrupo = new ProdutosGroup(categoria.Descricao, new ObservableCollection<Produto> { novoProduto }); ProdutosAgrupadosList.Add(novoGrupo); } } // 删除产品 public void RemoverProduto(Produto produto) { Produtos.Remove(produto); var grupo = ProdutosAgrupadosList.FirstOrDefault(g => g.Contains(produto)); if (grupo != null) { grupo.Remove(produto); // 分组为空时移除分组 if (grupo.Count == 0) { ProdutosAgrupadosList.Remove(grupo); } } }
3. 可选:让Produto实现INotifyPropertyChanged
如果产品属性变更需要UI实时更新,Produto类也需要实现该接口:
public class Produto : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private int _idCategoria; public int IdCategoria { get => _idCategoria; set { _idCategoria = value; OnPropertyChanged(); } } private string _nome; public string Nome { get => _nome; set { _nome = value; OnPropertyChanged(); } } }
内容的提问来源于stack exchange,提问作者leaf
相关产品推荐
相关产品推荐

