C# 获取 ListView 可见项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11187382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get ListView Visible items
提问by GameAlchemist
I have a ListViewwhich might contains a lot of items, so it is virtualizedand recycling items. It does not use sort. I need to refresh some value display, but when there are too many items, it is too slow to update everything, so I would like to refresh only the visible items.
我有一个ListView可能包含很多物品的物品,因此它是virtualized回收物品。它不使用排序。我需要刷新一些值显示,但是当项目太多时,更新所有内容太慢,所以我只想刷新可见项目。
How could I get a list of all currently displayed items ? I tried to look into the ListViewor in the ScrollViewer, but I still have no idea how to achieve this. The solution must NOT go through all items to test if they can be seen, because this would be too slow.
我怎样才能获得当前显示的所有项目的列表?我试图查看ListView或 中的ScrollViewer,但我仍然不知道如何实现这一点。解决方案不能通过所有项目来测试它们是否可以被看到,因为这太慢了。
I'm not sure code or xaml would be useful, it is just a Virtualized/Recycling ListViewwith its ItemSourcebound to an Array.
我不确定代码或 xaml 是否有用,它只是一个Virtualized/Recycling ListView并ItemSource绑定到一个Array.
Edit :Answer :
thanks to akjoshi, I found the way :
编辑:回答:
感谢 akjoshi,我找到了方法:
get the
ScrollViewerof theListView(with aFindDescendantmethod, that you can do yourself with theVisualTreeHelper).read its
ScrollViewer.VerticalOffset: it is the number of the first item shown- read its
ScrollViewer.ViewportHeight: it is the count of items shown.
Rq :CanContentScrollmust be true.
得到
ScrollViewer的ListView(有FindDescendant方法,你可以做你自己用VisualTreeHelper)。阅读它
ScrollViewer.VerticalOffset:它是显示的第一个项目的编号- 阅读它
ScrollViewer.ViewportHeight:它是显示的项目数。
Rq:CanContentScroll必须是真的。
采纳答案by akjoshi
Have a look at this question on MSDN showing a technique to find out the visible ListViewitems -
看看 MSDN 上的这个问题,展示了一种找出可见ListView项目的技术 -
How to find the rows (ListViewItem(s)) in a ListView that are actually visible?
如何在 ListView 中找到实际可见的行(ListViewItem(s))?
Here's the relevant code from that post -
这是该帖子中的相关代码-
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);
};
}
}
};
Another thing you should do is to use ObservableCollectionas your ItemSourceinstead of an Array; that will definitely improve the performance.
你应该做的另一件事是使用ObservableCollectionas yourItemSource而不是Array; 那肯定会提高性能。
Update:
更新:
Ya that might be true(arrayvs. ObservableCollection) but I would like to see some statistics related to this;
是的,这可能是真的(arrayvs. ObservableCollection),但我想看到一些与此相关的统计数据;
The real benefit of ObservableCollectionis if you have a requirement to add/remove items from your ListViewat run-time, in case of an Arrayyou will have to reassign the ItemSourceof ListViewand the ListViewfirst throws away its previous items and regenerates its entire list.
的真正好处ObservableCollection是,如果您需要ListView在运行时从您的项目中添加/删除项目,那么Array您将不得不重新分配ItemSourceofListView并且第ListView一个丢弃其先前的项目并重新生成其整个列表。
回答by Vivien Ruiz
How I see things :
我如何看待事物:
on one side, you have your data. They must be up to date, because this is where your information is in memory. Iterating on your data list should be pretty fast, and most of all, can be done on another thread, in background
on the other side, you have the display. Your
ListViewalready make the trick of refreshing only the datas displayed, since it's virtualizing ! You need no more tricks, it's already in place !
一方面,您拥有自己的数据。它们必须是最新的,因为这是您的信息在内存中的位置。迭代您的数据列表应该非常快,最重要的是,可以在后台的另一个线程上完成
另一方面,你有显示器。
ListView由于它是虚拟化的,您已经使用了仅刷新显示的数据的技巧!你不需要更多的技巧,它已经到位!
On last work, using a binding on an ObservableCollectionis a good advice. If you intend to modify the ObservableCollectionfrom an another thread, I would recommend this : http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
在最后的工作中,在 an 上使用绑定ObservableCollection是一个很好的建议。如果您打算ObservableCollection从另一个线程修改它,我会推荐这个:http: //blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
回答by elbweb
After trying to figure out something similar, I thought I would share my result here (as it seems easier than the other responses):
在尝试找出类似的东西后,我想我会在这里分享我的结果(因为它似乎比其他响应更容易):
Simple visibility test I got from 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);
}
Afterwards you can loop through the listboxitems and use that test to determine which are visible. Since the listboxitems are always ordered the same the first visible one in this list would be the first visible one to the user.
之后,您可以遍历 listboxitems 并使用该测试来确定哪些是可见的。由于 listboxitems 总是按相同的顺序排列,因此此列表中的第一个可见项将是用户第一个可见的项。
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;
}
回答by robbi
I spend a lot of time finding a better solution for this, In my situation i have a scrollviewer, filled with items with custom heigths that can be set visible/invisible, i came up with this. It does the same as above solutions but with a fraction of the CPU. I hope it helps some one. The first items of the listview / scrollpanel is TopVisibleItem
我花了很多时间为此找到更好的解决方案,在我的情况下,我有一个滚动查看器,里面装满了可以设置为可见/不可见的自定义高度的项目,我想出了这个。它的作用与上述解决方案相同,但只占用一小部分 CPU。我希望它可以帮助某人。列表视图/滚动面板的第一项是 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,
}
回答by VinayChoudhary99
If you have a virtualization enabled ListView, Then you can get all Current Visible items as below:
如果您启用了虚拟化ListView,那么您可以获得所有当前可见的项目,如下所示:
- Get VirtualizingStackPanel
- Get all ListViewItems in VirtualizingStackPanel
- 获取 VirtualizingStackPanel
- 获取 VirtualizingStackPanel 中的所有 ListViewItems
The code is shown below.
代码如下所示。
VirtualizingStackPanel virtualizingStackPanel = FindVisualChild<VirtualizingStackPanel>(requiredListView);
List<ListViewItem> items = GetVisualChildren<ListViewItem>(virtualizingStackPanel);
The Functions are shown below.
功能如下所示。
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;
}
This will return you list of current ListViewItemloaded for displaying. Hope it helps :).
这将返回为显示加载的当前ListViewItem列表。希望能帮助到你 :)。

