Follow Me on Instagram Subscribe via RSS Feed

Creating a Multi-Click Event in Silverlight 5

September 8, 2011

One of the things missing in Silverlight has been an implementation of a “double click” event.  Prior to Silverlight 5 you were basically on your own to write the code to implement this.  With Silverlight 5 out, we were given a new property in the MouseButtonEventArgs call ClickCount.  Instead of simply providing a double click event, the Silverlight team gave us the ability to track double click, triple click, and so on. However, there is a limitation to the ClickCount and this article will look at creating a solution to resolve it.  However, first lets take a quick look at how to use the new click count.


Implementing ClickCount

Using the ClickCount is a very straight forward process. For this post we will be using a very simple test harness that will look like this:

image

Not the most complex layout in the world, is it?  The beautiful blue box on the left is going to be our click area.  So if you want to click, that is a great place to click.  The right side is a simple ListBox with the ItemsSource set to an ObservableCollection<string>.  Pretty simple right?  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>

Our code behind it also nice and simple (this is a demo after all Smile).

private ObservableCollection<string> _events = 
             new ObservableCollection<string>();
public MainPage()
{
    InitializeComponent();
    lbClicks.ItemsSource = _events;
}

 

Now that we have that out of the way, we can look at the first example.  To track the ClickCount we need to add the MouseLeftButtonDown event.  So let’s add it 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 quickly see that we can track a double click or triple click or a click-42 if we so desire (I would advise against that unless you are adding easter egg’s in your code Smile).

All we would need to add is a conditional statement based off of the ClickCount property.

if(e.ClickCount == 2)
{
  MessageBox.Show("Double click");
}

The Flaw

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.

 

The Work Around

So what do we do if we want to track multiple click events?  Yep, you guessed it, you gotta write some code.  In order to create a reusable solution, I decided to implement the 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.

The concept behind the behavior is fairly straight forward.  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 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 it’s Click event, it is stopped, a MouseMultiClick event is fired and the Behavior waits for the next MouseLeftButtonDown event.

**Note: It is important to note that the timer is set at 200 milliseconds.  While you can adjust this some, if the time is longer than that used by Silverlight to create the ClickCount you will get unexpected results.

So how do we use our awesome new 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.

Pretty simple stuff, huh?  But it’s a great little utility to add to your collection.  It also gives you the opportunity to take a look at how Behaviors work and their great reusability features.

The link to the sample project is below.  I left both the behavior and the MouseLeftButtonDown events in the code so you can compare how the behave differently from each other.

Download project here: SLMultiClick.zip

Comments (4)

Trackback URL | Comments RSS Feed

  1. Akshaya says:

    Hi

    I was trying to use this with a button control but was unable to, can you please explain how can I do it?

    Thanks

  2. Tony Champion says:

    That’s actually interesting. I hadn’t tried that yet. It seems like it will not work. The Click event does not track a Click count and the MouseLeftButtonDown (or Up) is not firing. I will inquire about this and get back to you.

  3. Akshaya says:

    Just FYI, I have tried already setting Button’s ClickMode=Release and added the MouseLeftButtonUp event using Button.AddHandler, using this the click event was no more messing things up and my MouseLeftButtonUp method was getting called. But the problem is that its getting stuck at clickcount=1 and is not going more.

    Perhaps, some extension can be done to this approach using what you showed above…what do you think?

  4. Akshaya says:

    @Tony Champion
    Hi, any luck with this? or can you guide me on to something?

Leave a Reply