Xamarin.Forms中更新CollectionView时维持滚动位置的实现方案
Xamarin.Forms CollectionView 定时更新时保持滚动位置的解决方案
问题根源
当前每次更新数据时直接创建新的ObservableCollection并赋值给Lista_Mesas,这会导致CollectionView完全重新绑定数据源,从而丢失之前的滚动位置。
解决方案
1. 复用现有ObservableCollection,避免替换整个集合
初始化时创建一次集合实例,后续更新仅修改集合内的元素(新增、删除、更新属性),让CollectionView只渲染变化的部分,维持滚动位置。
修改ViewModel构造函数
public MesasAbiertasViewModels(INavigation nav) { Navigation = nav; ds_ma = new DataServiceMesasAbiertas(); _dspedidos = new DataServicePedidosView(); ds_abrir_mesa = new DataServiceAbrirMesaView(); _dspda = new DataServiceConfigurarPDA(); list_config = _dspda.ObtenerTodosConfiguracion(); // 初始化集合,后续复用该实例 Lista_Mesas = new ObservableCollection<CabeceraComandaModels>(); IniciarActualizacion(); }
修改CargarListaMesas方法
public async void CargarListaMesas() { try { // 获取最新数据 var nuevasMesas = ds_ma.ObtenerComandasCab().ToList(); mesas_ocupadas = ds_ma.ObtenerMesasAbiertas(); // 回到UI线程更新集合(集合操作必须在UI线程执行) await Device.InvokeOnMainThreadAsync(() => { // 移除不在新数据中的旧项 var itemsToRemove = Lista_Mesas.Where(item => !nuevasMesas.Any(n => n.Id_comanda == item.Id_comanda)).ToList(); foreach (var item in itemsToRemove) { Lista_Mesas.Remove(item); } // 更新或添加新项 foreach (var nuevaMesa in nuevasMesas) { bool ocupada = MesaOcupada(nuevaMesa.Mesa, mesas_ocupadas); nuevaMesa.Total = nuevaMesa.Total_condto > 0 ? nuevaMesa.Total_condto : nuevaMesa.Total_coniva; nuevaMesa.Esta_abierta = ocupada ? 1 : 0; var existingItem = Lista_Mesas.FirstOrDefault(item => item.Id_comanda == nuevaMesa.Id_comanda); if (existingItem != null) { // 更新现有项的属性 existingItem.Mesa = nuevaMesa.Mesa; existingItem.Total = nuevaMesa.Total; existingItem.Esta_abierta = nuevaMesa.Esta_abierta; existingItem.Comprobante = nuevaMesa.Comprobante; // 补充其他需要更新的属性 } else { // 添加新项到集合 Lista_Mesas.Add(nuevaMesa); } } }); } catch(Exception mi) { await DisplayAlert("Error", mi.Message, "OK"); } }
2. 确保数据模型实现INotifyPropertyChanged
CabeceraComandaModels需要实现接口,让属性更新时自动通知UI刷新,避免重新创建项:
public class CabeceraComandaModels : INotifyPropertyChanged { private int _mesa; public int Mesa { get => _mesa; set { _mesa = value; OnPropertyChanged(); } } private decimal _total; public decimal Total { get => _total; set { _total = value; OnPropertyChanged(); } } private int _esta_abierta; public int Esta_abierta { get => _esta_abierta; set { _esta_abierta = value; OnPropertyChanged(); } } // 其他属性同理实现 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
3. 特殊场景:必须替换集合时恢复滚动位置
如果业务上必须替换整个集合,可以先记录滚动位置,替换后恢复:
在View的代码-behind中添加逻辑
private double _lastScrollY; // 监听滚动事件记录位置 private void lstpedidos_Scrolled(object sender, ItemsViewScrolledEventArgs e) { _lastScrollY = e.VerticalOffset; } // 恢复滚动位置的方法 public async Task RestoreScrollPosition() { if (lstpedidos.ItemsLayout is LinearItemsLayout) { await lstpedidos.ScrollToAsync(_lastScrollY, ScrollToPosition.Start, animate: false); } }
可通过MessagingCenter或绑定命令,在ViewModel更新集合后通知View调用恢复方法。
内容的提问来源于stack exchange,提问作者Yoandry Castellanos
相关产品推荐
相关产品推荐

