Silverlight 5 has given us a new property in the MouseButtonEventArgs called Click Count. Before the release of this Silverlight 5, there are no implementation of “double click” event, you need to write your code to implement that. But instead of giving double click event, the Silverlight gave us the ability to tracking how much clicking we done. But there’s a limitation of the clickcount, and in this article will look at creating a solution to resolve it.
Implement ClickCount
For this post we will be using a simple test harness that look like this:
The blue box on the left is going to be our click area. The right side is a simple ListBox with the ItemsSource set to an ObservableCollection<string>. And here is what the XAML looks like:
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Blue"
Width="100"
Height="100"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<ListBox x:Name="lbClicks"
Margin="5,5,5,5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"/>
</Grid>
You can use the code below for the code behind that xaml
private ObservableCollection<string> _events =
new ObservableCollection<string>();
public MainPage()
{
InitializeComponent();
lbClicks.ItemsSource = _events;
}
look at the first example. To track the ClickCount we need to add the MouseLeftButtonDown event. Add the code below to our rectangle in XAML:
<Rectangle Fill="Blue"
Width="100"
Height="100"
VerticalAlignment="Center"
HorizontalAlignment="Center"
MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"/>
In our event handler, we can easily track the click count by adding a new string to our _events collection. You will notice that the ClickCount will increase each time you click the mouse button if you are quick enough. Pause in the clicking for a moment and the ClickCount will reset!.
private void Rectangle_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e)
{
_events.Add("Click Count : " + e.ClickCount);
}
You can see that we can track a double click or triple click or a click-15 if we want.
All we would need to add is a conditional statement based off of the ClickCount property.
if(e.ClickCount == 2)
{
MessageBox.Show("Double click");
}
Issue
however, While this is an interesting solution for the missing functionality, it does have a missing component that will cause you some grief. Instead of our conditional statement that we had above, what if our code looked something like this:
if(e.ClickCount == 1)
{
MessageBox.Show("Single click");
}
else if(e.ClickCount == 2)
{
MessageBox.Show("Double click");}
}
If you run this code, you will notice that you will never fire the “Double click” MessageBox. This is because the moment the first MessageBox is displayed, the focus is taken away from our rectangle and thus the ClickCount is started over when we come back to it. This means that attempting to implement two events is a bit tricky.
How to make it worked!
So How to track multiple click? You need to write some code. In order to create a reusable solutionwe decide to implement solution as a behavior. This will allow us to add a multiple click handler to any UIElement in XAML without having to do a bunch of wiring. However, the code is straight forward enough you can easily remove it and create your own reusable solution.
Every time a user clicks on your object a DispatcherTimer is stopped and then started. In addition we track the latest sender object and the ClickCount. Once the user stops clicking on your object, the DispatcherTimer is allowed to hit the Click event and we fire an event giving the latest ClickCount and the sending object.
Our code for the behavior will looks like this:
public class MultiClick : Behavior<UIElement>
{
private DispatcherTimer _timer;
private int _clickCount;
private object _sender;
public MultiClick()
{
_timer = new DispatcherTimer()
{Interval = new TimeSpan(0, 0, 0, 0, 250)};
_timer.Tick += _timer_Tick;
}
#region events and delegates
public delegate void MouseMultiClickHandler(object sender,
int clickCount);
public event MouseMultiClickHandler MouseMultiClick;
#endregion
#region Behavior Overrides
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseLeftButtonDown
+= AssociatedObject_MouseLeftButtonDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseLeftButtonDown
-= AssociatedObject_MouseLeftButtonDown;
}
#endregion
#region EventHandlers
void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
if (MouseMultiClick != null)
MouseMultiClick(_sender, _clickCount);
}
void AssociatedObject_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e)
{
_timer.Stop();
_clickCount = e.ClickCount;
_sender = sender;
_timer.Start();
}
#endregion
}
The OnAttached/OnDetaching methods are how the Behavior is attached to the object, an UIElement is our case. The AssociatedObject is simply to object that the Behavior is attached to. As we discussed before, the DispatcherTimer is stopped and started when the user clicks on the object. Once the DispatcherTimer hits the Click event, it(dispatcher time) will stop stopped, a MouseMultiClick event is fired and the Behavior waits for the next MouseLeftButtonDown event.
**Note: Its important to set the time at 200 milliseconds. And no longer than that you may get unexpected results.
This is how we use our behavior, Here is what are Rectangle looks like with our Behavior. I have also removed the MouseLeftButtonDown event.
<Rectangle Fill="Blue"
Width="100"
Height="100"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<i:Interaction.Behaviors>
<local:MultiClick MouseMultiClick="MultiClick_MouseMultiClick"/>
</i:Interaction.Behaviors>
</Rectangle>
You can see we attached to the MouseMultiClick event we created in our Behavior. The code behind is pretty much identical to our original code behind, where we are simply tracking the event:
private void MultiClick_MouseMultiClick(object sender,
int clickCount)
{
_events.Add("Multi Click : " + clickCount);
}
If you run the project again, you will now see that instead of getting an event for every click, you only get one event for each round of clicking. So you can track single, double, triple, etc. From there you can implement your code based on your project needs.