#739 – Handling Touch Input at Different Levels

In WPF, there are three different ways that your application can support touch input:

  • Built-in support for touch.  Some elements will automatically respond to touch input.  For example, you can trigger the Click event for a button by touching the button or scroll a ListBox by touching and dragging.
  • Manipulation Events.  User interface elements support a series of manipulation events that let you detect when the user is trying to rotate, scale (zoom) or translate (move) an element.  The touch points from two fingers are automatically mapped to an event with the correct data.  For example, spreading two fingers apart triggers an event that knows you want to zoom in.
  • Raw Touch Events.  You can handle individual events for touch down, up and move actions on an element, for all supported touch points.  For example, you can track the location of 10 fingers touching the screen at the same time.
Advertisement

#738 – Sample Code – Drawing and Moving Circles at Touch Points

Here’s some sample code that draws a circle for each touch point, when a finger contacts the screen, and then moves that circle around as you move your finger.  This is done using the raw touch events–TouchDown, TouchMove and TouchUp.

    <Canvas Name="canvMain" Background="Transparent"
        TouchDown="Canvas_TouchDown" TouchUp="Canvas_TouchUp" TouchMove="Canvas_TouchMove"/>

 

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

        private const double CircleWidth = 55;
        private Dictionary<int, Point> TouchPositions;
        private Dictionary<int, Ellipse> TouchEllipses;

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

            TouchPoint tp = e.GetTouchPoint(null);

            Ellipse el = AddEllipseAt(canvMain, tp.Position, Brushes.Red);

            TouchPositions.Add(e.TouchDevice.Id, tp.Position);
            TouchEllipses.Add(e.TouchDevice.Id, el);
            e.Handled = true;
        }

        private void Canvas_TouchMove(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            Canvas.SetLeft(TouchEllipses[e.TouchDevice.Id], tp.Position.X - (CircleWidth / 2));
            Canvas.SetTop(TouchEllipses[e.TouchDevice.Id], tp.Position.Y - (CircleWidth / 2));
            e.Handled = true;
        }

        private void Canvas_TouchUp(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            TouchPositions.Remove(e.TouchDevice.Id);

            canvMain.Children.Remove(TouchEllipses[e.TouchDevice.Id]);
            TouchEllipses.Remove(e.TouchDevice.Id);

            canvMain.ReleaseTouchCapture(e.TouchDevice);
            e.Handled = true;
        }

        private Ellipse AddEllipseAt(Canvas canv, Point pt, Brush brush)
        {
            Ellipse el = new Ellipse();
            el.Stroke = brush;
            el.Fill = brush;
            el.Width = CircleWidth;
            el.Height = CircleWidth;

            Canvas.SetLeft(el, pt.X - (CircleWidth / 2));
            Canvas.SetTop(el, pt.Y - (CircleWidth / 2));

            canv.Children.Add(el);

            return el;
        }

    }

738-001

#737 – Touch Behavior when Maximum Number of Touch Points Reached

When you already have the maximum number of touch points engaged as input devices, additional touches on the device will be ignored.

For example, assume that your hardware supports a maximum of two touch points and you’re already touching the screen with two fingers.  If you place a third finger on the screen, you will not get a TouchDown for that third finger.  However, if you leave all three fingers on the screen and then lift a finger up, the first finger lifted up will not see a TouchUp event.  The behavior can be summarized as:

  • If you’ve already reached maximum number of touch points, adding fingers will not result in TouchDown events
  • If you currently have more fingers touching the screen than the maximum number of touch points, lifting  a finger will not result in a TouchUp event until you’re back down to the maximum number of touch points

#736 – Finding the Maximum Number of Touch Points at Run-time

You can write code that discovers at run-time the number of touch points supported by the hardware that you’re running on.  You do this by calling the GetSystemMetrics Win32 API call.

    class Program
    {
        [DllImport("user32.dll")]
        static extern int GetSystemMetrics(int nIndex);

        // Index passed in to GetSystemMetrics() indicates
        // what data we're asking for.
        private const int SM_DIGITIZER = 94;
        private const int SM_MAXIMUMTOUCHES = 95;

        // Masks used to check results from SM_DIGITIZER check
        private const int NID_READY = 0x80;
        private const int NID_MULTI_INPUT = 0x40;

        static void Main(string[] args)
        {
            string info;

            int digitizer = GetSystemMetrics(SM_DIGITIZER);

            if ((digitizer & (NID_READY + NID_MULTI_INPUT)) == NID_READY + NID_MULTI_INPUT)
            {
                int numTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES);
                info = string.Format("Multitouch ready, {0} inputs supported", numTouchPoints);
            }
            else
                info = "Multitouch not supported";

            Console.WriteLine(info);
            Console.ReadLine();
        }
    }

736-001

#735 – System Applet Indicates Maximum Number of Touch Points

The number of simultaneous touch points depends on the particular touch hardware that you’re using.  To quickly check what is supported on the machine that you’re using, bring up the System applet in Control Panel.  (Control Panel | System and Security | System, or just type “System” in Windows 7 or Windows 8 search.

The maximum number of simultaneous touch points is listed in the middle of the window, labeled Pen and Touch.

735-001

#734 – Recognizing Different Fingers in Touch Event Handlers

When you’re handling low-level touch events in WPF and the user will be using more than one finger at a time on the screen, you’ll want to keep track of which finger is generating a particular touch event.  You can do this using the TouchEventArgs.TouchDevice.Id property.  Every touch event handler will report a different Id for each finger that is touching the screen.  Also, when you touch and drag a finger on the screen, the Id property will remain the same for all events associated with that finger.

Here’s an example.

        private const double CircleWidth = 10;
        private Dictionary<int, Point> LastPositionDict;

        private void Canvas_TouchDown(object sender, TouchEventArgs e)
        {
            try
            {
                TouchPoint tp = e.GetTouchPoint(null);

                AddEllipseAt(canvMain, tp.Position, Brushes.Red);

                LastPositionDict.Add(e.TouchDevice.Id, tp.Position);
            }
            catch (Exception xx)
            {
                MessageBox.Show(xx.ToString());
            }
        }

        private void Canvas_TouchMove(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            AddLineFromTo(canvMain, LastPositionDict[e.TouchDevice.Id], tp.Position, Brushes.Black);
            LastPositionDict[e.TouchDevice.Id] = tp.Position;
        }

        private void Canvas_TouchUp(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            AddEllipseAt(canvMain, tp.Position, Brushes.Blue);
            LastPositionDict.Remove(e.TouchDevice.Id);
        }

Now I can draw with two fingers at the same time:
734-001

#733 – A Full List of Touch Related Events

Here’s a full list of UIElement events that you can handle when you want to handle touch input.  All of the events listed below are also defined for ContentElement.

All events are bubbling, unless flagged as tunneling.

Raw touch events:

  • GotTouchCapture – element has captured touch input
  • LostTouchCapture – element has lost touch capture
  • PreviewTouchDown – finger touches element  (tunneling)
  • PreviewTouchMove – finger moving on screen  (tunneling)
  • PreviewTouchUp – finger lifts off screen after moving  (tunneling)
  • TouchDown – finger touches element
  • TouchEnter – finger moves into element from outside
  • TouchLeave – finger moves out of element
  • TouchMove – finger moving on screen
  • TouchUp – finger lifts off screen after moving

Events related to manipulation (gestures):

  • ManipulationBoundaryFeedback – manipulation enters boundary
  • ManipulationCompleted – manipulation on element finishes
  • ManipulationDelta – position changes during manipulation
  • ManipulationInertiaStarting – finger leaves screen during manipulation
  • ManipulationStarted – manipulation on element starts
  • ManipulationStarting – user puts finger on element

#732 – Basic Events for Raw Touch Input

WPF includes a set of events for handling raw touch input.  These events are defined for all UIElement, ContentElement, and UIElement3D objects.

The most basic events are:

  • TouchDown – User touches the screen
  • TouchMove – User moves finger across the screen
  • TouchUp – User lifts finger off the screen

Below is a simple example that allows drawing using touch.  We’ve defined event handlers and attached them to a main Canvas element.  A red circle is drawn at the TouchDown point and a blue circle at the TouchUp point.  A continuous line is drawn as the user moves their finger across the screen.

        private const double CircleWidth = 10;
        private Point LastPosition;

        private void Canvas_TouchDown(object sender, TouchEventArgs e)
        {
            try
            {
                TouchPoint tp = e.GetTouchPoint(null);

                AddEllipseAt(canvMain, tp.Position, Brushes.Red);
                LastPosition = tp.Position;
            }
            catch (Exception xx)
            {
                MessageBox.Show(xx.ToString());
            }
        }

        private void Canvas_TouchMove(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            AddLineFromTo(canvMain, LastPosition, tp.Position, Brushes.Black);
            LastPosition = tp.Position;
        }

        private void Canvas_TouchUp(object sender, TouchEventArgs e)
        {
            TouchPoint tp = e.GetTouchPoint(null);

            AddEllipseAt(canvMain, tp.Position, Brushes.Blue);
        }

        private void AddEllipseAt(Canvas canv, Point pt, Brush brush)
        {
            Ellipse el = new Ellipse();
            el.Stroke = brush;
            el.Fill = brush;
            el.Width = CircleWidth;
            el.Height = CircleWidth;

            Canvas.SetLeft(el, pt.X - (CircleWidth / 2));
            Canvas.SetTop(el, pt.Y - (CircleWidth / 2));

            canv.Children.Add(el);
        }

        private void AddLineFromTo(Canvas canv, Point from, Point to, Brush brush)
        {
            Line l = new Line();
            l.Stroke = brush;
            l.X1 = from.X;
            l.Y1 = from.Y;
            l.X2 = to.X;
            l.Y2 = to.Y;
            l.StrokeThickness = 2;

            canv.Children.Add(l);
        }

So when I touch and drag on a touch-enabled device, I get something that looks like this:

732-BasicTouch

#731 – The Idea of Multi-Touch

Touch input is the idea of using your finger as an input device by touching a screen.  Multi-touch means that you can touch the screen with more than one finger, with each finger touching a different spot on the screen.

Multit-touch is typically used to track gestures that the user performs with more than one finger.  For example, placing two fingers on the screen and then spreading the fingers apart is interpreted as a “zoom in” gesture.  Moving the two fingers together is intepreted as a “zoom out” gesture.  And placing two fingers on the screen and rotating them both is interpreted as a “rotate” gesture.

731-001731-002731-003

Windows 7 and Windows 8 both include support for multi-touch input.

 

#730 – Use QueryContinueDrag Event to Know When Mouse Button State Changes

The QueryContinueDrag event lets you know when a mouse button changes during a drag-drop operation.  It will also indicate whether the state of the Shift, Ctrl, or Alt keys change while dragging.  You wire up an event handler to the control that the drag-drop operation originates from.

In the example below, the source control waits for the left mouse button to be released and then cleans out its content.

        <Label Content="Drag from here" Background="LavenderBlush"
               HorizontalAlignment="Center" Margin="10" Padding="10"
               MouseDown="Label1_MouseDown"
               QueryContinueDrag="Label1_QueryContinueDrag"/>
        <Label Content="To here" Background="SandyBrown" AllowDrop="True"
               HorizontalAlignment="Center" Margin="10" Padding="10"
               Drop="Label2_Drop"/>
        private void Label1_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label lblFrom = e.Source as Label;

            if (e.LeftButton == MouseButtonState.Pressed)
                DragDrop.DoDragDrop(lblFrom, lblFrom.Content, DragDropEffects.Copy);
        }

        private void Label1_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            Label lblFrom = e.Source as Label;

            if (!e.KeyStates.HasFlag(DragDropKeyStates.LeftMouseButton))
                lblFrom.Content = "...";
        }

        private void Label2_Drop(object sender, DragEventArgs e)
        {
            string draggedText = (string)e.Data.GetData(DataFormats.StringFormat);

            Label toLabel = e.Source as Label;
            toLabel.Content = draggedText;
        }

730-001
730-002