MouseDoubleClick events don't bubble

2019-03-15 04:35发布

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?

7条回答
叛逆
2楼-- · 2019-03-15 05:05

Since there have been no definite answers to this question, this is the workaround I ended up using:

protected override void ListViewItem_MouseDoubleClick(MouseButtonEventArgs e) {
    var originalSource = e.OriginalSource as System.Windows.Media.Visual;
    if (originalSource.IsDescendantOf(this)) {
        // Test for IsDescendantOf because other event handlers can have changed
        // the visual tree such that the actually clicked original source
        // component is no longer in the tree.
        // You may want to handle the "not" case differently, but for my
        // application's UI, this makes sense.
        for (System.Windows.DependencyObject depObj = originalSource;
             depObj != this;
             depObj = System.Windows.Media.VisualTreeHelper.GetParent(depObj))
        {
            if (depObj is System.Windows.Controls.Primitives.ButtonBase) return;
        }
    }

    MessageBox.Show("ListViewItem doubleclicked.");
}

Class names are here unnecessarily typed with full namespaces for documentation purposes.

查看更多
登录 后发表回答