任何人都知道如何通过抓取 ListView 中的第一个可见项目来获取 ListViewItem?我知道如何在索引 0 处获取项目,但不是第一个可见的项目。
5 回答
工作太痛苦了:
HitTestResult hitTest = VisualTreeHelper.HitTest(SoundListView, new Point(5, 5));
System.Windows.Controls.ListViewItem item = GetListViewItemFromEvent(null, hitTest.VisualHit) as System.Windows.Controls.ListViewItem;
以及获取列表项的功能:
System.Windows.Controls.ListViewItem GetListViewItemFromEvent(object sender, object originalSource)
{
DependencyObject depObj = originalSource as DependencyObject;
if (depObj != null)
{
// go up the visual hierarchy until we find the list view item the click came from
// the click might have been on the grid or column headers so we need to cater for this
DependencyObject current = depObj;
while (current != null && current != SoundListView)
{
System.Windows.Controls.ListViewItem ListViewItem = current as System.Windows.Controls.ListViewItem;
if (ListViewItem != null)
{
return ListViewItem;
}
current = VisualTreeHelper.GetParent(current);
}
}
return null;
}
在试图找出类似的东西之后,我想我会在这里分享我的结果(因为它似乎比其他回复更容易):
我从这里得到的简单能见度测试。
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 并使用该测试来确定哪些是可见的。由于列表框项的顺序始终相同,因此此列表中的第一个可见项将是用户的第一个可见项。
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;
}
我不敢相信没有更简单的方法...
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/2d527831-43aa-4fd5-8b7b-08cb5c4ed1db
我们只需要计算列表框的偏移量,第一个可见项将是索引处等于 VerticalOffset 的项...
// queue is the name of my listbox
VirtualizingStackPanel panel = VisualTreeHelper.GetParent(queue.Items[0] as ListBoxItem) as VirtualizingStackPanel;
int offset = (int)panel.VerticalOffset;
// then our desired listboxitem is:
ListBoxItem item = queue.Items[offset] as ListBoxItem;
希望这可以帮助你。. .!
WPF 的普遍性ListView
似乎阻止了该类提供像 WinForms' 这样的属性TopItem
。但是,如果实例配置了VirtualizingStackPanel
,您仍然可以直接查询最顶层的索引。这避免了其他方法所需的搜索和迭代。(该方法基于这篇文章。)
我认为在接受的答案中使用的命中测试方法更通用,但如果你真正想要的是列表索引而不是列表项,那么这可能会节省IndexOf
调用。
在对列表内容进行重大更改后,我的应用程序需要保存和恢复列表位置。设置顶部位置的代码(基于这篇文章)也如下所示。为方便起见,这些被实现为扩展方法。
public static class ListViewExtensions {
public static int GetTopItemIndex(this ListView lv) {
if (lv.Items.Count == 0) {
return -1;
}
VirtualizingStackPanel vsp = lv.GetVisualChild<VirtualizingStackPanel>();
if (vsp == null) {
return -1;
}
return (int) vsp.VerticalOffset;
}
public static void ScrollToTopItem(this ListView lv, object item) {
ScrollViewer sv = lv.GetVisualChild<ScrollViewer>();
sv.ScrollToBottom();
lv.ScrollIntoView(item);
}
}
非常方便的GetVisualChild
方法来自MSDN 帖子:
public static class VisualHelper {
public static T GetVisualChild<T>(this Visual referenceVisual) where T : Visual {
Visual child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++) {
child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
if (child != null && child is T) {
break;
} else if (child != null) {
child = GetVisualChild<T>(child);
if (child != null && child is T) {
break;
}
}
}
return child as T;
}
}
使用注意事项ScrollToTopItem
:ScrollToBottom()
调用立即生效,但ScrollIntoView()
似乎被推迟了。因此,如果您GetTopItemIndex()
在 之后立即调用ScrollToTopItem()
,您将获得靠近底部的项目的索引。
更新:只是想注意ScrollIntoView()
,对于少于 1,000 个项目的列表,我的系统需要 60-100 毫秒。有时它会默默地失败。我最终创建了一个“滚动到索引”方法来sv.ScrollToVerticalOffset()
代替它。