0
votes

I'm trying to handle Ctrl + Down in a treeview as a custom shortcut. I've tried the following code:

<Window x:Class="WpfTreeIssue.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    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"
    xmlns:local="clr-namespace:WpfTreeIssue"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800"
    x:Name="_this">
<Grid DataContext="{Binding ElementName=_this}">
    <TreeView>
        <TreeView.InputBindings>
            <KeyBinding
                Key="Down" Modifiers="Ctrl"
                Command="{Binding ControlDownCommand}" />
            <KeyBinding
                Key="A"
                Command="{Binding ControlDownCommand}" />
        </TreeView.InputBindings>

        <TreeViewItem Header="Level 1" IsExpanded="True">
            <TreeViewItem Header="Level 2.1" />
            <TreeViewItem Header="Level 2.2" IsExpanded="True">
                <TreeViewItem Header="Level 3.1" />
                <TreeViewItem Header="Level 3.2" />
            </TreeViewItem>
            <TreeViewItem Header="Level 2.3" />
        </TreeViewItem>
    </TreeView>
</Grid>

Codebehind:

public partial class MainWindow : Window
{
    public ICommand ControlDownCommand
    {
        get
        {
            return new RelayCommand<KeyBinding>(x => OnControlDown(x));
        }
    }

    private void OnControlDown(KeyBinding keyBinding)
    {
        Console.Write("HELLO");
    }

    public MainWindow()
    {
        InitializeComponent();
    }
}

RelayCommand is a basic ReplayCommand as seen here: https://www.c-sharpcorner.com/UploadFile/20c06b/icommand-and-relaycommand-in-wpf/

I've added KeyBindings for both Ctrl-Down & A. The A shortcut works just fine, but Ctrl-Down does not work.

I've tried removing the Ctrl modifier and the down shortcut still does not work (it moves the selected tree item, which makes sense). Interestly, the down shortcut DOES work if you navigate to the last item in the treeview and hit down (Ctrl-down does not work in that instance).

Any suggestions as to how I can get the Ctrl-Down KeyBinding to work on my TreeView?

Edit: I've also tried overriding the TreeView's OnKeyDown and OnPreviewKeyDown methods with no luck. See below:

public class CustomTreeView : TreeView
{
    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Down)
        {
            Console.WriteLine("Down arrow");
            if ((e.KeyboardDevice.IsKeyDown(Key.RightCtrl) || e.KeyboardDevice.IsKeyDown(Key.LeftCtrl)))
            {
                Console.WriteLine("TEST");
                e.Handled = true;
            }
        }

        base.OnKeyDown(e);
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Down)
        {
            Console.WriteLine("Down arrow");
            if ((e.KeyboardDevice.IsKeyDown(Key.RightCtrl) || e.KeyboardDevice.IsKeyDown(Key.LeftCtrl)))
            {
                Console.WriteLine("TEST");
                e.Handled = true;
            }
        }

        base.OnPreviewKeyDown(e);
    }
}

If I hold the Ctrl key & press down the line Console.WriteLine("Down arrow"); never gets hit (down arrow event is not sent). If I just press down, the line gets hit but the ctrl modifier is not set.

1

1 Answers

1
votes

There is a code in TreeView control which handles Ctrl+Down so your binding is not working as the keys event is already processed. You need to override OnKeyDown on TreeView to do something of your own.

Update: Here is a tested sample

protected override void OnKeyDown(KeyEventArgs e)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                if (System.Windows.Input.Key.Down == e.Key)
                {
                    System.Diagnostics.Trace.WriteLine("Ctlr+Down");
                    e.Handled = true;
                }
            }

            if (!e.Handled)
            {
                base.OnKeyDown(e);
            }
        }