The simplest way to detect DoubleClick in Silverlight

by Jim McCurdy 7. August 2009 22:32

Here is a simple static class, MouseButtonHelper, that offers a single method, IsDoubleClick,  to determine if a standard MouseLeftButtonDown or MouseLeftButtonUp event is a double click.  In the past I have used timers, and Triggers and Behaviors to accomplish the same thing, but this approach is less code, less XAML, and uses a lot less resources.

Usage is as follows:

  • In XAML or in code behind, handle the standard MouseLeftButtonDown or MouseLeftButtonUp event for the object you wish to perform double click handling.
  • Implement the handler function for the above event as follows:
public void OnMouseButtonDown(object sender, MouseButtonEventArgs e)
{
	bool doubleClick = MouseButtonHelper.EventIsDoubleClick(sender, e);
	if (doubleClick)
		MessageBox.Show("Double click detected!", "Alert", MessageBoxButton.OK);
}

The code below has been tested and used with Silverlight 4.

using System;
using System.Windows;
using System.Windows.Input;

namespace ClassLibrary
{
	internal static class MouseButtonHelper
	{
		private const long k_DoubleClickSpeed = 500;
		private const double k_MaxMoveDistance = 10;

		private static long _LastClickTicks = 0;
		private static Point _LastPosition;
		private static WeakReference _LastSender;

		internal static bool IsDoubleClick(object sender, MouseButtonEventArgs e)
		{
			Point position = e.GetPosition(null);
			long clickTicks = DateTime.Now.Ticks;
			long elapsedTicks = clickTicks - _LastClickTicks;
			long elapsedTime = elapsedTicks / TimeSpan.TicksPerMillisecond;
			bool quickClick = (elapsedTime <= k_DoubleClickSpeed);
			bool senderMatch = (_LastSender != null && sender.Equals(_LastSender.Target));

			if (senderMatch && quickClick && position.Distance(_LastPosition) <= k_MaxMoveDistance)
			{
				// Double click!
				_LastClickTicks = 0;
				_LastSender = null;
				return true;
			}

			// Not a double click
			_LastClickTicks = clickTicks;
			_LastPosition = position;
			if (!quickClick)
				_LastSender = new WeakReference(sender);
			return false;
		}

		private static double Distance(this Point pointA, Point pointB)
		{
			double x = pointA.X - pointB.X;
			double y = pointA.Y - pointB.Y;
			return Math.Sqrt(x * x + y * y);
		}
	}
}

Comments (11) -

Hector
Hector
8/5/2010 1:34:44 PM #

Cool

This really helpme a LOT ! !

Reply

Mosherot
Mosherot
11/12/2010 6:15:07 AM #

When I used custume usercontrols on a canvas and I wanted to doubleclick the object and the canvas separately - your code had problems - it detected both the usercontrol and the canvas. So i needed to change your code a little bit.
Hopefully, this will serve someone else...Smile


internal static class MouseButtonHelper
  {
    private const long k_DoubleClickSpeed = 500;
    private const double k_MaxMoveDistance = 10;

    private static long m_LastClickTicks = 0;
    private static Point m_LastPosition;
    private static object m_LastSender;
    
    internal static bool IsDoubleClick(object sender, MouseButtonEventArgs e)
    {
      bool senderMatch = sender.Equals(m_LastSender);
      long clickTicks = DateTime.Now.Ticks;
      Point position = e.GetPosition(null);
      long elapsedTicks = clickTicks - m_LastClickTicks;
      long elapsedTime = elapsedTicks / TimeSpan.TicksPerMillisecond;
      double distance = position.Distance(m_LastPosition);
      
      if (elapsedTime > k_DoubleClickSpeed){ // if overdue, no doubleclick
        m_LastClickTicks = clickTicks;
        m_LastPosition = position;
        m_LastSender = sender;
        return false;        
      }
      else if (senderMatch && elapsedTime <= k_DoubleClickSpeed && distance <= k_MaxMoveDistance)
      {
        // Double click!
        m_LastClickTicks = 0;
        m_LastSender = null;
        return true;
      }
          
      // Not a double click
      m_LastClickTicks = clickTicks;
      m_LastPosition = position;
      return false;
    }

    private static double Distance(this Point pointA, Point pointB)
    {
      double x = pointA.X - pointB.X;
      double y = pointA.Y - pointB.Y;
      return Math.Sqrt(x * x + y * y);
    }
  }

Your code was VERY helpful and was really the best solution available. Thank you.

Reply

Alex
Alex
1/5/2011 9:38:03 AM #

You should use WeakReference to lastSender object in order to work correctly with GC

Reply

Jim McCurdy
Jim McCurdy
1/5/2011 11:25:16 AM #

Good suggestion Alex.   I made the change to use a WeakReference, and also incorporated Mosherot's improvements.

Thanks

Reply

Dave K
Dave K
10/27/2011 2:57:41 PM #

This is a very nice solution indeed to the double click issue in Silverlight, thanks for sharing! BTW have you seen the following:

msdn.microsoft.com/.../...clickcount(v=vs.96).aspx

Looks like we'll be able to do things in a more standard way in SL5.

Thanks again for the post, great solution.

Reply

jim mccurdy
jim mccurdy
10/27/2011 3:31:01 PM #

Yea, it will be a standard part of Silverlight 5.  In the mean time...

Reply

lambacini
lambacini
12/13/2011 8:04:49 AM #

nice solution.really help me Smile
Thanks.

Reply

Andr&#233; Nascentes
André Nascentes
1/6/2012 7:08:45 AM #

Thanks a lot!

Reply

truyenle
truyenle
2/17/2012 5:49:37 PM #

Cool! Simple and work like charm.

Just a question, I'm using Accordion control as
XAML
...
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
...
<controls:Accordion x:Name="Accordion" Margin="5" Grid.Row="0" Background="DarkGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                             ItemsSource="{Binding VideoStreams}">

...

The result will be multi items will be added in Accordion, how to integrated your class so that the app detect double click on the child items not the Parent one (Accordion).

many thanks for your share,.

Reply

Fiach
Fiach
3/2/2012 3:31:09 PM #

Thank you so much! Smile

Much better than the Silverlight toolkit!

Reply

downWithTheBass
downWithTheBass
4/24/2012 2:43:08 PM #

Perfect and simple! Thanks for this

Reply

Pingbacks and trackbacks (3)+

Add comment

biuquote
  • Comment
  • Preview
Loading

About Me

My Photo Jim is always looking for new clients in need of software development expertise.  He is particularly well suited for Silverlight, WPF, and ASP.NETprojects.

Professional Biography

Jim McCurdy operates Face to Face Software, where he works designing, developing, and managing software projects for a variety of clients.

Jim currently specializes in contract development projects for Silverlight, WPF, and ASP.NET platforms. Recent projects include two complete web sites developed using Silverlight, .NET, and C#. The first is YinYangMoney.com, a unique financial and lifestyle planning web application. The second is McPivot.com, a unique web application that presents custom queries and visualization of baseball data back to the 1800's.

Jim has worked in the software industry for as long as he can remember, and has broad expertise in Web, Windows, and systems technologies. Jim has been a team player on many agile development projects throughout his career, and is a founding member at software startups Astral Development and Powerhouse Entertainment.

You can Email Jim at Face to Face Software.

Blog Archive

Welcome Readers