1. Problem
You need to add gestures to the controls that don't provide the Click event.
2. Solution
You need to use the Behavior class defined in the System.Windows.Interactivity.dll assembly.
3. How It Works
Almost all Silverlight controls in Windows Phone 7 have the Tap and Double Tap gestures associated to the Click and Double Click events. By the way, there are controls such as Grid (which inherits from the Panel control) that provide neither the Click nor Double Click events.
Examining the parent classes from which the Panel class derives, we arrive at the UIElement
class that provides mouse events. Obviously, a Windows Phone doesn't
have a mouse, but the events are fired the same when a finger touches
the screen. You can manage MouseLeftButtonDown and MouseLeftButtonUp events to simulate the Tap or the Double Tap gestures on controls that derive from the UIElement
class. In your code, you have to specify the response to mouse events
for each control you need to manage. This is not the best approach to
have when you need to add gestures to elements that don't provide them.
It would be nice to have generic and reusable code that could be applied
in XAML code to every control that needs it. This can be created by
using the Behavior class.
The Behavior class (see its class diagram in Figure 1)
provides the methods to create a behavior that is applicable to
controls. Each control that derives from the class specified as generic
type during the behavior definition will have that behavior. It provides
the OnAttached method, where you can specify the event handlers that you want to trap. You can release those event handlers in the OnDetaching method provided by the Behavior class.
4. The Code
In the code for this recipe, you are going to add both the Tap and Hold gestures to the Grid control. Let's create a new Silverlight Windows Phone 7 application called AddGestureDemo from Visual Studio 2010.
The first operation you are going to perform is to add a new class to the project called AddTap. You can accomplish this step from Visual Studio 2010 by choosing Project => Add Class. The skeleton of AddTap class is generated.
The next thing to do is to add a reference to the System.Windows.Interactivity.dll
assembly. From the Solution Explorer, right-click the project name and
select Add Reference from the context menu. From the Add Reference
dialog box, select the .NET tab and search for System.Windows.Interactivity.
Now you can add the using directive in the AddTap class to point to the System.Windows.Interactivity namespace. In that way, you can derive the AddTap class from the Behavior<UIElement> class.
Next you override the OnAttached and OnDetaching methods. For the former, you are going to define event handlers for the MouseLeftButtonDown and MouseLeftButtonUp events. For the latter, you will detach them.
You will use the MouseLeftButtonDown event to store the ticks from the DateTime.Now property. Those ticks will be subtracted from the ticks picked in the MouseLeftButtonUp
event and compared to the ticks equal to 1 second. In this way, you can
tell when the user taps the screen (button down and up in less than 1
second) or when user holds a finger on the screen (button down and up in
more than 1 second). So in the former case, the DoTap event will be raised, and in the latter case, the DoHold event will be fired.
NOTE
We chose 1 second as the
time limit to distinguish a Tap gesture from a Hold gesture, but you can
change this value simply by changing the TimeSpan.TicksPerSecond value in the if condition within the AssociatedObject_MouseLeftButtonUp event handler.
. . .
using System.Windows.Interactivity;
namespace AddGestureDemo
{
public class AddTap : Behavior<UIElement>
{
public event EventHandler Tap;
public event EventHandler Hold;
private bool _IsMousePressed;
private TimeSpan _mouseLeftButtonDownTime;
protected override void OnAttached()
{
this.AssociatedObject.MouseLeftButtonDown +=
new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
this.AssociatedObject.MouseLeftButtonUp +=
new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
base.OnAttached();
}
protected override void OnDetaching()
{
this.AssociatedObject.MouseLeftButtonDown -=
AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
base.OnDetaching();
}
void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_IsMousePressed = true;
_mouseLeftButtonDownTime = TimeSpan.FromTicks(DateTime.Now.Ticks);
}
void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TimeSpan _elapsedTime = TimeSpan.FromTicks(DateTime.Now.Ticks) ...
_mouseLeftButtonDownTime;
if (_IsMousePressed && _elapsedTime.Ticks < TimeSpan.TicksPerSecond)
DoTap();
else
DoHold();
_IsMousePressed = false;
}
void DoTap()
{
if (Tap != null)
Tap(AssociatedObject, EventArgs.Empty);
}
void DoHold()
{
if (Hold != null)
Hold(AssociatedObject, EventArgs.Empty);
}
}
}
In the MainPage.xaml file, you can add some directives to use the AddTap class with the content grid. First, in the namespaces definition, you have to add two new namespaces: tap and i.
Between the ContentPanel's Grid tags, you can use our new behavior by adding the following instructions:
<i:Interaction.Behaviors>
<tap:AddTap Tap="AddTap_Tap" Hold="AddTap_Hold"/>
</i:Interaction.Behaviors>
As you will see in the following code, the i namespace is added to the PhoneApplicationPage to point to the System.Windows.Interactivity assembly.
In that way, you declare that the new behavior is associated to the grid, and it will respond to the public Tap and Hold events defined in the AddTap class.
<phone:PhoneApplicationPage
x:Class="AddGestureDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tap="clr-namespace:AddGestureDemo"
xmlns:i="clr-
namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
. . .
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Background="White">
<i:Interaction.Behaviors>
<tap:AddTap Tap="AddTap_Tap" Hold="AddTap_Hold"/>
</i:Interaction.Behaviors>
</Grid>
</Grid>
Finally, when the Tap and Hold events are raised, you simply show a MessageBox message indicating which gesture has been performed.
5. Usage
Press Ctrl+F5 from Visual
Studio 2010, and be sure to select the Windows Phone 7 Emulator as the
output target. The emulator will start, briefly showing the application,
as you can see in Figure 2.
Now press and then rapidly release the left mouse button over the white zone. A message box showing You tapped the grid is shown.
Now press and hold for more than 1 second the left mouse button over the white zone and then release it. A message box showing Hold gesture in the grid appears.