#752 – Tracking Total Scale when Scaling by Touch Manipulation

When you use touch manipulation events to scale an element, you typically read the Scale property of the ManipulationDelta object passed in to the ManipulationDelta event handler.   This property reports a delta scaling value to apply to the element, derived from the user’s touch manipulation (e.g. pinch/expand).

For example, a scale value of 1.05 says “scale the object 5% larger than it was the last time that this event was fired”.

In the code example below, we also track total scale, relative to the original size of the element.  (Note that we don’t actually scale the element here).

        private Vector totalScale = new Vector(1.0, 1.0);

        private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            ManipulationDelta md = e.DeltaManipulation;
            Vector scale = md.Scale;

            totalScale.X *= scale.X;
            totalScale.Y *= scale.Y;

            Console.WriteLine(string.Format(
                "Scale: X={0}, Y={1}, TotalScale: X={2}, Y={3}",
                scale.X, scale.Y, totalScale.X, totalScale.Y));

            e.Handled = true;
        }

752-001

#751 – Indicating which Touch Manipulation Modes You Support

When you support touch manipulation events, you can choose which types of touch manipulation to support for an element.  The modes include:

  • Translation (in X, Y, or both)
  • Rotation
  • Scaling

You indicate which modes you want to support within the ManipulationStarting event handler, setting the ManipulationStartingEventArgs.Mode property.  You can set this property to some combination of :

  • ManipulationModes.None
  • ManipulationModes.TranslateX
  • ManipulationModes.TranslateY
  • ManipulationModes.Translate
  • ManipulationModes.Rotate
  • ManipulationModes.Scale
  • ManipulationModes.All

When you enable a mode, the ManipulationDelta event will fire when a user manipulates the element using touch, and will include data for that style of manipulation.  When a manipulation mode isn’t supported, the ManipulationDelta event will not fire for that mode.  It may fire for other manipulation actions, but will not include data for modes that are disabled.

The mode values listed above can be combined using the OR (|) operator.

            // Support translation and scaling
            e.Mode = ManipulationModes.Translate | ManipulationModes.Scale;

#750 – Using Touch Manipulation to Translate in Just One Dimension

You can set the IsManipulationEnabled property and handle the ManipulationDelta event for an element, to support translating (moving) the element using touch.

By default, when you read the ManipulationDelta.Translation property, it will contain translation values for both X and Y.  If you want to allow moving the element only horizontally or vertically, you can handle the ManipulationStarting event and set the ManipulationStartingEventArgs.Mode property to either TranslateX or TranslateY.

The example below shows how we could limit translation to be only horizontal.  You could do the same thing by ignoring the Y component of the Translation property in the ManipulationDelta event handler.

        private void Image_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {
            // Ask for manipulations to be reported relative to the canvas
            e.ManipulationContainer = canvMain;

            // Allow only horizontal translation
            e.Mode = ManipulationModes.TranslateX;
        }

#749 – Handling the TouchEnter and TouchLeave Events

When handling the raw touch events, if you touch a user interface element and then lift your finger off the element, you’ll see the events:

  • TouchDown
  • TouchUp

These two events do not capture, however, cases when you slide your finger onto or off of the element.  For this situation, you can  handle the TouchEnter and TouchLeave events, which fire when a touch contact enters or leaves the boundaries of the element.

If you then touch an element and then lift your finger off the element, you’ll see:

  • TouchEnter
  • TouchDown
  • TouchUp
  • TouchLeave

If you slide your finger onto the element and then lift your finger off the element, you’ll see:

  • TouchEnter
  • TouchUp
  • TouchLeave

Touching an element and then sliding off it leads to:

  • TouchEnter
  • TouchDown
  • TouchLeave

Finally, sliding onto and then off an element leads to:

  • TouchEnter
  • TouchLeave

#748 – Getting the Size of a Contact Point during Raw Touch

In the handlers for the various raw touch events, you can get information about the size of the actual touch contact (the area where your finger is touching the screen).  You get the size from the TouchPoint.Bounds property, which contains the touch position and its size.

Note that the touch contact will not have a non-zero size on every device.  In some cases, the size may be reported if this feature is not supported.

Here’s an example of drawing an ellipse at a size based on the size of the touch contact.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            TouchEllipses = new Dictionary<int, Ellipse>();
        }

        private Dictionary<int, Ellipse> TouchEllipses;

        private void Canvas_TouchDown(object sender, TouchEventArgs e)
        {
            canvMain.CaptureTouch(e.TouchDevice);
            TouchPoint tp = e.GetTouchPoint(canvMain);

            Ellipse el = new Ellipse();
            el.Stroke = Brushes.Black;
            el.Fill = Brushes.Black;

            el.Width = tp.Bounds.Width > 0 ? tp.Bounds.Width : 50;
            el.Height = tp.Bounds.Height > 0 ? tp.Bounds.Height : 50;

            Canvas.SetLeft(el, tp.Position.X - (el.Width / 2));
            Canvas.SetTop(el, tp.Position.Y - (el.Height / 2));

            canvMain.Children.Add(el);
            TouchEllipses.Add(e.TouchDevice.Id, el);

            e.Handled = true;
        }

        private void Canvas_TouchUp(object sender, TouchEventArgs e)
        {
            canvMain.Children.Remove(TouchEllipses[e.TouchDevice.Id]);
            TouchEllipses.Remove(e.TouchDevice.Id);

            e.Handled = true;
        }
    }

#747 – Implementing Inertia during Touch Manipulation

We’ve talked about how to calculate a value for inertial deceleration.  Once you know what deceleration value that you want, you can implement inertia during touch manipulation by handling the ManipulationInertiaStarting event.

The ManipulationInertiaStarting event will fire after the user lifts their finger off of the screen.  In the event handler, if you specify a deceleration value, the inertia will be modeled and the object will continue to move after the user lifts their finger.

The example below specifies a deceleration value of 40 in/sec^2.  It also dumps out the initial velocity of the object being translated, for informational purposes.

Note that we are setting up inertia for translation only.  We could also specify different deceleration values for rotation and scaling to get touch-based inertia while rotating or scaling.

        private void Image_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
        {
            e.TranslationBehavior.DesiredDeceleration = 40.0 * 96.0 / (1000.0 * 1000.0);
            Trace.WriteLine(e.TranslationBehavior.InitialVelocity);
        }

#746 – Specifying Inertial Deceleration

In WPF, you can use inertia so that objects will continue moving on the screen after you lift your finger off the screen.

Calculation of inertial behavior requires both an initial velocity and a deceleration.  WPF knows the initial velocity of an object, based on how fast you are moving it on the screen.  The deceleration value is something that you specify.

Initial velocity values are typically in the range of around 0-4 DIPs/ms (DIPs per millisecond), or 0-42 in/sec.

Deceleration is expressed in DIPs/ms^2 (DIPs per millisecond squared).  If we want to decelerate to 0 within about 1/2 sec, we can use values in the range of  0-0.008 DIPs/ms^2.  (4 / 500).  This is equivalent to 83 ft/sec^2.

If you start with a deceleration value in in/sec^2, you can convert to DIPs/ms^2 using the formula:

x’ = x * 96 / (1000 * 1000)

You can experiment with different deceleration values to get the exact deceleration behavior that you want in your application.