Xamarin: how to get cursor/touch coordinates (position X and Y)?

Quentame picture Quentame · Apr 3, 2017 · Viewed 8k times · Source

Introduction

I am creating my first Xamarin app (that will target UWP first, then Android, finally maybe iOS).

Basically, the app should detect multiple fingers and circles will pop over each finger and follow them.

My app

First I thought that UI and graphics couldn't be coded with Xamarin so I started UWP code :

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Input;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Input;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
using Windows.UI.Input;
using Windows.UI.Xaml.Shapes;
using Windows.UI;

namespace App1.UWP
{
    public sealed partial class MainPage
    {
        public MainPage()
        {
            this.InitializeComponent();

            LoadApplication(new App1.App());
        }

        private async void OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            Debug.WriteLine(e.Pointer.PointerDeviceType + " " + sender + " " + e);

            Point pointerPos = e.GetCurrentPoint(rootPanel).Position;

            Debug.WriteLine(pointerPos.X + " " + pointerPos.Y);

            Ellipse ellipse1 = new Ellipse();
            ellipse1.Fill = new SolidColorBrush(Colors.SteelBlue);
            ellipse1.Width = 100;
            ellipse1.Height = 100;
            //ellipse1.Margin.Left = pointerPos.X + 50;
            //ellipse1.Margin.Top = pointerPos.Y + 50;

            rootPanel.Children.Add(ellipse1);
            Debug.WriteLine(rootPanel.Children.Count);

            switch (e.Pointer.PointerDeviceType)
            {
                case PointerDeviceType.Touch:
                    break;
                case PointerDeviceType.Pen:
                    break;
                case PointerDeviceType.Mouse:

                    ContentDialog noMouseAvaliableDialog = new ContentDialog()
                    {
                        Title = "Erreur de méthode d'entrée",
                        Content = "Il est impossible de savoir qui va jouer en premier aver un joueur :(\n Veuillez essayer avec le clavier.",
                        PrimaryButtonText = "OK chef"
                    };

                    await noMouseAvaliableDialog.ShowAsync();
                    break;
                default:
                    break;
            }

            Debug.WriteLine("-------------------------");
        }
    }
}

MainPage.xaml

<forms:WindowsPage
    x:Class="App1.UWP.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:forms="using:Xamarin.Forms.Platform.UWP"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1.UWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    PointerPressed="OnPointerPressed">

    <RelativePanel
        Name="rootPanel"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch">

    </RelativePanel>
</forms:WindowsPage>

As you can see, I can get the X and Y position of the cursor, BUT my ellipse doesn't display. So I noticed that Xamarin xaml won instead of my UWP xaml.

So I tried again with Xamarin. I looked for shared code graphics API, I found SkiaSharp. And my ellipse appear in the Xamarin code :

MainPage.xaml.cs

using SkiaSharp;
using SkiaSharp.Views.Forms;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace App1
{
    public partial class MainPage : ContentPage
    {
        private static SKCanvas canvas;

        public MainPage()
        {
            InitializeComponent();
        }

        private void OnPainting(object sender, SKPaintSurfaceEventArgs e)
        {
            canvas = e.Surface.Canvas;
        }

        private void onTapGestureRecognizerTapped(object sender, EventArgs e)
        {
            Debug.WriteLine("Tapped !!!!!");
            //((TappedEventArgs) e).
        }

        /*private void onPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            Debug.WriteLine("Panned !!!!!" + e.TotalX + " " + e.TotalY);
        }*/

        internal void drawCircleOnCanvas(Point pointerPos, int radius)
        {
            Debug.WriteLine(pointerPos.X + " " + pointerPos.Y);

            SKPaint circleFill = new SKPaint
            {
                IsAntialias = true,
                Style = SKPaintStyle.Fill,
                Color = SKColors.AliceBlue
            };
            canvas.DrawCircle(((float) pointerPos.X - 50), ((float) pointerPos.Y - 50), radius, circleFill);
        }
    }
}

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App1"
             x:Class="App1.MainPage"
             xmlns:views="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms">

    <Label Text="Ooooh, you touch my tralala" 
           VerticalOptions="Center" 
           HorizontalOptions="Center" />

    <!--<relativelayout
        x:Name="rootPanel" 
        backgroundcolor="lime"
        horizontaloptions="fill"
        verticaloptions="fill">

    </relativelayout>-->

    <Grid>
        <views:SKCanvasView PaintSurface="OnPainting"/>
        <Grid.GestureRecognizers>
            <TapGestureRecognizer
                Tapped="onTapGestureRecognizerTapped"
                NumberOfTapsRequired="1"/>
            <!--<PanGestureRecognizer PanUpdated="onPanUpdated"/> -->
        </Grid.GestureRecognizers>
    </Grid>

</ContentPage>

Finally, it rests only the X and Y position for each touched point to draw my ellipses on. As you can see, I looked different ways: TapGestureRecognizer, PanGestureRecognizer ...

But I couldn't get those coordonates.

I found MR.Gestures API, but it's licensed per app name.

Questions

So, I am asking experimented people :

  • Do Xamarin provides touch position/gesture API ?
  • Do MR.Gestures is the best solution to handle touch ?
  • Is there other touch shared code APIs ?

Or simply, how can I get the coordonates of all touched points with Xamarin ?

Thanks

PS: I find this article while writting this Stack, reading ...

Answer

TaiT&#39;s picture TaiT's · Apr 3, 2017

Do Xamarin provides touch position/gesture API ?

Unfortunatly, no! The only thing you can do with Xamarin is using the TapGestureRecognizer, PinchGestureRecognizer or PanGestureRecognizer class, as you mentionned, but that's all. None of them will give you x/y coordinates.

Do MR.Gestures is the best solution to handle touch ?

I came across a same type of issue. If you don't want to reinvent the wheel, you should definitely go for an external API.

Mr.Gestures does the job pretty good. Unfortunatly, I haven't heard of any free API. As you mentionned, it's per app licenced :(

Or simply, how can I get the coordonates of all touched points with Xamarin ?

You should implement custom renderers for each platform (or UWP in your case). That's what Mr.Gestures does for you behind the scenes. If you don't want to implement this yourself, currently your only option is to use an external API.

Maybe one day Xamarin will provide this kind of API out of the box... Let's keep finger crossed!

Hope this helps!