My scenario, simplified: I have a ListView containing rows of Employees, and in each Employee row, there are buttons "Increase" and "Decrease" adjusting his salary.
Pretend that in my program, double-clicking an Employee row means "fire this person".
The problem is that while I'm clicking "Increase" rapidly, this triggers a double click event on the ListViewItem. Naturally, I don't want to fire people when I'm just increasing their salary.
According to how all other events work, I expect to be able to solve this by setting Handled=true
on the event. This, however, doesn't work. It appears to me that WPF generates two separate, completely unlinked, double click events.
The following is a minimal example to reproduce my issue. The visible components:
<ListView>
<ListViewItem MouseDoubleClick="ListViewItem_MouseDoubleClick">
<Button MouseDoubleClick="Button_MouseDoubleClick"/>
</ListViewItem>
</ListView>
And the handler code:
private void Button_MouseDoubleClick(object s, MouseButtonEventArgs e) {
if (!e.Handled) MessageBox.Show("Button got unhandled doubleclick.");
e.Handled = true;
}
private void ListViewItem_MouseDoubleClick(object s, MouseButtonEventArgs e) {
if (!e.Handled) MessageBox.Show("ListViewItem got unhandled doubleclick.");
e.Handled = true;
}
After firing up this program and double-clicking the listed button, both messageboxes show up in sequence. (Also, the button is stuck in the down position after this.)
As a "fix" I can, on the ListViewItem handler, inspect the visual tree attached to the event and check that "there is a button there somewhere" and thus discard the event, but this is a last resort. I want to at least understand the issue before coding such a kludge.
Does anyone know why WPF does this, and an elegant idiomatic way to avoid the problem?
I think you'll find that the MouseDoubleClick
event is an abstraction on top of the MouseDown
event. That is, if two MouseDown
events occur in quick enough succession, the MouseDoubleClick
event will also be raised. Both the Button
and ListViewItem
appear to have this logic, so that explains why you're seeing two distinct MouseDoubleClick
events.
As per MSDN:
Although this routed event seems to follow a bubbling route through an element tree, it actually is a direct routed event that is raised along the element tree by each UIElement. If you set the Handled property to true in a MouseDoubleClick event handler, subsequent MouseDoubleClick events along the route will occur with Handled set to false.
You could try handling MouseDown
on the Button
and setting that to handled so that it doesn't propagate to the ListViewItem
.
Wish I could verify this myself but I'm .NET-less at the moment.
The MSDN documentation for the MouseDoubleClick does give a suggestion on how to keep the MouseDoubleClick event from bubbling up:
Control authors who want to handle mouse double clicks should use the MouseLeftButtonDown event when ClickCount is equal to two. This will cause the state of Handled to propagate appropriately in the case where another element in the element tree handles the event.
So you could hanlde the MouseLeftButtonDown event and set hanged to true if ClickCount is two. But this fails on Buttons because they already handle the MouseLeftButtonDown and don't raise that event.
But there is still the PreviewMouseLeftButtonDown event. Use that on your buttons to set handled to true when ClickCount equals two as below:
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
if (e.ClickCount == 2)
e.Handled = true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With