Yet Another Silverlight Double Click Helper

I’m currently working on a Silverlight 5 project and I had to write up a class to help me manage the single click, double click, and hold down click. Even though Silverlight 5 introduced the “ClickCount” inside the event arguments, it’s not very helpful that for a double click it fires a single click event with “1 click” before it send the event for “2 clicks”.

I realise there are heaps of code snippets out there but honestly, I wasn’t impressed with many. A lot don’t seem to be that re-usable and quite a few look like they’ve been hacked together as a quick fix. In any case, here is another implementation if anyone is in need of one. To use it do the following steps:

  1. Instantiate a copy of the MouseClickManager class passing your control in through the constructor
  2. Bind to the SingleClick event to be notified for single click events.
  3. Bind to the DoubleClick event to be notified for double click events. Users have 400ms in which to do a double click.
  4. Bind to the HeldClick event to be notified when the mouse has been held down for more than 1.25secs.

An important thing to note with the MouseClickManager is that if the mouse is moved at all during a click, no event will fire. This means if you have implemented dragging of items, this code should not impact dragging.

Below the MouseClickManager code is an example of how you might use it. Hopefully the code is well labelled enough to make it fairly simple to follow.

Mouse Click Manager Class

    public class MouseClickManager
    {
        private enum MouseState
        {
            Inactive,
            MouseDown,
            SingleClick,
            DoubleClick
        }

        private MouseState _state;
        private DispatcherTimer _timerHeldClick = null;
        private DispatcherTimer _timerDoubleClick = null;
        private object _lastSender = null;
        private MouseButtonEventArgs _lastMouseEventArgs = null;

        public MouseClickManager(UIElement control)
        {
            control.MouseLeftButtonDown += new MouseButtonEventHandler(MouseLeftButtonDown);
            control.MouseLeftButtonUp += new MouseButtonEventHandler(MouseLeftButtonUp);
            control.MouseMove += new MouseEventHandler(MouseMove);

            _timerHeldClick = new DispatcherTimer();
            _timerHeldClick.Interval = new TimeSpan(0, 0, 0, 1, 250);
            _timerHeldClick.Tick += new EventHandler(OnHeldClick);

            _timerDoubleClick = new DispatcherTimer();
            _timerDoubleClick.Interval = new TimeSpan(0, 0, 0, 0, 400);
            _timerDoubleClick.Tick += new EventHandler(DoubleClickTick);
        }

        public event MouseButtonEventHandler HeldClick;
        public event MouseButtonEventHandler SingleClick;
        public event MouseButtonEventHandler DoubleClick;

        private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (_state == MouseState.Inactive)
            {
                _timerHeldClick.Start();
                _timerDoubleClick.Start();

                _state = MouseState.MouseDown;
            }

            _lastSender = sender;
            _lastMouseEventArgs = e;
        }

        private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (_state == MouseState.MouseDown)
            {
                _state = MouseState.SingleClick;
            }
            else if (_state == MouseState.SingleClick)
            {
                _state = MouseState.DoubleClick;
            }
        }

        private void MouseMove(object sender, MouseEventArgs e)
        {
            if (_state != MouseState.Inactive && _state != MouseState.SingleClick)
            {
                _timerDoubleClick.Stop();
                _timerHeldClick.Stop();

                _state = MouseState.Inactive;
                _lastSender = null;
                _lastMouseEventArgs = null;
            }
        }

        private void DoubleClickTick(object sender, EventArgs e)
        {
            _timerDoubleClick.Stop();

            switch (_state)
            {
                case MouseState.SingleClick:
                    OnSingleClick();
                    break;

                case MouseState.DoubleClick:
                    OnDoubleClick();
                    break;

                default:
                    break;
            }
        }

        private void OnSingleClick()
        {
            _timerHeldClick.Stop();

            if (SingleClick != null)
            {
                SingleClick(_lastSender, _lastMouseEventArgs);
            }

            _state = MouseState.Inactive;
            _lastSender = null;
            _lastMouseEventArgs = null;
        }

        private void OnDoubleClick()
        {
            _timerHeldClick.Stop();

            if (DoubleClick != null)
            {
                DoubleClick(_lastSender, _lastMouseEventArgs);
            }

            _state = MouseState.Inactive;
            _lastSender = null;
            _lastMouseEventArgs = null;
        }

        private void OnHeldClick(object sender, EventArgs e)
        {
            _timerHeldClick.Stop();

            if (_state == MouseState.MouseDown || _state == MouseState.SingleClick)
            {
                if (HeldClick != null)
                {
                    HeldClick(_lastSender, _lastMouseEventArgs);
                }
            }

            _state = MouseState.Inactive;
            _lastSender = null;
            _lastMouseEventArgs = null;
        }
    }

Sample Usage

In my sample below you can see I also add other event handlers to the MouseLeftButtonDown, MouseLeftButtonUp, and MouseMove events and there will be no impact to these or the MouseClickManager class.

        private void Loaded(object sender, RoutedEventArgs e)
        {
            MouseLeftButtonDown += new MouseButtonEventHandler(MotionHandler.OnMouseLeftButtonDown);
            MouseLeftButtonUp += new MouseButtonEventHandler(MotionHandler.OnMouseLeftButtonUp);
            MouseMove += new MouseEventHandler(MotionHandler.OnMouseMove);

            ClickManager.SingleClick += new MouseButtonEventHandler(SingleClick);
            ClickManager.DoubleClick += new MouseButtonEventHandler(DoubleClick);
        }

        public void SingleClick(object sender, MouseButtonEventArgs e)
        {
            // Do some stuff.
        }

        private void DoubleClick(object sender, MouseButtonEventArgs e)
        {
            // Do some stuff.
        }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s