Search code examples
c#xamarinuwpgesturexamarin.uwp

How do I implement LongPress in a Xamarin.UWP application?


I am trying to implement a custom PlatformEffect that will be execute when the user holds down a Control for a long time for my Xamarin.UWP. However I found that my application does not respond to mouse clicks. I read that I need to change the GestureSettings for the application according to the Remarks here to fix this. Problem is I don't know how I can do that for a Xamarin.UWP application, any ideas would be very helpful.


Solution

  • How do I change the GestureSettings for a Xamarin.UWP application?

    Derive from official document , Touch can produce a Holding action, but mouse devices generally can't. So, if you want to implement mouse holding, you need use add GestureRecognizer for your element and use GestureRecognizer's holding event to implement mouse holding. For more detail please refer the following.

    public static class MouseHoldingEffect
    {
    
        public static readonly BindableProperty MouseHoldingProperty =
        BindableProperty.CreateAttached("MouseHolding", typeof(Action), typeof(MouseHoldingEffect), default(Action), propertyChanged: OnhandlerChanged);
    
    
        public static Action GetMouseHolding(BindableObject view)
        {
            return (Action)view.GetValue(MouseHoldingProperty);
        }
    
    
        public static void SetMouseHolding(BindableObject view, Action value)
        {
            view.SetValue(MouseHoldingProperty, value);
        }
    
        static void OnhandlerChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var view = bindable as View;
            if (view == null)
            {
                return;
            }
    
            Action action = (Action)newValue;
            if (action != null)
            {
                view.Effects.Add(new ControlTooltipEffect());
            }
            else
            {
                var toRemove = view.Effects.FirstOrDefault(e => e is ControlTooltipEffect);
                if (toRemove != null)
                {
                    view.Effects.Remove(toRemove);
                }
            }
        }
    
        class ControlTooltipEffect : RoutingEffect
        {
            public ControlTooltipEffect() : base($"Microsoft.{nameof(MouseHoldingEffect)}")
            {
    
            }
        }
    }
    

    UWPMouseEffect

    public class UWPMouseEffect : PlatformEffect
    {
        Windows.UI.Input.GestureRecognizer recognizer;
        ManipulationInputProcessor manipulationProcessor;
       
        protected override void OnAttached()
        {
            var control = Control ?? Container;
    
            if (control is UIElement)
            {
                var mouseHolding = Element.GetValue(MouseHoldingEffect.MouseHoldingProperty) as Action;
                var target = control as UIElement;
                var parent = Window.Current.Content;
                recognizer = new Windows.UI.Input.GestureRecognizer();
                manipulationProcessor = new ManipulationInputProcessor(recognizer, target, parent, mouseHolding);
            }
        }
    
        protected override void OnDetached()
        {
    
        }
    }
    
    class ManipulationInputProcessor
    {
        Action mouseHolding;
        Windows.UI.Input.GestureRecognizer recognizer;
        UIElement element;
        UIElement reference;
        TransformGroup cumulativeTransform;
        MatrixTransform previousTransform;
        CompositeTransform deltaTransform;
        public ManipulationInputProcessor(Windows.UI.Input.GestureRecognizer gestureRecognizer, UIElement target, UIElement referenceFrame, Action holdingAction)
        {
            recognizer = gestureRecognizer;
            element = target;
            reference = referenceFrame;
            mouseHolding = holdingAction;
            // Initialize the transforms that will be used to manipulate the shape
            InitializeTransforms();
    
            // The GestureSettings property dictates what manipulation events the
            // Gesture Recognizer will listen to.  This will set it to a limited
            // subset of these events.
            recognizer.GestureSettings = GenerateDefaultSettings();
    
            // Set up pointer event handlers. These receive input events that are used by the gesture recognizer.
            element.PointerPressed += OnPointerPressed;
            element.PointerMoved += OnPointerMoved;
            element.PointerReleased += OnPointerReleased;
            element.PointerCanceled += OnPointerCanceled;
    
    
            recognizer.Holding += Recognizer_Holding;
    
        }
    
        private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
        {
            recognizer.ProcessMoveEvents(e.GetIntermediatePoints(reference));
        }
    
        private void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
        {
            recognizer.CompleteGesture();
            element.ReleasePointerCapture(e.Pointer);
        }
    
        private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
        {
            recognizer.ProcessUpEvent(e.GetCurrentPoint(reference));
    
            // Release the pointer
            element.ReleasePointerCapture(e.Pointer);
        }
    
        private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            element.CapturePointer(e.Pointer);
            // Feed the current point into the gesture recognizer as a down event
            recognizer.ProcessDownEvent(e.GetCurrentPoint(reference));
        }
    
        private GestureSettings GenerateDefaultSettings()
        {
            return GestureSettings.HoldWithMouse;
        }
    
        private void Recognizer_Holding(Windows.UI.Input.GestureRecognizer sender, HoldingEventArgs args)
        {
            System.Diagnostics.Debug.WriteLine("-----------Holding---------");
            mouseHolding();
        }
    
        private void InitializeTransforms()
        {
            cumulativeTransform = new TransformGroup();
            deltaTransform = new CompositeTransform();
            previousTransform = new MatrixTransform() { Matrix = Matrix.Identity };
    
            cumulativeTransform.Children.Add(previousTransform);
            cumulativeTransform.Children.Add(deltaTransform);
    
            element.RenderTransform = cumulativeTransform;
        }
    }
    

    Usage

    <StackLayout>
        <Label
            effect:MouseHoldingEffect.MouseHolding="{Binding MouseHoldingAction}"
            FontSize="25"
            Text="Hello" VerticalOptions="Center" HorizontalOptions="Center" Margin="100"/>
    </StackLayout>
    

    ViewModel

    public class ViewModel : INotifyPropertyChanged, IDisposable
    {
        public ViewModel()
        {
            MouseHolding(() =>
            {
                // do some stuff
    
            });
        }
        public Action MouseHoldingAction { set; get; }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void Dispose()
        {
            
        }
    
        public void MouseHolding(Action action)
        {
            MouseHoldingAction = action;
        }
    
    }