我有ListView
可能包含很多项目,因此它是virtualized
和回收项目。它不使用排序。我需要刷新一些值显示,但是当项目太多时,更新所有内容太慢,所以我只想刷新可见项目。
如何获取所有当前显示的项目列表?我试着调查ListView
或ScrollViewer
,但我仍然不知道如何实现这一目标。该解决方案不得通过所有项目来测试它们是否可以被看到,因为这样会太慢。
我不确定代码或xaml是否有用,它只是Virtualized
/ Recycling ListView
,其ItemSource
绑定到Array
。
修改:
答案:
感谢akjoshi,我找到了方法:
获取ScrollViewer
的{{1}}
(使用ListView
方法,您可以自己使用FindDescendant
)。
阅读其VisualTreeHelper
:这是显示的第一个项目的编号
ScrollViewer.VerticalOffset
:这是显示的项目数
Rq:ScrollViewer.ViewportHeight
必须为真。答案 0 :(得分:8)
在尝试找出类似的东西之后,我想我会在这里分享我的结果(因为它似乎比其他回复更容易):
我从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);
}
之后,您可以遍历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;
}
答案 1 :(得分:7)
在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
您将不得不重新分配ItemSource
的{{1}}和ListView
首先丢弃其先前的项目并重新生成其整个列表。
答案 2 :(得分:1)
我如何看待事物:
一方面,您有自己的数据。它们必须是最新的,因为这是您的信息在内存中的位置。迭代你的数据列表应该非常快,最重要的是,可以在另一个线程上完成,在后台
另一方面,你有显示器。您的ListView
已经制作了仅刷新显示的数据的技巧,因为它正在虚拟化!你不需要更多技巧,它已经到位了!
在最后的工作中,在ObservableCollection
上使用绑定是一个很好的建议。如果您打算从另一个帖子修改ObservableCollection
,我建议您:http://blog.quantumbitdesigns.com/2008/07/22/wpf-cross-thread-collection-binding-part-1/
答案 3 :(得分:0)
我花了很多时间为此寻找更好的解决方案, 在我的情况下,我有一个scrollviewer,充满了可以设置为可见/不可见的自定义高度的项目,我想出了这个。它与上述解决方案相同,但只占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,
}
答案 4 :(得分:0)
如果您启用了虚拟化 ListView ,那么您可以获取所有当前可见项目,如下所示:
代码如下所示。
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 的列表。 希望它有所帮助:)。