AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.48k stars 2.21k forks source link

Tap Events: Holding event not firing. #17208

Open GIL0487 opened 5 days ago

GIL0487 commented 5 days ago

Describe the bug

Tap Events: Holding event never fires on button

To Reproduce

Create a very basic Avalonia application: I did this by using VisualStudio Project Menu -> Avalonia C# Project. I added a button to the MainWidow.

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="AvaloniaApplication1.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="AvaloniaApplication1">

    <Button Margin="50, 50, 0, 0"
            Content="TEST"
            Height="50"
            Width="50"
            Background="Yellow"
            Foreground="Red"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Gestures.IsHoldWithMouseEnabled="True"
            Gestures.IsHoldingEnabled="True"
            Holding="InputElement_OnHolding">
    </Button>

</Window>

I also added the handler in the code behind

using Avalonia.Controls;
using Avalonia.Input;

namespace AvaloniaApplication1.Views;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void InputElement_OnHolding(object? sender, HoldingRoutedEventArgs e)
    {
        var breakPointTest = false;
    }
}

Expected behavior

The breakpoint in never hit and, by looking ad the DevTools, the holding event is never generated by the Avalonia code.

Based on the online documentation, settings the Gestures should be enough. https://docs.avaloniaui.net/docs/concepts/input/pointer

Upon additional debug, it seems that the method GetIsHoldWithMouseEnabled in Gestures always return false, eventhough the property is set to true in the xaml.

 public static bool GetIsHoldWithMouseEnabled(StyledElement element)
        {
            return element.GetValue(IsHoldWithMouseEnabledProperty);
        }

Avalonia version

11.1.3 (the same applies to 11.2.0-rc)

OS

Windows

Additional context

Win 11 Enterprise 23H2 VS2022 17.11.4

stevemonaco commented 5 days ago

Certain events, often gestures, are dependent on certain other events occurring (or not occurring). Controls such as Button will handle PointerPressed events which is a required part of Gestures.Holding, so it never happens. If you change your example to Rectangle, add a Fill, and remove the Button-specific properties, your gesture hits the breakpoint.

Some design insight here from the team may be necessary. Could required events in gestures be subscribed with handledEventsToo: true so gestures will still fire regardless of a control's opinions on handling events? I'm not able to capture the HoldingEvent despite AddHandler with Tunneling and handledEventsToo: true, but I expected that.

GIL0487 commented 4 days ago

I could be wrong, but based on my analysis, I believe that the event handling from the Buttonare correctly set. What appears to be the main issue is that the property IsHoldWithMouseEnabledProperty always returns false in Gestures when GetIsHoldWithMouseEnabled(StyledElement element) is invoked. The property is set to true in the xaml.

stevemonaco commented 4 days ago

What appears to be the main issue is that the property IsHoldWithMouseEnabledProperty always returns false in Gestures when GetIsHoldWithMouseEnabled(StyledElement element) is invoked.

After investigating, you're correct. Please disregard my previous comment. The default value of the Gestures.IsHoldWithMouseEnabled property is false and the property is not inherited. The ContentPresenter within the Button is the e.Source that the Gestures.PointerPressed handler is working with. Please leave the issue open for a maintainer to determine if this is a bug or by-design.

Anyways, as a workaround, I've tested the following to work (Fluent Theme):

<Style Selector="Button /template/ ContentPresenter#PART_ContentPresenter">
    <Setter Property="(Gestures.IsHoldWithMouseEnabled)" Value="True" />
</Style>

You could forward this via binding instead:

<Style Selector="Button /template/ ContentPresenter#PART_ContentPresenter">
    <Setter Property="(Gestures.IsHoldWithMouseEnabled)" Value="{Binding $parent[Button].(Gestures.IsHoldWithMouseEnabled)}" />
</Style>
GIL0487 commented 4 days ago

Thanks for your reply, I can confirm that this workaround works in my test application. It also works if I regress the Avalonia version back to 11.1.0.

I will leave the issue open and wait for a reply from a Maintainer.

Thanks

GIL0487 commented 3 days ago

An update on this. I noticed that the workaround works only if the pointer is not over the AccessText element of the button. When the pointer is over the Button but not over the AccessText, the Holding event is fired. image

When the pointer is over the AccessText, the Holding event is NOT fired. image

Thanks

stevemonaco commented 3 days ago

You can make the workaround selector more universal and apply to all descendant controls. The holding event will bubble up to your Button handler.

<Style Selector="Button :is(Control)">
    <Setter Property="(Gestures.IsHoldWithMouseEnabled)" Value="{Binding $parent[Button].(Gestures.IsHoldWithMouseEnabled)}" />
</Style>