How to get index of a ListBox item without SelectedIndex or the like (in PreviewMouseDown, nothing is "selected" yet)

StackOverflow https://stackoverflow.com/questions/21993558

Question

How to implement the GetListBoxItemIndex function below to get the index of the item I clicked on? I tried using VisualTreeHelper with no success (meaning, VisualTreeHelper obviously works, but I'm not getting anywhere with the tree search...)

private void MyListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e){
    var listBox = sender as ListBox;
    var src = e.OriginalSource as DependencyObject;
    if (src == null || listBox == null) return;

    var i = GetListBoxItemIndex(listBox,src);

    DragDrop.DoDragDrop(src, BoundCollection[i], DragDropEffects.Copy);
    // BoundCollection defined as:
    // ObservableCollection<SomeDataModelType> BoundCollection
}

Please note that there's nothing selected yet in this state, because it's a PreviewMouseDown event

Was it helpful?

Solution

The following code will help you solve the problem, the steps are in the comments,

private void ListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    // you can check which mouse button, its state, or use the correct event.

    // get the element the mouse is currently over
    var uie = ListBox.InputHitTest(Mouse.GetPosition(this.ListBox));

    if (uie == null)
        return;

    // navigate to its ListBoxItem container
    var listBoxItem = FindParent<ListBoxItem>((FrameworkElement) uie);

    // in case the click was not over a listBoxItem
    if (listBoxItem == null)
         return;

    // here is the index
    int index = this.ListBox.ItemContainerGenerator.IndexFromContainer(listBoxItem);
    MessageBox.Show(index.ToString());
}

public static T FindParent<T>(FrameworkElement child) where T : DependencyObject
{
    T parent = null;
    var currentParent = VisualTreeHelper.GetParent(child);

    while (currentParent != null)
    {

       // check the current parent
       if (currentParent is T)
       {
          parent = (T)currentParent;
          break;
       }

       // find the next parent
       currentParent = VisualTreeHelper.GetParent(currentParent);
    }

    return parent;
}

Update

What you wanted is the index of the data item you clicked on. once you get the container, you can't get the index until you get hold of the data item associated with it via the DataContext, and look for its index in the bound collection. the ItemContainerGenerator already does that for you.

In your code you're assuming that the ListBoxItem index is the same index of the data item in the bound collection, this is only true if Virtualization is turned off. If it is on then only the containers for the items in the visible region are instantiated. For example, if you have 1000 data item in your bound collection, and you've scrolled to the 50th data item, now, theoretically, the ListBoxItem at index 0 is bound to the 50th data item, which proves that your assumption is wrong.

I said earlier theoretically, because there are some hidden containers created at the top and bottom of the scrolling region when Virtualization is on, in order for keyboard navigation to work properly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top