How to capture a mouse click on an Item in a ListBox in WPF?
Asked Answered
M

6

38

I want to get notified when an item in a ListBox gets clicked by the mouse, whether it is already selected or not.

I searched and found this: (http://kevin-berridge.blogspot.com/2008/06/wpf-listboxitem-double-click.html see the comments)

private void AddDoubleClickEventStyle(ListBox listBox, MouseButtonEventHandler mouseButtonEventHandler)
{
    if (listBox.ItemContainerStyle == null)
        listBox.ItemContainerStyle = new Style(typeof(ListBoxItem));
    listBox.ItemContainerStyle.Setters.Add(new EventSetter()
    {
        Event = MouseDoubleClickEvent,
        Handler = mouseButtonEventHandler
    });
}

//Usage:
AddDoubleClickEventStyle(listView1, new MouseButtonEventHandler(listView1_MouseDoubleClick));

This works, but it does it for a DoubleClick. I can't get it working for a single click though. I tried MouseLeftButtonDownEvent - as there doesn't seem to be a MouseClick event, but it's not being called.

A bit more general side question: How can I see what events do exist and which handlers correspond to them and when they actually do something? For example, what tells me that for a MouseDoubleClickEvent I need a MouseButtonEventHandler? Maybe for a MouseLeftButtonDownEvent I need some other handler and that's why it's not working?

I also tried subclassing ListBoxItem and override OnMouseLeftButtonDown - but it doesn't get called either.

Marc

Megagamete answered 13/8, 2009 at 11:22 Comment(1)
I have chosen to wrap each listboxitem with button and use button event instead. #17057522Logan
A
58

I believe that your MouseLeftButtonDown handler is not called because the ListBox uses this event internally to fire its SelectionChanged event (with the thought being that in the vast majority of cases, SelectionChanged is all you need). That said, you have a couple of options.

First, you could subscribe to the PreviewLeftButtonDown event instead. Most routed events have a routing strategy of Bubbling, which means that the control that generated the event gets it first, and if not handled, the event works its way up the visual tree giving each control a chance at handling the event. The Preview events, on the other hand, are Tunneling. This means that they start at the root of the visual tree (generally Window), and work their way down to the control that generated the event. Since your code would get the chance to handle the event prior to the ListBoxItem, this will get fired (and not be handled) so your event handler will be called. You can implement this option by replacing MouseDoubleClickEvent in your sample with PreviewMouseLeftButtonDown.

The other option is to register a class handler that will be notified whenever a ListBoxItem fires the MouseLeftButtonDown event. That is done like this:

EventManager.RegisterClassHandler(typeof(ListBoxItem),
    ListBoxItem.MouseLeftButtonDownEvent,
    new RoutedEventHandler(this.MouseLeftButtonDownClassHandler));

private void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
{
}

Class Handlers are called before any other event handlers, but they're called for all controls of the specified type in your entire application. So if you have two ListBoxes, then whenever any ListBoxItem is clicked in either of them, this event handler will be called.

As for your second question, the best way to know what type of event handler you need for a given event, and to see the list of events available to a given control, is to use the MSDN documentation. For example, the list of all events handled by ListBoxItem is at http://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem_events.aspx. If you click on the link for an event, it includes the type of the event handler for that event.

Afrit answered 13/8, 2009 at 12:19 Comment(4)
One thing, I'd like to add. DON'T use the ButtonDown event. Use the ButtonUp event. Applications that do stuff as soon as you hold the button down are just weird. And it also has a user interaction reason. In basically all popular applications you can cancel button clicks by moving the mouse away before releasing the button.Thoraco
@StefanFabian yes but items in a listbox normally getselected on mousedown. And moving mouse away can still work.Corked
@StefanFabian I've been trying both of the preview events (up and down) and I like your thinking that with up you can move the mouse out of the control and cancel the click, but I'm not seeing that happen. Regardless of where the mouse is when I release the button, my event gets called (using the System.Windows.Interaction triggers method).Symmetry
@PaulGibson hm I assume they made the design decision that up is called always and clicked only if it's not canceled. So, you'll have to check if the mouse is still over the element yourself if you want the cancel functionality.Thoraco
S
25

There is also another way - to handle PreviewMouseDown event and check if it was triggered by the list item:

In XAML:

<ListBox PreviewMouseDown="PlaceholdersListBox_OnPreviewMouseDown"/> 

In codebehind:

private void PlaceholdersListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    var item = ItemsControl.ContainerFromElement(sender as ListBox, e.OriginalSource as DependencyObject) as ListBoxItem;
    if (item != null)
    {
        // ListBox item clicked - do some cool things here
    }
}

Was inspired by this answer, but it uses listbox by name, I propose to use sender argument to avoid unnecessary dependencies.

Surtout answered 23/7, 2014 at 11:52 Comment(3)
anyway to find the index of the ListBoxItem that was clicked on?Disputatious
So far the best solution I've found is to iterate through the list of items until you find a match. This works for my case since I have a very small lists, but it may not scale well for others.Disputatious
Spent a lot of time looking for the right event with no success... Thank you so much Pavel!Memorable
U
22

I think the first option in Andy's answer, of using PreviewMouseLeftButtonDown, is the way to go about this. In XAML it would look like this:

<ListBox Name="testListBox">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <EventSetter
                Event="PreviewMouseLeftButtonDown" 
                Handler="ListBox_MouseLeftButtonDown" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>
Uchish answered 13/8, 2009 at 16:26 Comment(0)
U
10

There is another way to get MouseDown event in ListBox. You can add event handler for events that are marked as handled by using handledEventsToo signature of AddHandler method:

myListBox.AddHandler(UIElement.MouseDownEvent, 
        new MouseButtonEventHandler(ListBox_MouseDown), true);

Third parameter above is handledEventsToo which ensures that this handler will be invoked no matter if it is already marked as Handled (which ListBoxItem does in ListBox).
See Marking Routed Events as Handled, and Class Handling for explanation.
See How to Attach to MouseDown Event on ListBox for example.

Underdrawers answered 25/7, 2010 at 14:49 Comment(0)
A
4

You can use Event="MouseLeftButtonUp"
Unlike "PreviewLeftButtonDown" it will get the ListBoxItem handled too.

Aerobatics answered 17/3, 2018 at 22:45 Comment(0)
S
0

You can use the SelectionChangedEventArgs argument of the SelectionChanged event to find what item is add or removed through AddedItems and RemovedItems, usually only have the latest clicked on, or if not, then look at the last item which is the count-1.

Sniggle answered 22/6, 2015 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.