获取 ListView 可见项
Posted
技术标签:
【中文标题】获取 ListView 可见项【英文标题】:Get ListView Visible items 【发布时间】:2012-06-26 14:14:16 【问题描述】:我有一个ListView
,它可能包含很多物品,所以它是virtualized
和回收物品。它不使用排序。我需要刷新一些值显示,但是当项目太多时,更新所有内容太慢,所以我想只刷新可见项目。
如何获得所有当前显示项目的列表?我试图查看ListView
或ScrollViewer
,但我仍然不知道如何实现这一点。解决方案不能遍历所有项目来测试它们是否可以看到,因为这太慢了。
我不确定代码或 xaml 是否有用,它只是一个 Virtualized
/Recycling ListView
,其 ItemSource
绑定到 Array
。
编辑: 答案: 感谢 akjoshi,我找到了方法:
获取ListView
的ScrollViewer
(使用 FindDescendant
方法,您可以自己使用 VisualTreeHelper
)。
读取它的ScrollViewer.VerticalOffset
:它是显示的第一个项目的编号
ScrollViewer.ViewportHeight
:它是显示的项目数。
Rq : CanContentScroll
必须为真。
【问题讨论】:
你是如何填写你的 ListView 的?显式创建 ListViewItem?一组 ItemSource ?捆绑 ?也许给我们一些代码! 另请参阅此答案***.com/a/26344535/1830814 【参考方案1】:查看 MSDN 上的这个问题,该问题展示了一种找出可见 ListView
项目的技术 -
How to find the rows (ListViewItem(s)) in a ListView that are actually visible?
这是该帖子中的相关代码 -
listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString();
listView.Loaded += (sender, e) =>
ScrollViewer scrollViewer = listView.GetVisualChild<ScrollViewer>(); //Extension method
if (scrollViewer != null)
ScrollBar scrollBar = scrollViewer.Template.FindName("PART_VerticalScrollBar", scrollViewer) as ScrollBar;
if (scrollBar != null)
scrollBar.ValueChanged += delegate
//VerticalOffset and ViweportHeight is actually what you want if UI virtualization is turned on.
Console.WriteLine("Visible Item Start Index:0", scrollViewer.VerticalOffset);
Console.WriteLine("Visible Item Count:0", scrollViewer.ViewportHeight);
;
;
您应该做的另一件事是使用ObservableCollection
作为您的ItemSource
而不是Array
;那肯定会improve the performance。
更新:
是的,这可能是真的(array
vs. ObservableCollection
)但我想看看一些与此相关的统计数据;
ObservableCollection
的真正好处是,如果您需要在运行时从您的ListView
添加/删除项目,如果是Array
,您将不得不重新分配@987654335 的ItemSource
@ 和 ListView
首先丢弃它以前的项目并重新生成它的整个列表。
【讨论】:
Rq :就性能和内存使用而言,对于我的应用程序而言,该数组远远超过了 Observable 集合。我使用的项目数量在 100.000-1.000.000 范围内。 您提供的 MS 链接将 List 与 Observable 集合进行比较,项目计数较低 (1000) 并且虚拟化关闭,很可能是因为否则不会看到明显的差异。所以它不适用于我的案例,我想知道它是否与任何案例相关(为什么有人会关闭虚拟化?) ListView 只会重新生成可见的项目,因为除了 MS 没有人关闭虚拟化 :)。在我的应用程序中,几乎所有数组都可能在刷新时发生变化。项目的 ObsColl 将导致计数 > 200.000 (Win XP) 的内存异常,并且在该计数时过滤时间 > 10 分钟。有了一个数组,我在 1 分钟内达到了 5.000.000。我希望我能提供一些“证明”,但我知道 WPF 没有 JSPerf ......对我来说,底线是:ObsColl 更适合不太大的集合(而且更方便),但没有什么能比数组更好>> 100.000 项。 ObservableCollection 的问题是它不喜欢线程,即使您使用 Dispatcher。我不得不切换到常规 List 并告诉应用程序仅更新 Count 更改,因为 OC 无法跟踪它添加了多少项目与列表中存在多少项目,这导致 WPF 崩溃。 那么当CanContentScroll
为false时呢?【参考方案2】:
在尝试找出类似的东西后,我想我会在这里分享我的结果(因为它似乎比其他回复更容易):
我从here 获得的简单可见性测试。
private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
if (!element.IsVisible)
return false;
Rect bounds =
element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
之后,您可以遍历列表框项并使用该测试来确定哪些是可见的。由于列表框项的顺序始终相同,因此此列表中的第一个可见项将是用户第一个可见的项。
private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
var items = new List<object>();
foreach (var item in PhotosListBox.Items)
if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
items.Add(item);
else if (items.Any())
break;
return items;
【讨论】:
【参考方案3】:我如何看待事物:
一方面,您拥有自己的数据。它们必须是最新的,因为这是您的信息在内存中的位置。迭代你的数据列表应该很快,最重要的是,可以在后台的另一个线程上完成
在另一边,你有显示器。您的ListView
已经使用了只刷新显示数据的技巧,因为它是虚拟化的!你不需要更多的技巧,它已经到位了!
在最后的工作中,对ObservableCollection
使用绑定是一个很好的建议。如果您打算从另一个线程修改ObservableCollection
,我建议您这样做:http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
【讨论】:
如果你不了解它,你可能对 MVVM 模式感兴趣 ;) 我没有调查什么在使用 CPU,但是遍历我的列表并在一个属性上为所有项目设置 NotifyPropertyChanged 对于必须执行我的程序的(慢速)计算机来说任务太重了。该列表可能有 100.000 项长。因此,虚拟化并不能挽救局面。我测试 ObservableCollection 比我的应用程序中的数组慢 5 倍以上。 访问数组肯定比访问 ObservableCollection 快。但是 ObservableCollection 使用绑定完成了使 UI 保持最新的所有工作。以我的经验,创建新的图形项目是大部分时间。不是数据列表背后的工作。【参考方案4】:我花了很多时间为此寻找更好的解决方案, 在我的情况下,我有一个滚动查看器,里面装满了可以设置为可见/不可见的自定义高度的项目,我想出了这个。它的作用与上述解决方案相同,但只占用了 CPU 的一小部分。我希望它对某人有所帮助。 listview / scrollpanel 的第一项是 TopVisibleItem
public int TopVisibleItem get; private set;
private double CurrentDistance;
private void TouchScroller_ScrollChanged(object sender, ScrollChangedEventArgs e)
if (myItemControl.Items.Count > 0)
MoveDirection direction = (MoveDirection)Math.Sign(e.VerticalChange);
if (direction == MoveDirection.Positive)
while (CurrentDistance < e.VerticalOffset && TopVisibleItem < myItemControl.Items.Count)
CurrentDistance += ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight;
TopVisibleItem += 1;
else
while (CurrentDistance >= e.VerticalOffset && TopVisibleItem > 0)
CurrentDistance -= ((FrameworkElement)myItemControl.Items[TopVisibleItem]).ActualHeight;
TopVisibleItem -= 1;
public enum MoveDirection
Negative = -1,
Positive = 1,
【讨论】:
【参考方案5】:如果您启用了虚拟化ListView,那么您可以获得所有当前可见项,如下所示:
-
获取 VirtualizingStackPanel
获取 VirtualizingStackPanel 中的所有 ListViewItems
代码如下所示。
VirtualizingStackPanel virtualizingStackPanel = FindVisualChild<VirtualizingStackPanel>(requiredListView);
List<ListViewItem> items = GetVisualChildren<ListViewItem>(virtualizingStackPanel);
功能如下所示。
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
return null;
private List<childItem> GetVisualChildren<childItem>(DependencyObject obj) where childItem : DependencyObject
List<childItem> childList = new List<childItem>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
childList.Add(child as childItem);
if (childList.Count > 0)
return childList;
return null;
这将返回当前加载显示的 ListViewItem 列表。 希望它有所帮助:)。
【讨论】:
以上是关于获取 ListView 可见项的主要内容,如果未能解决你的问题,请参考以下文章
更改 ListView 中当前顶部可见项的颜色 - Flutter