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

如何阻止UWP ListView绑定的ObservableCollection更新时自动滚动到顶部

问题根因

你当前设置的ItemsUpdatingScrollMode="KeepItemsInView"仅在修改现有列表项、增量增删少量数据时生效,你代码中调用records.Clear()会一次性移除所有绑定项,此时ListView视口内容为空会自动滚动到顶部,后续重新添加所有项也不会恢复之前的滚动位置,因此之前的配置不生效。

解决方案

方案1(推荐):改为增量更新现有项,不执行全量清空操作

你的业务场景是拉取货币数据,每条数据的id为唯一标识,因此不需要全量清空重加,直接匹配更新现有项的属性即可,同时避免了列表项重绘的性能损耗。

  1. 首先让Coin类实现INotifyPropertyChanged接口,保证属性变更时UI自动刷新:
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Coin : INotifyPropertyChanged
{
    private string _id;
    public string id
    {
        get => _id;
        set { _id = value; OnPropertyChanged(); }
    }

    private string _name;
    public string name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    private decimal _price;
    public decimal price
    {
        get => _price;
        set { _price = value; OnPropertyChanged(); }
    }

    private string _logo;
    public string logo
    {
        get => _logo;
        set { _logo = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. 修改getData方法逻辑,移除Clear调用,改为匹配更新:
using System.Linq;

public async Task getData()
{
    var client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(new Uri("https://api.nomics.com/v1/currencies/ticker?key=<api key>&limit=10"));
    var jsonString = await response.Content.ReadAsStringAsync();
    JsonArray root = JsonValue.Parse(jsonString).GetArray();
    var validIds = new List<string>();

    for (uint i = 0; i < root.Count; i++)
    {
        string id = root.GetObjectAt(i).GetNamedString("id");
        validIds.Add(id);
        string name = root.GetObjectAt(i).GetNamedString("name");
        decimal price = decimal.Parse(root.GetObjectAt(i).GetNamedString("price"));
        var updatedCoin = new Coin
        {
            id = id,
            name = name,
            price = Math.Round(price, 4),
            logo = "https://cryptoicon-api.vercel.app/api/icon/" + id.ToLower()
        };
        // 匹配现有项更新属性
        var existingCoin = records.FirstOrDefault(c => c.id == id);
        if (existingCoin != null)
        {
            existingCoin.name = updatedCoin.name;
            existingCoin.price = updatedCoin.price;
            existingCoin.logo = updatedCoin.logo;
        }
        else
        {
            // 新增数据直接添加
            records.Add(updatedCoin);
        }
    }

    // 移除不在新数据中的旧项
    var toRemove = records.Where(c => !validIds.Contains(c.id)).ToList();
    foreach (var item in toRemove)
    {
        records.Remove(item);
    }
}

方案2:全量刷新时手动记录并恢复滚动位置

如果业务场景必须全量清空重加,可以在更新前记录当前滚动偏移量,更新完成后恢复位置:

  1. 先实现获取ListView内嵌ScrollViewer的方法:
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Controls;

private ScrollViewer GetScrollViewer(DependencyObject element)
{
    if (element is ScrollViewer viewer) return viewer;
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
    {
        var child = VisualTreeHelper.GetChild(element, i);
        var result = GetScrollViewer(child);
        if (result != null) return result;
    }
    return null;
}
  1. 修改getData方法,添加滚动位置恢复逻辑:
using Windows.UI.Core;

public async Task getData()
{
    var scrollViewer = GetScrollViewer(CoinsLV);
    // 记录当前滚动偏移量
    double currentOffset = scrollViewer.VerticalOffset;

    var client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(new Uri("https://api.nomics.com/v1/currencies/ticker?key=<api key>&limit=10"));
    var jsonString = await response.Content.ReadAsStringAsync();
    JsonArray root = JsonValue.Parse(jsonString).GetArray();
    records.Clear();
    for (uint i = 0; i < root.Count; i++)
    {
        string id = root.GetObjectAt(i).GetNamedString("id");
        string name = root.GetObjectAt(i).GetNamedString("name");
        decimal price = decimal.Parse(root.GetObjectAt(i).GetNamedString("price"));
        records.Add(new Coin {
            id = id,
            name = name,
            price = Math.Round(price, 4),
            logo = "https://cryptoicon-api.vercel.app/api/icon/" + id.ToLower()
        });
    };
    // 等待UI渲染完成后恢复滚动位置
    await CoinsLV.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        scrollViewer.ChangeView(null, currentOffset, null, false);
    });
}

内容的提问来源于stack exchange,提问作者Thilo Jaeggi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 16:24:03