从 wpf 中的文本框禁用快捷命令,保持击键检测

disable shortcut commands from textbox in wpf, keeping keystrokes detection

我正在尝试一种从 WPF 应用程序中的文本框捕获组合键的方法。 到目前为止编写的代码(我在其他讨论中找到的)帮助我做到这一点,除了那些链接到快捷命令的组合(Ctrl+C、Ctrl+V、Ctrl+X...)。 我想捕获用户在文本框上按下的所有键(Space、Delete、Backspace 和上面的所有组合)。 这是代码:

int MaxKeyCount = 3;
List<Key> PressedKeys = new List<Key>();

private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Handled) return;

        //Check all previous keys to see if they are still pressed
        List<Key> KeysToRemove = new List<Key>();
        foreach (Key k in PressedKeys)
        {
            if (!Keyboard.IsKeyDown(k))
                KeysToRemove.Add(k);
        }

        //Remove all not pressed keys
        foreach (Key k in KeysToRemove)
            PressedKeys.Remove(k);

        //Add the key if max count is not reached
        if (PressedKeys.Count < MaxKeyCount)
            //Add the key if it is part of the allowed keys
            //if (AllowedKeys.Contains(e.Key))
            if (!PressedKeys.Contains(e.Key))
                PressedKeys.Add(e.Key);

        PrintKeys();

        e.Handled = true;
    }

    private void PrintKeys()
    {
        //Print all pressed keys
        string s = "";
        if (PressedKeys.Count == 0) return;

        foreach (Key k in PressedKeys)
            if (IsModifierKey(k))
                s += GetModifierKey(k) + " + ";
            else
                s += k + " + ";

        s = s.Substring(0, s.Length - 3);
        TextBox.Text = s;
    }

    private bool IsModifierKey(Key k)
    {
        if (k == Key.LeftCtrl || k == Key.RightCtrl ||
            k == Key.LeftShift || k == Key.RightShift ||
            k == Key.LeftAlt || k == Key.RightAlt ||
            k == Key.LWin || k == Key.RWin)
            return true;
        else
            return false;
    }

    private ModifierKeys GetModifierKey(Key k)
    {
        if (k == Key.LeftCtrl || k == Key.RightCtrl)
            return ModifierKeys.Control;

        if (k == Key.LeftShift || k == Key.RightShift)
            return ModifierKeys.Shift;

        if (k == Key.LeftAlt || k == Key.RightAlt)
            return ModifierKeys.Alt;

        if (k == Key.LWin || k == Key.RWin)
            return ModifierKeys.Windows;

        return ModifierKeys.None;
    }

有人想出禁用快捷命令但保留击键的想法吗? 谢谢!

我认为可以使用相应的应用程序命令使用自定义 CommandBindings 覆盖这些。在处理程序中将 ExecutedRoutedEventArgs.Handled 设置为 true

另外:就可用性而言,这是个坏主意。


示例:

<TextBox>
    <TextBox.CommandBindings>
        <CommandBinding Command="Paste" Executed="CommandBinding_Executed"/>
    </TextBox.CommandBindings>
</TextBox>
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    e.Handled = true;
}

这可能不是最优雅的解决方案,但对我来说很管用。我需要一个接受所有输入键而不触发快捷命令的文本框,以便用户可以将热键更改为他们想要的任何内容

class NoShortcutTextBox : TextBox
{
    /// <summary>
    /// Occurs when a key is pressed while focus is on this element. Apply to all shortcuts like
    /// Ctrl-C or Ctrl-V
    /// </summary>
    public new event KeyEventHandler KeyDown;

    public NoShortcutTextBox()
    {
        CommandManager.AddPreviewCanExecuteHandler(this, CanExecute);

        // Workaround as we cannot raise event in base class, so we hide it and use
        // our version of KeyDown instead
        base.KeyDown += (sender, e) => KeyDown(sender, e);

        // --Demo--
        KeyDown += (sender, e) =>
        {
            // Fetch the actual shortcut key
            var key = (e.Key == Key.System ? e.SystemKey : e.Key);

            if (key == Key.LeftShift || key == Key.RightShift
            || key == Key.LeftCtrl || key == Key.RightCtrl
            || key == Key.LeftAlt || key == Key.RightAlt
            || key == Key.LWin || key == Key.RWin) return;

            var sb = new StringBuilder();

            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
            {
                sb.Append("Ctrl+");
            }
            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
            {
                sb.Append("Shift+");
            }
            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
            {
                sb.Append("Alt+");
            }

            Console.WriteLine(sb.Append(key.ToString()).ToString());
        };
    }

    private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.Handled = true;

        if (!(e.Command is RoutedUICommand command)) return;

        switch (command.Text)
        {
            case "Copy":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.C));
                break;
            case "Cut":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.X));
                break;
            case "Paste":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.V));
                break;
            case "Select All":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.A));
                break;
            case "Undo":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Z));
                break;
            case "Redo":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Y));
                break;
            case "Backspace":
            case "DeletePreviousWord":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Back));
                break;
            case "Delete":
            case "DeleteNextWord":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Delete));
                break;
            case "MoveToLineStart":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Home));
                break;
            case "MoveToLineEnd":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.End));
                break;
            case "ToggleInsert":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Insert));
                break;
            case "MoveUpByPage":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.PageUp));
                break;
            case "MoveDownByPage":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.PageDown));
                break;
            case "MoveLeftByCharacter":
            case "MoveLeftByWord":
            case "SelectLeftByWord":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Left));
                break;
            case "MoveRightByCharacter":
            case "MoveRightByWord":
            case "SelectRightByWord":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Right));
                break;
            case "MoveDownByLine":
            case "MoveDownByParagraph":
            case "SelectDownByParagraph":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Down));
                break;
            case "MoveUpByLine":
            case "MoveUpByParagraph":
            case "SelectUpByParagraph":
                KeyDown?.Invoke(this, GetKeyEventArgs(Key.Up));
                break;
        }
    }

    private KeyEventArgs GetKeyEventArgs(Key key)
    {
        return new KeyEventArgs(
            Keyboard.PrimaryDevice,
            new HwndSource(0, 0, 0, 0, 0, "", IntPtr.Zero), // dummy source
            0,
            key)
        {
            RoutedEvent = TextBox.KeyDownEvent,
        };
    }
}

您可以使用 e.Handled=true 删除 PreviewKeyDown 事件中的任何关键操作或执行任何其他操作:

    private void yourControl_PreviewKeyDown(object sender, KeyEventArgs e) {
        // Remove shortcut for Ctrl+C
        if ((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control && e.Key == Key.C)
            e.Handled = true;
    }