我有一个 ListView
它可能包含很多项目,所以它是 virtualized
和回收物品。它不使用排序。我需要刷新一些值显示,但是当项目太多时,更新所有内容太慢,所以我想只刷新可见项目。
我怎样才能获得所有当前显示项目的列表?我试着调查一下 ListView
或者在 ScrollViewer
,但我仍然不知道如何实现这一目标。如果可以看到解决方案,解决方案不得通过所有项目进行测试,因为这样做太慢了。
我不确定代码或xaml是否有用,它只是一个 Virtualized
/Recycling ListView
用它 ItemSource
绑定到 Array
。
编辑:
答案:
感谢akjoshi,我找到了方法:
在MSDN上查看这个问题,展示一种找出可见的技术 ListView
物品 -
如何在ListView中找到实际可见的行(ListViewItem(s))?
这是该帖子的相关代码 -
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
;肯定会的 提高性能。
更新:
雅可能是真的(array
与 ObservableCollection
)但我希望看到一些与此相关的统计数据;
真正的好处 ObservableCollection
如果您有要求添加/删除您的项目 ListView
在运行时,如果是 Array
你将不得不重新分配 ItemSource
的 ListView
和 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<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;
}
我怎么看东西:
在最后的工作中,使用绑定 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,
}
如果启用了虚拟化 列表显示,然后您可以获得所有当前可见项目,如下所示:
- 获取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的 加载显示。
希望能帮助到你 :)。