Search code examples
c#windowsxamluwpuwp-xaml

MenuFlyoutItem.KeyboardAccelerators fires Invoked event twice


I have implemented MenuFlyoutItem.KeyboardAccelerators and below you can see the code

Xaml code

        <Button Content="Edit Options"
            Width="100"
            Height="100"
            HorizontalAlignment="Right">
        <Button.Flyout>
            <MenuFlyout>
                <MenuFlyoutItem x:Name="FlyoutItem1" Text="Copy" Icon="Copy">
                    <MenuFlyoutItem.KeyboardAccelerators>
                        <KeyboardAccelerator x:Name="Test" Key="C" Modifiers="Control" Invoked="Test_Invoked" />
                    </MenuFlyoutItem.KeyboardAccelerators>
                </MenuFlyoutItem>
                <MenuFlyoutSeparator/>
            </MenuFlyout>
        </Button.Flyout>
    </Button>

Code-Behind

    public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }
    private void Test_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
    {
        Debug.WriteLine($">>>>>>>>>>>>>>>>>>>>>>>>>>>>> invoked Hash = {sender.GetHashCode()}");
    }
}

Before clicking on the button in the application, the CTRL+C hotkey action fires an event. But when I click on the button and MenuFlyout appears, the Ctrl+C hotkey action fires twice the same event. And when I click on another place in the application event again fires normally only once.


If you want to test the application follow this link enter link description here


Solution

  • According to Mircrosoft Documentation about KeyboardAccelerator

    By default, an accelerator has global scope. However, you can constrain scope using ScopeOwner or disable an accelerator completely using IsEnabled.

    I don't know is this a bug or just a behavior that they haven't described on their documentation but if you got more than one KeyboardAccelerator in the same scope then it fires twice if you are focused on the element that contains KeyboardAccelerator.

    Solution.

    So in your case you need to specify scope for KeyboardAccelerator and it will fire only once in that scope.

    Important!

    You need to bind ScopeOwner only using x:Bind. It won't work with Binding.

    <Button Content="Edit Options"
            x:Name="button"
            Width="100"
            Height="100"
            HorizontalAlignment="Right">
        <Button.Flyout>
            <MenuFlyout>
                <MenuFlyoutItem x:Name="FlyoutItem1" Text="Copy" Icon="Copy">
                    <MenuFlyoutItem.KeyboardAccelerators>
                        <KeyboardAccelerator x:Name="Test" Key="C" Modifiers="Control" Invoked="Test_Invoked" ScopeOwner="{x:Bind button}" />
                    </MenuFlyoutItem.KeyboardAccelerators>
                </MenuFlyoutItem>
                <MenuFlyoutSeparator/>
            </MenuFlyout>
        </Button.Flyout>
    </Button>