#657 – Detecting Double Clicks in User Interface Elements

You can react to a user double-clicking on a user interface element by handling one of the mouse button events and checking the MouseButtonEventArgs.ClickCount field.  When a user double-clicks on an element, all of the mouse down and mouse up events will be fired twice.  During the second round of events, the ClickCount property will have a value of 2.

Here’s an example, where we instrument the MouseDown and MouseUp events for a Label.

When we just click on the element, we see a MouseDown and a MouseUp event, with a ClickCount of 1.

If we double-click on the Label, we’ll see two sets of events.  The second MouseDown event will report a ClickCount of 2.

 

We could distinguish between double-clicking with the left mouse button vs. the right mouse button by handling either the MouseLeftButtonDown and MouseRightButtonDown events.

 

Advertisement

#656 – Data Available to Mouse Button Event Handlers

The various mouse button events that fire when a mouse button is pressed or released all provide data to the event handler as part of a MouseButtonEventArgs object.  The information contained in this object and available to button press event handlers is listed below.

  • LeftButton – Current state of left button (Released | Pressed)
  • MiddleButton – Current state of middle button (Released | Pressed)
  • RightButton – Current state of right button (Released | Pressed)
  • ButtonState – Current state of whichever button triggered the event (Released | Pressed)
  • ChangedButton – Which button triggered the event?  (Left | Middle | Right | XButton1 | XButton2)
  • ClickCount – # of times button was clicked (events will fire multiple times)
  • MouseDevice – Access to a MouseDevice object, which allows getting general information about the mouse position or mouse buttons

#655 – Bubbling Mouse Events Swallowed by Some Controls

When you click a mouse button on a Label control, you’ll see a series of tunneling and bubbling events, as shown below.

However, if you click a mouse button on a TextBox control, you’ll only see some of these events fire.  Specifically, handlers that you define will only see the PreviewXxx versions of the events.  The other events are swallowed by the TextBox control.

In general, controls that do something as the result of a user pressing a mouse button will swallow the related events.  Clicking within a TextBox gives it focus.  Clicking on a Button or ComboBox also results in the control responding to the click, so these controls also swallow the non-preview events.

#654 – Mouse Events for Button Presses

When a user clicks on a mouse button, there are a series of preview events (tunneling) and actual events (bubbling) that travel up and down the logical tree.

For example, for a Label contained in a StackPanel, which is contained in a Window, the full sequence of events for a left mouse button click on the Label is:

  • PreviewMouseLeftButtonDown for Window
  • PreviewMouseDown for Window
  • PreviewMouseLeftButtonDown for StackPanel
  • PreviewMouseDown for StackPanel
  • PreviewMouseLeftButtonDown for Label
  • PreviewMouseDown for Label
  • MouseLeftButtonDown for Label
  • MouseDown for Label
  • MouseLeftButtonDown for StackPanel
  • MouseDown for StackPanel
  • MouseLeftButtonDown for Window
  • MouseDown for Window
  • PreviewMouseLeftButtonUp for Window
  • PreviewMouseUp for Window
  • PreviewMouseLeftButtonUp for StackPanel
  • PreviewMouseUp for StackPanel
  • PreviewMouseLeftButtonUp for Label
  • PreviewMouseUp for Label
  • MouseLeftButtonUp for Label
  • MouseUp for Label
  • MouseLeftButtonUp for Stackpanel
  • MouseUp for StackPanel
  • MouseLeftButtonUp for Window
  • MouseUp for Window

 

#653 – MouseMove Events

Most controls in WPF inherit a series of mouse input events from the UIElement class.  This includes the PreviewMouseMove (tunneling) and MouseMove (bubbling) events.

The MouseMove events indicate that the user is moving the mouse across the element in question.  As the user moves the mouse, the event fires for the control that the mouse is over.

Because these events propagate, however, the events will also tunnel down to the control that the mouse is over and then bubble back up the logical tree.

For example, if we have a TextBox inside a StackPanel that is inside a Window and we move the mouse around within the TextBox, we’ll see the following events fired:

  • PreviewMouseMove on Window
  • PreviewMouseMove on StackPanel
  • PreviewMouseMove on TextBox
  • MouseMove on TextBox
  • MouseMove on StackPanel
  • Mousemove on Window

 

#652 – Mouse Event Summary

Below are the events defined for ContentElement, UIElement and UIElement3D classes related to mouse input:

  • PreviewMouseMove (tunneling) / MouseMove (bubbling) – Mouse moves while over element
  • GotMouseCapture  (bubbling) – Element has captured the mouse, receiving mouse input   
  • LostMouseCapture (bubbling) – Element no longer is receiving mouse input
  • MouseEnter (direct) – Mouse pointer has entered boundaries of this element
  • MouseLeave (direct) – Mouse pointer has left boundaries of this element
  • PreviewMouseDown (MouseDown  – Mouse button is pressed
  • PreviewMouseLeftButtonDown (direct, appears to tunnel) / MouseLeftButtonDown (direct, appears to bubble) – Left mouse button is pressed
  • PreviewMouseLeftButtonUp (direct, appears to tunnel) / MouseLeftButtonUp (direct, appears to bubble) – Left mouse button is released
  • PreviewMouseRightButtonDown (direct, appears to tunnel) / MouseRightButtonDown (direct, appears to bubble) – Right mouse button is pressed
  • PreviewMouseRightButtonUp (direct, appears to tunnel) / MouseRightButtonUp (direct, appears to bubble) – Right mouse button is released
  • PreviewMouseUp (tunneling) / MouseUp (bubbling) – Mouse button released
  • PreviewMouseWheel (tunneling) / MouseWheel (bubbling) – Mouse wheel position changed

#651 – Using Static Members of the Keyboard Class

The KeyboardDevice class represents the current state of the keyboard and is accessible from within keypress event handlers using the KeyEventArgs.KeyboardDevice property.

        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(string.Format("A key pressed? {0}", e.KeyboardDevice.IsKeyDown(Key.A)));
        }

You can also use the Keyboard.PrimaryDevice property to access the current KeyboardDevice object.

        public void CheckForA()
        {
            Console.WriteLine(string.Format("A key pressed? {0}", Keyboard.PrimaryDevice.IsKeyDown(Key.A)));
        }

In addition to providing access to the KeyboardDevice object, the Keyboard class also provides several static methods that give you the same information.  This is easier than referencing the PrimaryDevice property.

        public void CheckForA()
        {
            Console.WriteLine(string.Format("A key pressed? {0}", Keyboard.IsKeyDown(Key.A)));
        }

#650 – Getting Information About Keyboard Keys from Any Method

You can use the KeyEventArgs.KeyboardDevice property within a keypress handler to get information about any keyboard key.  For example:

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    Console.WriteLine(string.Format("Toggle info: Caps Lock:{0}, Scroll Lock: {1}, Num Lock: {2}",
        e.KeyboardDevice.IsKeyToggled(Key.CapsLock),
        e.KeyboardDevice.IsKeyToggled(Key.Scroll),
        e.KeyboardDevice.IsKeyToggled(Key.NumLock)));
}

You can also access the same KeyboardDevice object from any method, using the Keyboard.PrimaryDevice property (found in System.Windows.Input). This property will refer to an instance of a KeyboardDevice object.

        public void MyFunctionToGetKeyInfo()
        {
            Console.WriteLine(string.Format("Toggle info: Caps Lock:{0}, Scroll Lock: {1}, Num Lock: {2}",
                Keyboard.PrimaryDevice.IsKeyToggled(Key.CapsLock),
                Keyboard.PrimaryDevice.IsKeyToggled(Key.Scroll),
                Keyboard.PrimaryDevice.IsKeyToggled(Key.NumLock)));
        }

#649 – KeyStates Property Combines IsDown and IsToggled

In a keypress handler, you can check several states for the key that triggered the event.  The IsDown and IsToggled properties indicate whether the key in question is currently down and whether it’s in the toggled state, respectively.

You can also get information on the current state of the key using the KeyStates property.  The property is an enumerated value containing a bitwise combination of the Down and Toggled values.

        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(string.Format("--- PreviewKeyDown for key {0}", e.Key));
            Console.WriteLine(string.Format("  IsDown={0}, IsToggled={1}", e.IsDown, e.IsToggled));
            Console.WriteLine(string.Format("  KeyStates={0}", e.KeyStates));
        }

#648 – Check the Toggled State of Any Key

You can use the KeyboardDevice.IsKeyToggled method to check the toggled state of any of the three toggle keys–Caps Lock, Scroll Lock, or Num Lock.

For example, we can do this in a keypress event handler for a TextBox.

        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(string.Format("Toggle info: Caps Lock:{0}, Scroll Lock: {1}, Num Lock: {2}",
                e.KeyboardDevice.IsKeyToggled(Key.CapsLock),
                e.KeyboardDevice.IsKeyToggled(Key.Scroll),
                e.KeyboardDevice.IsKeyToggled(Key.NumLock)));
        }

Now, as we type, we’re told of the current state of these three toggle keys.