Reputation: 17
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
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
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
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