Simple Gesture Sample

Simple Gesture Sample

  • Comments 6

This blog post is a simple sample to get started with basic manipulation. It shows you how to move a little circle around the screen using drags and flicks. In order to keep the code simple, it doesn't do anything complicated (like bounce off the sides) but you could add that if you liked.

First a quick introduction. Direct manipulation consists of three distinct phases

  1. ManipulationStarted. This event occurs when the user starts a direct manipulation by placing their finger(s) on the screen
  2. ManipulationDelta. This event occurs repeatedly while the user is moving their finger(s) on the screen
  3. ManipulationCompleted. This occurs when the user removes their finger(s) from the screen

For simple single-finger gestures these are roughly equivalent to MouseDown, MouseMove, and MouseUp, but manipulation is more useful for several reasons:

  1. Manipulation events gives you useful properties like Translation that make it very easy to transform elements to match the manipulation (as if you were "directly manipulating" a real-world object – hence the name)
  2. Manipulation can handle simple two-finger gestures, such as pinch / stretch (although the only way to do that on the emulator right now is if you have a supported touch-screen monitor)
  3. Manipulation exposes the notion of inertia which enables you to do things like "flick" objects or apply other life-like physics to them

Given this quick introduction, here's how the simple sample works. First we have an ellipse defined in XAML:

<Ellipse x:Name="ball"
         Fill="{StaticResource PhoneAccentBrush}" 
         CacheMode="BitmapCache" 
         Width="50" Height="50" 
         ManipulationStarted="OnManipulationStarted" 
         ManipulationCompleted="OnManipulationCompleted" 
         ManipulationDelta="OnManipulationDelta">

  <Ellipse.RenderTransform> 
    <TranslateTransform x:Name="ballTranslate"/> 
  </Ellipse.RenderTransform>

</Ellipse>

Nothing special here; it's just an ellipse with an empty TranslateTransform applied. Note that we give the transform a name so we can animate it directly (see note about this later). Also note that we use the CacheMode property to avoid drawing the ellipse constantly as we move it about the screen.

We also define a couple of animations that will provide the "coasting" effect when we flick the ball:

<Storyboard x:Name="CoastBall" Completed="CoastBallCompleted">

  <DoubleAnimationUsingKeyFrames 
    Storyboard.TargetName="ballTranslate" 
    Storyboard.TargetProperty="X"> 
    <EasingDoubleKeyFrame x:Name="coastX" 
                          KeyTime="00:00:00.75" Value="0"> 
      <EasingDoubleKeyFrame.EasingFunction> 
        <CircleEase EasingMode="EaseOut"/> 
      </EasingDoubleKeyFrame.EasingFunction> 
    </EasingDoubleKeyFrame> 
  </DoubleAnimationUsingKeyFrames>

  <DoubleAnimationUsingKeyFrames 
    Storyboard.TargetName="ballTranslate" 
    Storyboard.TargetProperty="Y"> 
    <EasingDoubleKeyFrame x:Name="coastY" 
                          KeyTime="00:00:00.75" Value="0"> 
      <EasingDoubleKeyFrame.EasingFunction> 
        <CircleEase EasingMode="EaseOut"/> 
      </EasingDoubleKeyFrame.EasingFunction> 
    </EasingDoubleKeyFrame> 
  </DoubleAnimationUsingKeyFrames>

</Storyboard>

Again, nothing special here; they're just animations on the X and Y properties of the afore-mentioned transform. Note that we don't specify a Value for the animations, since we're going to compute them at runtime. We add a simple CircleEase to give a nice effect to the animation (looks like it is slowing down due to friction).

A quick tip here – I wasted several hours coding up this example because initially I tried to animate the ball using a more complex property path; something like:

<DoubleAnimationUsingKeyFrames 
  Storyboard.TargetName="ball" 
  Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)">

but this led to pain and suffering... take my advice and don't do that ;-).

Now for the code, it's pretty basic. In the ManipulationStarted event, we just change the colour of the ball:

private void OnManipulationStarted(object sender, ManipulationStartedEventArgs e)

  e.Handled = true
  ball.Fill = Application.Current.Resources["PhoneForegroundBrush"] as SolidColorBrush;
}

Then in the ManipulationDelta, we update the transform to match the manipulation. Note there is no need to transform mouse coordinates into element coordinates or any of the stuff you'd normally do for mouse-based animation:

private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)

  e.Handled = true
  ballTranslate.X += e.DeltaManipulation.Translation.X; 
  ballTranslate.Y += e.DeltaManipulation.Translation.Y;
}

Finally, in the ManipulationCompleted event, we check to see if there is any inertia (ie, the user's finger was moving when it left the screen). If so, we do some really cheap math to figure out a likely landing point for the ball, and kick off the animation. If not, we just reset the ball's colour.

private void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)

  e.Handled = true
  if (e.IsInertial) 
  { 
    CoastBall.Stop();

    // Rough guess of final location 
    finalX = ballTranslate.X + e.FinalVelocities.LinearVelocity.X / 4; 
    finalY = ballTranslate.Y + e.FinalVelocities.LinearVelocity.Y / 4;

    // Bound it to the visible area 
    double maxWidth = (LayoutRoot.ActualWidth - ball.ActualWidth) / 2; 
    double maxHeight = (LayoutRoot.ActualHeight - ball.ActualHeight) / 2;

    // Simple helper function to limit the value 
    finalX = LimitValue(finalX, -maxWidth, maxWidth); 
    finalY = LimitValue(finalY, -maxHeight, maxHeight);

    // Set values on the animations 
    coastX.Value = finalX; 
    coastY.Value = finalY;

    CoastBall.Begin(); 
  } 
  else 
  { 
    ball.Fill = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush
  }
}

One last thing is that we have an event handler for when the coast animation completes, which is use to update the actual value of the transform to the final values, and to reset the colour of the ball:

private void CoastBallCompleted(object sender, EventArgs e)

  CoastBall.Stop(); 
  ball.Fill = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush
  ballTranslate.X = finalX; 
  ballTranslate.Y = finalY;
}

And that's it! The finished project is attached; have fun.

Attachment: GestureTest.zip
  • I when to get Multitouch point in wp7 silverlight.

    i use ManipulationDelta, but can't get Multi point, just can get one.

    Can i get two or three point in wp7 silverlight?

    Because Touch.FrameReported obsolete. i can't use it.

    so, is possible can get Multitouch point in wp7 silverlight?

    Thanks.

    -William in TAIWAN

  • Currently Silverlight only supports single-touch gestures and pinch-zoom gestures; there are no arbitrary multi-touch events today (and the Windows Phone built-in apps do not use two-finger gestures other than zoom). Do you have examples of what you woud like to do with 2- or 3-finger gestures?

    Note that XNA supports up to 4-point multi-touch for gaming scenarios.

  • I have a multi touch screen..... So how do you do the pinch zoom?

  • That will have to be another post... but basically look for the CumulativeManipulation.Scale property on ManipulationDelta event args and you should have a good starting point.

  • Sweet. That's all I needed (didn't realize that's what that property was for). Will take it from there :)

  • I wrote an article about the Bing maps control with WP7 and from what I found is that if you hold down CTRL and flick the mouse it does zoom.  So it looks that like Map control at least supports multi-touch (could be wrong).  But it does make sense..in a tablet you want to pinch and expand fingers for zooming in and out.

    http://silverlighthack.com/post/2010/03/21/Using-the-Silverlight-Bing-Maps-control-on-the-Windows-Phone-7.aspx

Page 1 of 1 (6 items)