Tuesday, July 22, 2008

WPF Double Click Events

First some background. I am trying to make a list of CheckBox Objects that when Single-Clicked will add their name to a list and when Double-Clicked will remove all others from the list and then add itself.

In the good old days (WinForms, Win32) when a Double-Click event was passed you would normally see a Single-Click event fire just before the Double-Click. This system worked out fine because logically you expect the Single-Click since it happened first and then you could override the Single-Click with whatever you wanted to have happen in the Double-Click Event.

So imagine my surprise in WPF when I made a CheckBox and subscribed to both the SingleClickEvent and the MouseDoubleClick and when the Double-Click fired I received a Double-Click followed by the Single-Click. Now this throws me off, but in this case I can make my Single-Click method which follows my Double-Click method do what I need it to do, however, when I go do unit tests I find that I have to write a test like this:
DoubleClickEvent.Raise(this, new EventArgs());
SingleClickEvent.Raise(this, new EventArgs());
I don’t like this as a test because it does not logically follow what you would expect from a Double-Click Event.

With the background out of the way let us explore some possible solutions.

So in WPF I decided to work with the MouseLeftButtonUpEvent and the MouseLeftButtonDownEvent. This was a mistake as I never saw the events fire. This, I think, was because of the hierarchy of the CheckBox eats these events in order to have its own click event. Lucky for me there is a PreviewMouseLeftButtonUpEvent and a PreviewMouseLeftButtonDownEvent. These events are fired and I can then use the MouseEventArgs.ClickCount to know if I have a single or double click event. But even this isn’t working correctly. I believe that a MouseUp event is much more accurate than a MouseDown event because if a user was to click then realize It was a mistake they would usually drag the mouse off of the object and let go and the click event wouldn’t not fire. Now if I was using the MouseUp event the ClickCount would never grow in count to register a double click. Therefore I am stuck with using the MouseDownEvent to get the behavior I wish from ClickCount. This I don’t want to do because it doesn’t give me the correct behavior out of user side which is stated above.

I think the final option is to derive a control from CheckBox and write my own event handling to force behavior as I expect it. Again I don’t want to do this because it seems like adding some complexity to the system to counter a problem that should not be a problem in the first place.

In conclusion I have no real answer for this issue that is not a hack or adding too much complexity to the system for how simple the issue really is I guess I could do a timer and try to do all the events myself but again it is much to complex.

Has anyone else had this sort of problem with a single click following a double click or am I going crazy?


Example projects both WinForms and WPF

2 comments:

Harley Pebley said...

Hi Mike. Hope things are going well.

No you're not crazy. WPF handles double click differently than WinForms.

If I understand correctly, WinForms defers double click handling to the WinAPI that fires a WM_ event indicating the double click. The timing is controlled by a Windows configuration parameter.

OTOH, WPF for some reason decided to implement their own double click detection. The click event is still driven by the underlying WM_ mouse events but the double click event is driven by internal WPF code. Weird. Seems like more work for the WPF team.

All that weirdness aside, I think you'll find that if you set e.Handled to true in your double click handler you won't get the second click event after the double click event.

Hope that helps.
Cheers.

Harley Pebley said...

Doh!

For some reason this just popped up in my RSS feed even though it's how many years old? Oh, well. I'll leave it there in case someone else trips over it in their trips around the interwebs.