获取ListView可见项目
我有一个可能包含很多项目的ListView
,因此它是virtualized
和回收项目。 它不使用排序。 我需要刷新一些值显示,但是当项目太多时,更新所有内容太慢,所以我想只刷新可见项目。
我怎样才能获得所有当前显示项目的列表? 我试图查看ListView
或ScrollViewer
,但我仍然不知道如何实现这一点。 如果可以看到解决方案,解决方案不得通过所有项目进行测试,因为这样做太慢了。
我不确定代码或xaml是否有用,它只是一个Virtualized
/ Recycling ListView
,其ItemSource
绑定到一个Array
。
编辑:答案:
感谢akjoshi,我找到了方法:
-
获取
ListView
的ScrollViewer
(使用FindDescendant
方法,您可以使用VisualTreeHelper
自己完成)。 -
读取它的
ScrollViewer.VerticalOffset
:它是显示的第一个项目的编号 - 读取它的
ScrollViewer.ViewportHeight
:它是显示的项目数。
Rq:CanContentScroll
必须为true。
在MSDN上查看这个问题,展示一种技术来找出可见的ListView
项目 –
如何在ListView中找到实际可见的行(ListViewItem(s))?
这是该post的相关代码 –
listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString(); listView.Loaded += (sender, e) => { ScrollViewer scrollViewer = listView.GetVisualChild(); //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
; 这肯定会提高性能 。
更新:
Ya可能是真的( array
vs. ObservableCollection
)但我希望看到一些与此相关的统计数据;
ObservableCollection
的真正好处是,如果您需要在运行时从ListView
添加/删除项目,则在Array
情况下,您将不得不重新分配ListView
的ItemSource
,并且ListView
首先抛弃其先前的项目并重新生成它整个清单。
在尝试找出类似的东西之后,我想我会在这里分享我的结果(因为它似乎比其他响应更容易):
我从这里得到的简单可见性测试。
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); }
之后,您可以遍历listboxitems并使用该测试来确定哪些是可见的。 由于listboxitems总是排序相同,因此该列表中的第一个可见的将是用户的第一个可见的。
private List
我怎么看东西:
-
一方面,你有你的数据。 它们必须是最新的,因为这是您的信息在内存中的位置。 迭代你的数据列表应该非常快,最重要的是,可以在后台的另一个线程上完成
-
另一方面,你有显示器。 你的
ListView
已经成功刷新了所显示的数据,因为它正在虚拟化! 你不需要更多技巧,它已经到位了!
在最后的工作中,在ObservableCollection
上使用绑定是一个很好的建议。 如果你打算从另一个线程修改ObservableCollection
,我建议你这样做: http : //blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
我花了很多时间为此寻找更好的解决方案,在我的情况下,我有一个滚动查看器,充满了可以设置为可见/不可见的自定义高度的项目,我想出了这个。 它与上述解决方案相同,但只占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, }
如果您启用了虚拟化ListView ,那么您可以获得所有当前可见项目,如下所示:
- 获取VirtualizingStackPanel
- 获取VirtualizingStackPanel中的所有ListViewItems
代码如下所示。
VirtualizingStackPanel virtualizingStackPanel = FindVisualChild(requiredListView); List items = GetVisualChildren (virtualizingStackPanel);
function如下所示。
private childItem FindVisualChild(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 (child); if (childOfChild != null) return childOfChild; } } return null; } private List GetVisualChildren (DependencyObject obj) where childItem : DependencyObject { List childList = new List (); 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的列表。 希望能帮助到你 :)。