0

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.

Kikanye
  • 1,198
  • 1
  • 14
  • 33
  • So, you just want to implement mouse holding event with PlatformEffect , right? – Nico Zhu Oct 22 '20 at 06:24
  • Yes this is what I want to do. – Kikanye Oct 22 '20 at 06:39
  • Please check this [code sample](https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/BasicInput/cs/5-GestureRecognizer.xaml.cs#L135) that how to implement mouse holding with `Gesture`. – Nico Zhu Oct 22 '20 at 08:09
  • @NicoZhu-MSFT Thanks for this but the code is not much help. I know how to change the GestureSettings, the problem is that I do not know how to change the GestureSettings for a FrameworkElement or UIElement. I made a GestureRecognizer object but the Holding Event is never being triggered. – Kikanye Oct 22 '20 at 20:52
  • Maybe adding some code will make my question more clear? – Kikanye Oct 22 '20 at 20:53
  • Ok, fine I will share all my code that implement mouse holding with PlatformEffect . – Nico Zhu Oct 23 '20 at 06:35

1 Answers1

1

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;
    }

}
Nico Zhu
  • 32,367
  • 2
  • 15
  • 36
  • Thanks this was very helpful. – Kikanye Oct 23 '20 at 23:24
  • A note on this though, I found that this gives false positives from time to time. It sometimes detects regular taps as a long press (usually when taps occur quickly). Any means to handle this would be great. – Kikanye Oct 28 '21 at 15:56