Получить первый видимый элемент в WPF ListView C # - PullRequest
5 голосов
/ 28 мая 2010

Кто-нибудь знает, как получить ListViewItem, захватив первый видимый элемент в ListView? Я знаю, как получить элемент по индексу 0, но не первый видимый.

Ответы [ 5 ]

4 голосов
/ 28 мая 2010

Было так больно работать:

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;
    }
3 голосов
/ 28 сентября 2012

После попытки выяснить что-то подобное, я подумал, что поделюсь своим результатом здесь (как это кажется легче, чем другие ответы):

Простой тест видимости, который я получил от здесь .

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

После этого вы можете просмотреть список элементов списка и использовать этот тест, чтобы определить, какие из них видимы. Поскольку элементы списка всегда располагаются одинаково, первый видимый элемент в этом списке будет первым видимым для пользователя.

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 голосов
/ 06 января 2013

Нам нужно только вычислить смещение нашего списка, и первым видимым элементом будет элемент с индексом, равным 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;

Надеюсь, это поможет вам. , .!

2 голосов
/ 28 мая 2010

Не могу поверить, что нет более легкого пути ...

http://social.msdn.microsoft.com/forums/en-US/wpf/thread/2d527831-43aa-4fd5-8b7b-08cb5c4ed1db

0 голосов
/ 11 июня 2019

Похоже, что универсальность 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 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(), вы получите индекс для элемента рядом с нижним.

...