Windows Phone 7 Quick Tutorials: Part 7 - Touch

WP7 application can support different input methods: touch, on-screen keyboard, hardware keyboard, sensors, microphone and phone hardware buttons. This article will focus on touch.

Users can manipulate the applications with single or multi-touch gestures. Single gestures (operations performed with a single hand) are tap, double tap, pan, flick and touch and hold. Multi-touch gestures are pinch and stretch. WP7 supports multi touch gestures with at least four fingers.

Touch gesture are exposed through events to developers. There are two programming interfaces available for multi-touch: low-level and high-level. In addition, the Silverlight for Windows Phone Toolkit, a Microsoft project available on codeplex offers even a higher-level support for multi-touch.

If you run with the emulator, the mouse activity is converted to touch. However, you will be able to test only the single-touch gestures. For implementing support for multi-touch gesture you should use a real phone.

Low-Level Touch

The low-level interface is represented basically by a single event, called FrameReported in the static class Touch. The class is an application-level service that processes multi-touch input from the operating system and fires the FrameReported event. You can handle this event to get all touch events for your application. The TouchFrameEventArgs has the following members:

  • Timestamp, a property representing an integer timestamp value of the event.
  • GetPrimaryTouchPoint(), returns the primary touch point for the reported frame
  • GetTouchPoints(), returns all the touch points in the reported frame
  • SuspendMousePromotionUntilTouchUp(), disables automatic mouse-event promotion for the primary touch point until all the touch points report as TouchAction.Up.

Methods GetPrimaryTouchPoint() and GetTouchPoints() return touch points either relative to particular element (passed as argument) or to the upper-left corner of the screen (when null is passed as argument). However, they don't limit the touch events for a particular element. You always get touch events for the entire application. The only thing you can do is calculating the Position property (see below) relatively to an UI element.

The TouchPoint class represents a single touch point from a multi-touch message source. It has the following members:

  • Action, identifies the type of action the touch point represents;
  • Position, the coordinates of the touch point
  • Size, the touch point contact area (no actual data is returned)
  • TouchDevice, represents the device that produced the touch point. The term device does not represent a physical device, but rather a UI element. The type TouchDevice has the following members:
    • ID, a unique identifier provided by the operating system for the touch device
    • DirectlyOver, the topmost UIElement object that the mouse is over at the time when the touch point was produced.

To demonstrate how you can handle the low-level touch information we'll write a simple application with a small white rectangle in the center of the main page. When you tap onto the rectangle it starts growing either until it reaches a maximum size, in which case its size is reset, but it continues to grow, until you release the finger/mouse.

First, let's see how the XAML code for the content panel looks like:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Rectangle x:Name="rectangle"
               Width="100" Height="100"
               Fill="White" />
</Grid>

What we have to do is:

  • Install a handler for the Touch.FrameReported event (in the constructor)
  • Start a timer that increases the size of the rectangle, until a maximum point when it is reset
  • In the handler get the primary touch point and if the DirectlyOver property indicates the rectangle start or stop the timer that inflates the rectangle.

The implementation is shown below.

public partial class MainPage : PhoneApplicationPage
{
  DispatcherTimer m_timer;
  const int OriginalSize = 100;
  
  public MainPage()
  {
     InitializeComponent();

     ResetSize();

     m_timer = new DispatcherTimer();
     m_timer.Interval = TimeSpan.FromMilliseconds(50);
     m_timer.Tick += (s, e) =>
     {
        rectangle.Width += 5;
        rectangle.Height += 5;
        if (rectangle.Width >= 4 * OriginalSize || rectangle.Height >= 4 * OriginalSize)
           ResetSize();
     };

     Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
  }

  void ResetSize()
  {
     rectangle.Width = OriginalSize;
     rectangle.Height = OriginalSize;
  }

  void Touch_FrameReported(object sender, TouchFrameEventArgs e)
  {
     TouchPoint ppoint = e.GetPrimaryTouchPoint(null);
     if (ppoint != null && ppoint.TouchDevice.DirectlyOver == rectangle)
     {
        switch(ppoint.Action)
        {
           case TouchAction.Down:
              m_timer.Start();
              break;
           case TouchAction.Up:
              m_timer.Stop();
              break;
        }
     }
  }
}

The following image shows the rectangle in its original size and then inflated after tapping and holding onto it for a while.

To complicate things a little bit we'll add support for double tap: if you tap twice on the rectangle in less than a given interval (let's say 250 milliseconds) we'll consider this a double tap, and in this case reset the size of the rectangle to its initial value. The changes in code are minimal: just add a DateTime member to the class (m_lastclick) and then if the Action is TouchAction.Down check the time elapsed since the last click; if the interval is less than 250 milliseconds reset the size, otherwise just start the timer.

case TouchAction.Down:
  {
     var now = DateTime.Now;
     var diff = now - m_lastclick;
     m_lastclick = now;
     if (diff.TotalMilliseconds < 250)
     {
        if (m_timer.IsEnabled)
           m_timer.Stop();
        ResetSize();
     }
     else
        m_timer.Start();
  }
  break;

Windows Phone 7 Quick Tutorials: Part 7 - Touch

High-Level Touch

The high-level interface is represented by three events specific to an UI element. These events are:

  • ManipulationStarted: occurs when an input device starts the manipulation of an element.
  • ManipulationDelta: occurs when the input device changes position during manipulation.
  • ManipulationCompleted: occurs when the manipulation of the UI element is complete.

You can install handlers for these events for any UI element, but you can also handle them at page level. The Control class (which is the base class for UI elements that use a ControlTemplate to define their appearance) from which PhoneApplicationPage is derived has protected virtual methods called when these events occur.

protected virtual void OnManipulationCompleted(ManipulationCompletedEventArgs e);
protected virtual void OnManipulationDelta(ManipulationDeltaEventArgs e);
protected virtual void OnManipulationStarted(ManipulationStartedEventArgs e);

In this later case, you have to check the original source of the event to distinguish between the controls that raised the events. It is also possible to use both handlers at the control level and page level. That means you can actually handle the same event twice, and even do different things. This is possible because the events are routed from the topmost element for which the event was fired, up the visual tree until the frame element, so anyone interested can handle the event. All the manipulation events derive from RoutedEventArgs type which contains a property called OriginalSource that indicates the element where the event began. However, you can prevent the event to travel up the visual tree by setting the Handled property that all the manipulation events have, to true. That indicates that the event has been handled and should not be routed up the tree.

To demonstrate the handling of manipulation events we'll create a simple implementation of the famous Pong game. There is a board with two paddles on the left and right sides which you can move up and down. A ball bounces from one side to the other and players must hit the ball with their paddle, otherwise the ball hits the wall and they lose.

The content panel for the main page will be a grid with three columns. The left and right column will contain a rectangle, which is the paddle. This rectangle will be moved up and down with the finger (or the mouse in the emulator). The middle column will contain a canvas with an ellipse that is the ball. We will install handlers for the three manipulation events for the two rectangles. This is how the XAML code for the content panel looks like.

<!--ContentPanel - place additional content here-->
<Border Grid.Row="1"  Margin="12,0,12,0"
        BorderThickness="1" 
        BorderBrush="White">
   <Grid x:Name="ContentPanel">
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="25" />
         <ColumnDefinition Width="*" />
         <ColumnDefinition Width="25" />
      </Grid.ColumnDefinitions>
      <Rectangle x:Name="paddleLeft"
                 Fill="White"
                 Width="25" Height="100" Margin="0,250,0,0"
                 VerticalAlignment="Top"
                 Grid.Column="0"
                 ManipulationStarted="paddleLeft_ManipulationStarted"
                 ManipulationDelta="paddleLeft_ManipulationDelta"
                 ManipulationCompleted="paddleLeft_ManipulationCompleted" />
      <Rectangle x:Name="paddleRight"
                 Fill="White"
                 Width="25" Height="100" Margin="0,250,0,0"
                 VerticalAlignment="Top" 
                 Grid.Column="2"
                 ManipulationStarted="paddleRight_ManipulationStarted"
                 ManipulationDelta="paddleRight_ManipulationDelta"
                 ManipulationCompleted="paddleRight_ManipulationCompleted"/>
      <Canvas x:Name="board"                 
              Grid.Column="1">
         <Ellipse x:Name="ball" 
                  Fill="Red"
                  Width="20" Height="20"/>
      </Canvas>
   </Grid>
</Border>

We won't do too many things in the handlers, just some simple actions:

  • When the manipulation starts, change the fill color of the rectangle, just to show the player holds his finger (or the mouse with the emulator) on the paddle.
  • When the manipulation completed, change the fill color or the rectangle back to the original white to show the player released the paddle.
  • While manipulation proceeds, changed the Top value of the Margin property of the rectangle with the amount defined in the DeltaManipulation.Translation.Y property of the event arguments.

The following code shows the handlers for the left rectangle; the handlers for the right rectangle are identical. cyanBrush and whiteBrush are two class fields of type Brush. m_running indicates that a game is in progress and is used to prevent the user moving the paddles if the game is not running.

private void paddleLeft_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
   if (m_running)
   {
      paddleLeft.Fill = cyanBrush;
   }
}

private void paddleLeft_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
   if (m_running)
   {
      var newtop = paddleLeft.Margin.Top + e.DeltaManipulation.Translation.Y;
      if (newtop > 0 && newtop + paddleLeft.ActualHeight < ContentPanel.ActualHeight)
         paddleLeft.Margin = new Thickness(0, newtop, 0, 0);
   }
}

private void paddleLeft_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
   if (m_running)
   {
      paddleLeft.Fill = whiteBrush;
   }
}

The rest of the application is not important for the manipulation topic and will not be presented here, but you can check the WP7Manipulation project provided with this article and see how the very simple game logic was implemented. The following image shows the application in the emulator when it starts and while a game is in progress. The cyan color of the left paddle in the second image indicates that the player is holding and moving the paddle (with the mouse, since the screen shot was made with the emulator).

[wp7touch_2.jpg]

Windows Phone 7 Quick Tutorials: Part 7 - Touch

Higher-Level Touch

A higher-level support for multi-touch gestures is provided with the Silverlight for Windows Phone Toolkit available for download at codeplex at http://silverlight.codeplex.com. This toolkit contains controls you can use in your WP7 Silverlight applications such as WrapPanel, DatePicker and TimePicker, AutoCompleteBox and others. In addition it contains a GestureListener that provides support to detect gestures in your applications. The following gestures are supported:

  • Tap
  • Double-tap
  • Hold
  • Flick
  • Drag; three events can be used to implement dragging: DragStarted, DragDelta and DragCompleted
  • Pinch; three events to implement pinching (for instance zooming in or out): PinchStarted, PinchDelta, PinchCompleted

To use the toolkit you first have to download and install it and then add a reference to the Microsoft.Phone.Controls.Toolkit assembly to your project.

The following demo shows how to handle some of the events for these gestures. The content of the main page is represented by a stack panel with a border that encapsulates a text block and a list. Each time a gesture event happens on the border element, a text identifying the gesture is added to the list. The gesture handlers will be installed for the border (used only to act as a background to the TextBlock and highlight the area where you can exercise the gestures).

First, we must add the following namespace to the PhoneApplicationPage definition.

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 

Here is the main part of the XAML code:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <StackPanel>
    <Border Background="White" 
            Height="75">
       <toolkit:GestureService.GestureListener>
          <toolkit:GestureListener Tap="GestureListener_Tap"
                                   DoubleTap="GestureListener_DoubleTap"
                                   Hold="GestureListener_Hold"
                                   Flick="GestureListener_Flick" />
       </toolkit:GestureService.GestureListener>
       <TextBlock x:Name="txtTouch"
                  Text="Touch here"
                  Foreground="Black"                          
                  TextAlignment="Center" VerticalAlignment="Center"
                  Style="{StaticResource PhoneTextTitle2Style}"/>
    </Border>
    <ListBox x:Name="listEvents" />
 </StackPanel>
</Grid>

The implementation for the handlers is pretty straight-forward.

private void GestureListener_Tap(object sender, GestureEventArgs e)
{
   listEvents.Items.Add("tap");
}

private void GestureListener_DoubleTap(object sender, GestureEventArgs e)
{
   listEvents.Items.Add("double tap");
}

private void GestureListener_Hold(object sender, GestureEventArgs e)
{
   listEvents.Items.Add("hold");
}

private void GestureListener_Flick(object sender, FlickGestureEventArgs e)
{
   listEvents.Items.Add("flick");
}

The following image shows the application. Run it and exercise the gestures for yourself.

[wp7touch_3.jpg]

Conclusions

Touch is an important part of the WP7 applications. Users interact with the device with the finger and the system supports multi-touch events with at least four fingers. There are different levels of support for the touch events and in this article we've seen how we can handle them. You can use the one that you find most appropriate for your needs.



About the Author

Marius Bancila

Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • As businesses have grown in size and global reach, emerging technologies such as cloud computing and virtualization have appeared to help companies effectively connect and grow. However, the networking strategies and infrastructures that keep organizations connected have often remained in the past. Now, new strategies that leverage global connectivity and locations exist to provide a more flexible and cost-effective alternative to traditional networking systems. This Aberdeen report analyzes how top performing …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds