jonyballerino
jonyballerino

Reputation: 17

disable shortcut commands from textbox in wpf, keeping keystrokes detection

I'm trying a way to catch keys combination from a TextBox in a WPF application. The code written so far (that i found in an other discussion) helps me to do this, except for those combinations that are linked to a shortcut command (Ctrl+C, Ctrl+V, Ctrl+X...). I want to catch all keys that user press on textbox (Space, Delete, Backspace and all the combinations above). This is the code:

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;
    }

Someone has an idea for disable shortcut commands, keeping keystrokes? Thanks!

Upvotes: 1

Views: 1756

Answers (3)

Enrique
Enrique

Reputation: 134

You could remove any key action in PreviewKeyDown event with e.Handled=true or do whatever other action:

    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;
    }

Upvotes: 1

NearHuscarl
NearHuscarl

Reputation: 81310

This may not be the most elegant solution but it works for me. I need a textbox that accepts all input keys without triggering shortcut command so user can change hotkeys to whatever they want

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,
        };
    }
}

Upvotes: 1

brunnerh
brunnerh

Reputation: 184396

I think those can be overridden using custom CommandBindings using the corresponding application commands. Set ExecutedRoutedEventArgs.Handled to true in the handler.

Also: This is a bad idea in terms of usability.


Example:

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

Upvotes: 1

Related Questions