☰
WPF中的ListBox虚拟化下自动滚动实现
2026/10/15 8:03:18 网站建设 项目流程

现象

原写法的写法:

LogList.ScrollIntoView(LogList.Items[LogList.Items.Count - 1]);

数据不滚动

根因:ScrollIntoView 依赖容器,虚拟化下容器按需生成

这个 ListBox 开了虚拟化:

<ListBox x:Name="LogList" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">

ScrollIntoView(item)内部是两步:① 找到 item 对应的容器(ListBoxItem);② 按容器位置滚视口。而 UI 虚拟化的核心是只给可视区内的项生成容器——最新一行恰在视口外,容器根本不存在。于是:要显示它就得滚 → 要滚就得知道位置 → 要知道位置就得先有容器 →循环依赖,滚动被无限挂起。

修复:

private void OnLinesChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action != NotifyCollectionChangedAction.Add) return; if (DataContext is not DebugPanelViewModel vm || !vm.AutoScroll) return; // 修复:不用 ListBox.ScrollIntoView —— 虚拟化列表里它要等目标项的容器生成, // 而最新一行在视口外容器永远不会生成,滚动会被无限期挂起。 // 直接让 ListBox 内部的 ScrollViewer 滚到底,并延到下一轮 UI(此时新行已进入 Items)。 Dispatcher.BeginInvoke(new Action(() => { GetScrollViewer(LinesControl)?.ScrollToEnd(); }), DispatcherPriority.Loaded); }
// 递归找 ListBox 内部的 ScrollViewer private static ScrollViewer GetScrollViewer(DependencyObject d) { if (d is ScrollViewer sv) return sv; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(d); i++) { if (GetScrollViewer(VisualTreeHelper.GetChild(d, i)) is ScrollViewer found) return found; } return null; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询