4

I have a ListBox or DataGrid filled with thousands of entries. I would like to know items that the user has looked at (scrolling, searching or otherwise). How can I tell what is visible to the user in the ListBox?

Bonus: Set a timer so that the item has to be shown for a minimum of N milliseconds (in the event the user is just pulling down the scrollbar).

Update: This is a near duplicate of Get items in view within a listbox - but the solution it gives, using "SelectedItems", is not sufficient. I need to know the items whether they are selected or not!

Community
  • 1
  • 1
tofutim
  • 22,664
  • 20
  • 87
  • 148
  • This may be another clue: http://stackoverflow.com/questions/610343/wpf-listbox-getting-uielement-instead-of-of-selecteditem – tofutim May 29 '11 at 16:13
  • Clarifications I would like to know: Which is it? A `ListBox` or a `DataGrid`? Or are you going to chose one of the two based on whether the solution is possible with one and not the other? If you're using a `ListBox`, are you changing its `ItemsPanel` property at all? – Joel B Fant May 29 '11 at 22:32
  • I'm using an Xceed datagrid, but I'm willing to use a normal datagrid or a listbox to accomplish this. – tofutim May 30 '11 at 05:14
  • Maybe this? http://xceed.com/CS/forums/thread/11126.aspx – tofutim May 30 '11 at 06:40
  • Since you started a bounty on this and marked Elad's answer as accepted, you should probably award him the bounty. As things stand, it will not happen automatically when the bounty's window has ended. – Joel B Fant Jun 02 '11 at 19:52

1 Answers1

2

All you need to do is to get the underlying StackPanel that's inside the ListBox. It has enough information about which elements are showing. (It implements the interface IScrollInfo).

To get the underlying StackPanel (or actually VirtualizingStackPanel) from a given ListBox, we'll have to use VisualTreeHelper to go through the Visual Tree and look for the VirtualizingStackPanel, like so:

    private VirtualizingStackPanel GetInnerStackPanel(FrameworkElement element)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;

            if (child == null) continue;

            Debug.WriteLine(child.ToString());

            if (child is VirtualizingStackPanel) return child as VirtualizingStackPanel;

            var panel = GetInnerStackPanel(child);

            if (panel != null)
                return panel;
        }

        return null;

    }

Now that we have the StackPanel, we're very close. The StackPanel has the properties VerticalOffset and ViewportHeight (both coming from IScrollInfo) that can give us all the information we need.

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var theStackPanel = GetInnerStackPanel(MyListBox);

        List<FrameworkElement> visibleElements = new List<FrameworkElement>();

        for (int i = 0; i < theStackPanel.Children.Count; i++)
        {

            if (i >= theStackPanel.VerticalOffset && i <= theStackPanel.VerticalOffset + theStackPanel.ViewportHeight)
            {
                visibleElements.Add(theStackPanel.Children[i] as FrameworkElement);
            }
        }


        MessageBox.Show(visibleElements.Count.ToString());
        MessageBox.Show(theStackPanel.VerticalOffset.ToString());
        MessageBox.Show((theStackPanel.VerticalOffset + theStackPanel.ViewportHeight).ToString());

    }
Elad Katz
  • 7,483
  • 5
  • 35
  • 66