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

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>

解决方案

问题根源

  1. ViewModel未实现INotifyPropertyChanged:集合属性(如Produtos、ProdutosAgrupadosList)直接赋值时,无法通知UI更新。
  2. ProdutosAgrupadosList未清空就添加新分组:调用CreateGroups时旧分组残留,导致UI显示混乱。
  3. 直接替换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:30:23