AvaloniaUI / Avalonia.Xaml.Behaviors

Port of Windows UWP Xaml Behaviors for Avalonia Xaml.
MIT License
385 stars 46 forks source link

ButtonClickEventTriggerBehavior misses previously pressed modifiers #154

Open JaggerJo opened 5 months ago

JaggerJo commented 5 months ago

I had 2 problems with ButtonClickEventTriggerBehavior.

  1. It seems that only modifier keys get tracked for events that reach the control: https://github.com/AvaloniaUI/Avalonia.Xaml.Behaviors/blob/5d73e78b6f235dd54fa4272eb05426bdaa709a69/src/Avalonia.Xaml.Interactions.Custom/ButtonClickEventTriggerBehavior.cs#L36-L37

  2. Key Events that happened outside of the control / before the control was created are not respected.

I might be wrong about 1.

My working modified version of ButtonClickEventTriggerBehavior is below, but I'm not really happy with the implementation as it involves setting event handlers on the top-level.

Is there a better way to archive this?

public static class ModifierKeyStateProvider
{
    public static KeyModifiers ModifierKeyState { get; set; }
}

public class ButtonClickModifierKeyBehavior : Trigger<Button>
{
    public static readonly StyledProperty<KeyModifiers> KeyModifiersProperty =
        AvaloniaProperty.Register<ButtonClickModifierKeyBehavior, KeyModifiers>(nameof(KeyModifiers));

    public KeyModifiers KeyModifiers
    {
        get => GetValue(KeyModifiersProperty);
        set => SetValue(KeyModifiersProperty, value);
    }

    protected override void OnAttachedToVisualTree()
    {
        if (AssociatedObject is not null)
        {
            AssociatedObject.Click += AssociatedObject_OnClick;
        }
    }

    protected override void OnDetachedFromVisualTree()
    {
        if (AssociatedObject is not null)
        {
            AssociatedObject.Click -= AssociatedObject_OnClick;
        }
    }

    private void AssociatedObject_OnClick(object? sender, RoutedEventArgs e)
    {
        if (AssociatedObject is not null && KeyModifiers == ModifierKeyStateProvider.ModifierKeyState)
        {
            Interaction.ExecuteActions(AssociatedObject, Actions, e);
        }
    }
}

Glue code in App code behind:

        var topLevel = this.GetTopLevel();

        topLevel.AddHandler(
            InputElement.KeyDownEvent,
            (s, e) => ModifierKeyStateProvider.ModifierKeyState = e.KeyModifiers,
            handledEventsToo: true
        );

        topLevel.AddHandler(
            InputElement.KeyUpEvent,
            (s, e) => ModifierKeyStateProvider.ModifierKeyState = e.KeyModifiers,
            handledEventsToo: true
        );