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;

More by Author

Must Read