none
How to find the rows (ListViewItem(s)) in a ListView that are actually visible? RRS feed

  • Question

  • Hello;

    In a ListView where the DataContext is binded to a List<String> of 100000 (many rows),
    where VirtualizingStackPanel.IsVirtualizing="True"

    (a) How would i find out (via code) which rows (ListViewItem(s)) in the ListView, are actually visible?

    The reason is that would like to implement my own scrolling through a search result applied to the ListView (via a button click).
    I scroll via the button click from one search hit to the next.

    The following code (for example)

    Decorator border = VisualTreeHelper.GetChild(lv, 0) as Decorator;
    ScrollViewer scroll = border.Child as ScrollViewer;
    scroll.ScrollToVerticalOffset(newSelectedIndex);

    Simply moves the selected item to very top of the view ... which is not my intended result.

    If i knew the indexes of the rows currently visible, the scrolling could be done more intelligently...

    Thanks,



     


    Paul
    Thursday, October 16, 2008 3:59 PM

Answers

  • VerticalOffset and ViweportHeight is actually what you want if UI virtualization is turned on:

    listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString();
    listView.Loaded += (sender, e) =>
    {
        ScrollViewer scrollViewer = listView.GetVisualChild<ScrollViewer>();
        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);
                };
            }
        }
    };

    Thanks
    • Marked as answer by Schwartzberg Tuesday, October 21, 2008 6:39 AM
    Monday, October 20, 2008 9:48 AM
  • -> But if i assigned the IEnumerable to the DataContext no rows showed (unless the ListView had the attribute ItemsSource="{Binding}")

    I am just wondering why?

    The reason is that ItemsSource is the entry point for enable collection data binding for ItemsControl, it's pretty much like the DataSource property in Windows Forms/ASP.NET programming.

    Hope this clears things up a little bit.
    • Marked as answer by Schwartzberg Tuesday, October 21, 2008 6:39 AM
    Tuesday, October 21, 2008 3:28 AM

All replies

  • VerticalOffset and ViweportHeight is actually what you want if UI virtualization is turned on:

    listView.ItemsSource = from i in Enumerable.Range(0, 100) select "Item" + i.ToString();
    listView.Loaded += (sender, e) =>
    {
        ScrollViewer scrollViewer = listView.GetVisualChild<ScrollViewer>();
        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);
                };
            }
        }
    };

    Thanks
    • Marked as answer by Schwartzberg Tuesday, October 21, 2008 6:39 AM
    Monday, October 20, 2008 9:48 AM
  • Hej Marco,

    the listView.GetVisualChild is an extension method that you defined (probably among other places, here (on coordinating CheckBoxes in the headers of a ListView)).  

    I worked like a charm once i had/added the extension method to the solution :-)

    listView.ItemsSource = from i in Enumerable.Range(0, 10000) select "Item" + i.ToString();
    Caused 10000 rows to show, 
    but only after adding a GridView to the ListView, did the text "Item1", "Item2" show...
    So the GridView seemed necessary (... i just thought that there might be a default behavior...).

    But if i assigned the IEnumerable to the DataContext no rows showed (unless the ListView had the attribute ItemsSource="{Binding}")

    I am just wondering why?


    Thanks,
    Paul 


     public static class Extensions  
        {  
            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;  
            }  
        } 

    Paul
    Monday, October 20, 2008 11:51 AM
  • -> But if i assigned the IEnumerable to the DataContext no rows showed (unless the ListView had the attribute ItemsSource="{Binding}")

    I am just wondering why?

    The reason is that ItemsSource is the entry point for enable collection data binding for ItemsControl, it's pretty much like the DataSource property in Windows Forms/ASP.NET programming.

    Hope this clears things up a little bit.
    • Marked as answer by Schwartzberg Tuesday, October 21, 2008 6:39 AM
    Tuesday, October 21, 2008 3:28 AM