microsoft / XamlBehaviorsWpf

Home for WPF XAML Behaviors on GitHub.
MIT License
852 stars 140 forks source link

KeyTrigger.ActiveOnFocus ignored when the event is fired from a ListBox ItemTemplate when VirtualizationMode is set to Recycling #138

Closed danielkaczmarek closed 1 year ago

danielkaczmarek commented 1 year ago

When the KeyTrigger.ActiveOnFocus is set to true, I expect it to only fire when the control’s behaviour is attached to is focused. Instead what I get is a call from multiple controls, the more controls I fire the event from the more hits I get on each time I fire it eg, if I fire the event through shift + arrow 3 times, then the first time it will fire once, but the third time it will fire 3 times. This happens in a ListBox.ItemTemplate when the Virtualisation is set to Recycling.

Steps to reproduce the behavior: Open the attached project Set a break point in MainWindowViewModel.ClickCommandCallbackAction line 20 Run the project and press Shift + Right arrow key on a few separate records, each time scroll down or up to a new set of records Observe your break point being hit multiple times each time you press Shift + Right arrow key. You will also notice that the same Toggle Button is issuing this event multiple times (see the id oarameter passed into ClickCommandCallbackAction)

Expected behavior Each time you press Shift + arrow key, the breakpoint should only be hit once

Desktop (please complete the following information):

WpfApp1.zip

brianlagunas commented 1 year ago

This happens due to the complex nature of virtualization and the UIElement lifecycle. Basically, the recycled items are loaded multiple times and therefore the event get registered multiple times. The fix is quite simple, but I'm not sure if this should be added to the source or not.

Create a custom key trigger and add a simple check if the OnEvent should be called

public class CustomKeyTrigger : KeyTrigger
{
    bool keyTriggerAssigned = false;
    protected override void OnEvent(EventArgs eventArgs)
    {
        if (keyTriggerAssigned)
            return;
        base.OnEvent(eventArgs);
        keyTriggerAssigned = true;
    }
}

Let me know if this solves your problem.

@mgoertz-msft This may be something we need add to the KeyTrigger.OnEvent method, but I'm not sure what the impact of this addition would be. So for now I will just document the fix here, and if this keeps being reported, we may consider adding it to the source.

danielkaczmarek commented 1 year ago

This works, thank you 👍