在WPF ListView C#中获取第一个可见项

时间:2010-05-28 04:56:18

标签: c# wpf listview listviewitem

任何人都知道如何通过抓取ListView中的第一个可见项来获取ListViewItem?我知道如何在索引0处获取项目,但不知道第一个可见项目。

5 个答案:

答案 0 :(得分:3)

这对于工作非常痛苦:

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;
    }

答案 1 :(得分:3)

在尝试找出类似的东西之后,我想我会在这里分享我的结果(因为它似乎比其他回复更容易):

我从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;
}

答案 2 :(得分:2)

答案 3 :(得分:2)

我们只需要计算列表框的偏移量,第一个可见项就是索引处的项目等于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;

希望这会对你有所帮助。 。 !

答案 4 :(得分:0)

WPF ListView的通用性似乎阻止了该类提供WinForms的TopItem之类的属性。但是,如果实例配置有VirtualizingStackPanel,则仍然可以直接查询最顶层的索引。这避免了其他方法所需的搜索和迭代。 (该方法基于this post。)

我认为接受的答案中使用的命中测试方法更通用,但是如果您真正想要的是列表索引而不是列表项,那么这可能会节省一个IndexOf调用。

在对列表内容进行重大更改之后,我的应用需要保存和恢复列表位置。下方也显示了设置最高位置的代码(基于this post)。为方便起见,将它们作为扩展方法实现。

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 post

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(),则会获得靠近底部的项目的索引。