How do I read custom keyboard shortcut from user in WPF? - wpf

In my application, I want to let users customize keyboard shortcuts, just like it's done in Visual Studio's keyboard options. The user can focus a blank text box and then type any shortcut he wants to assign to a command.
The closest I've come to make it work is by subscribing to the TextBox.PreviewKeyDown event, setting it as handled to prevent actual text input in the text box. I then ignore the KeyDown events associated with modifier keys (is there a cleaner way to determine if a Key is a modifier key?).
// Code-behind
private void ShortcutTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
// The text box grabs all input
e.Handled = true;
if (e.Key == Key.LeftCtrl ||
e.Key == Key.RightCtrl ||
e.Key == Key.LeftAlt ||
e.Key == Key.RightAlt ||
e.Key == Key.LeftShift ||
e.Key == Key.RightShift)
return;
string shortcutText = "";
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
shortcutText += "Ctrl+";
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
shortcutText += "Shift+";
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
shortcutText += "Alt+";
_ShortcutTextBox.Text = shortcutText + e.Key.ToString();
}
The above works for any shortcut starting with Ctrl and Ctrl+Shift, but fails for any Alt shortcuts. The e.Key is always set to Key.System when I press a shortcut containing Alt.
How can I record Alt shortcuts from the user? Is there a better, more robust way to record shortcuts form the user?

The trick is to use the SystemKey property if the Key property is set to Key.System:
private void ShortcutTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
// The text box grabs all input.
e.Handled = true;
// Fetch the actual shortcut key.
Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
// Ignore modifier keys.
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;
}
// Build the shortcut key name.
StringBuilder shortcutText = new StringBuilder();
if ((Keyboard.Modifiers & ModifierKeys.Control) != 0) {
shortcutText.Append("Ctrl+");
}
if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0) {
shortcutText.Append("Shift+");
}
if ((Keyboard.Modifiers & ModifierKeys.Alt) != 0) {
shortcutText.Append("Alt+");
}
shortcutText.Append(key.ToString());
// Update the text box.
_ShortcutTextBox.Text = shortcutText.ToString();
}
I added the left and right Windows keys to the modifier list, because they sometimes appeared in the shortcut key name when a complex (Ctrl+Shift+Alt) key combination was typed from a Terminal Server session. They're never present in Keyboard.Modifiers, though, since they're reserved for global shortcuts, so I don't handle them there.
I also used a StringBuilder to avoid creating too many string instances.
This solution works with any key combination, except Shift+Alt (the Alt modifier is not seen in that case). That might be an artifact of my Terminal Server environment, though, so your mileage may vary.
Finally, I added a _File menu to the window to see what would happen, and the Alt+F shortcut key is effectively trapped by the text box before it reaches the menu, which seems to be what you want.

Hi
if You used WPF-Command in your application you can use this:
<Window.InputBindings>
<KeyBinding Command="YourCommnad"
Gesture="CTRL+C" />
</Window.InputBindings>

Related

WPF - set text to Window as TextBox

i would like catch text in WPF Window. For example, application window will focus and I am going to write without focus in some textbox. My application has input from barcode reader. I want users to be able read a barcode without click to some textbox - for faster work. It is possible? I tried event PreviewKeyDown but application catch only first char. My barcodes are in format #12000012546 and barcode reader emulate numeric keyboard (Shift + num) - for example instead of char # KeyEventArgs return only "System". This is my code:
static string text = string.Empty;
private void MainWindow_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
text += e.Key.ToString();
if (text.Length == 12)
{
MessageBox.Show(text)
text = null;
}
}
Thanks for advice
If your Barcode reader is really doing key presses (Shift + number) to do your # character, then check this out. This was tested on an Spanish Keyboard, so the # character is on my 3 key (Key.D3)
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
var regex = new Regex("(#)|([0-9])");
var keystr = e.Key.ToString();
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
if (regex.Match(keystr).Success)
if (e.Key == Key.D3) textBox1.Text += "#";
}
else if (regex.Match(keystr).Success)
textBox1.Text += keystr.Replace("D","");
}

For Japanese Full Width Characters, WPF textbox interactivity behavior AttachedObject PreviewTextInput doesn't work

I have developed a WPF textbox which formats numbers as we keep typing numbers in textbox. This is using interactivity behavior. Now, It works fine with normal regular keyboard. But as we support globalization, textbox should support to Japanese characters(numbers) as well which are full width characters. So to test, when I select Japanese keyboard - Microsoft IME in (windows-controlpanel-region & langauage - keyboard and languages - change keyboards), and enter japanese numbers my interactivity behavior code (hookup) AssociatedObject_PreviewTextInput does not get called until I press 'Enter' Key which creates other issues for me. For regular keyboard, this function gets called as soon I type number.
Code looks as below
private void AssociatedObject_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
TextBox tb = sender as TextBox;
// other stuff
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += AssociatedObject_PreviewTextInput;
/// other stuff
}
Any help is appreciated. Thank you.
You can listen to PreviewKeyDown/PreviewKeyUp to get notified for each stroke.
Any you may want to take over input by manipulate TextBox's Text property directly, instead of waiting for the final input after pressing Enter.
AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
Here I just handle the case where user is using IME to input digits (0~9). I insert the digit at the caret. And by setting e.Handled = true;, IME will not input anything to the TextBox.
private void AssociatedObject_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.ImeProcessedKey == Key.D0 ||
e.ImeProcessedKey == Key.D1 ||
e.ImeProcessedKey == Key.D2 ||
e.ImeProcessedKey == Key.D3 ||
e.ImeProcessedKey == Key.D4 ||
e.ImeProcessedKey == Key.D5 ||
e.ImeProcessedKey == Key.D6 ||
e.ImeProcessedKey == Key.D7 ||
e.ImeProcessedKey == Key.D8 ||
e.ImeProcessedKey == Key.D9 )
{
TextBox tb = sender as TextBox;
int index = tb.CaretIndex;
//stripe the leading 'D'
string d = e.ImeProcessedKey.ToString().Substring(1);
tb.Text = tb.Text.Insert(index, d);
tb.CaretIndex = index + 1; //I need to manually move the caret forward, since caret position was reset to 0.
e.Handled = true; //important, so IME does not input to the TextBox
}
}

Move focus to next control on enter key pressed with Silverlight

I need to let the users change to "next" textbox, combobox or button in a form when the press the Enter key (they CAN'T press Tab...)
I've found a couple of solutions (set a new event for each textbox's key pressed and set focus to the next control...) but I want this to have an easy manteinance.
Is there any magic around??
I've had problems even with tab navigation in silverlight...
private void textBox3_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (Keyboard.Modifiers == ModifierKeys.Shift)
textBox4.Focus();
else
textBox5.Focus();
e.Handled = true;
}
}

Access key getting selected even we did not press 'Alt' key in WPF

I have WPF application which is having tool bar. In tool bar I have some user controls as tools.
I have set access key to each control, it is working fine.
The issue is: If I click a user control(which is consist of Button and Label, I have set access key for Button) the given task is completed, but when I press any access key without pressing 'Alt' key then it is getting selected.
Any ideas?
Apparently, this was a deliberate change by Microsoft. See Atanas Koralski's answer here:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/14f6f49f-0027-471b-b68c-e7f6ba012012
Menu and ToolBar mnemonics work without pressing Alt key. We decided
that we will have uniform behavior in all cases so access key work
without pressing Alt key.
I understand that this is not in parity with Forms and we will
consider this issue and change the behavior in the next version.
For now as a workaround you can register a class handler for all
AccessKeyPressed events and handle the event if Alt key is not
pressed.
EventManager.RegisterClassHandler(typeof(UIElement),
AccessKeyManager.AccessKeyPressedEvent,
new AccessKeyPressedEventHandler(OnAccessKeyPressed));
...
private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e)
{
if (!e.Handled && e.Scope == null && (e.Target == null || e.Target == label))
{
// If Alt key is not pressed - handle the event
if ((Keyboard.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt)
{
e.Target = null;
e.Handled = true;
}
}
}
Also see mfc2wpf's reply:
I've used the above and it works. However, that prevented the default
action for Enter and ESC. So I inserted the following at the top of
the method.
if (Keyboard.IsKeyDown(Key.Enter) || Keyboard.IsKeyDown(Key.Escape)) return;
Access keys include Enter and Esc, which are the default keys for Buttons which have IsDefault = true or IsCancel = true. If you don't want to require Alt+Enter and Alt+Esc for those buttons, you would need to add the special condition to the handler.
As of .Net 4.5 you can configure this behavior with the CoreCompatibilityPreferences.IsAltKeyRequiredInAccessKeyDefaultScope property. To change the access key behavior so that they will only fire when Alt is pressed set it to true.
CoreCompatibilityPreferences.IsAltKeyRequiredInAccessKeyDefaultScope = true;
As the documentation states this must early in the app. Setting it will throw an exception after it has been read.
As stated in other answers, setting IsAltKeyRequiredInAccessKeyDefaultScope avoids invoking actions for access keys without pressing the Alt key. However, this can also have the effect of disabling the Enter key (for invoking the default action) and Esc key (for invoking the Cancel action).
Using the suggested workaround instead, and testing for Key.Enter and Key.Escape, can circumvent this problem. However, you might then find that menu items cannot be selected by their access key without pressing the Alt key, which could be a problem if a button in scope uses the same access key.
An alternative could then be to handle the access key event by checking whether a potentially invokable AccessText control is within a MenuItem or not, something along these lines:
EventManager.RegisterClassHandler(
typeof(UIElement),
AccessKeyManager.AccessKeyPressedEvent,
new AccessKeyPressedEventHandler(OnAccessKeyPressed));
...
static void OnAccessKeyPressed(object accessKeyTarget, AccessKeyPressedEventArgs e)
{
if (!e.Handled && e.Scope == null &&
(Keyboard.Modifiers & ModifierKeys.Alt) != ModifierKeys.Alt &&
!ShouldElementHandleAccessKeysWhenAltIsNotPressed(accessKeyTarget as UIElement))
{
e.Target = null;
e.Handled = true;
}
}
static bool ShouldElementHandleAccessKeysWhenAltIsNotPressed(UIElement element)
{
if (element == null) return false;
var accessText = element as AccessText;
if (accessText != null && !IsDecendantOfMenuItem(accessText)) return false;
return true;
}
static bool IsDecendantOfMenuItem(DependencyObject element)
{
for (; element != null; element = VisualTreeHelper.GetParent(element))
if (element is MenuItem) return true;
return false;
}

Why doesn't the MaxLength property on a RichTextBox work in WPF?

I am trying to set the MaxLength property on a RichTextBox but it does not seem to work.
Any ideas what might be happening?
The fundamental problem is that the WPF RichTextBox doesn't have a MaxLength property - unlike the Windows.Forms one.
Here's an improvement on #jhony's anwser. If you catch the PreviewKeyDown event and check the length, you also need to allow the user to press Delete and BackSpace after hitting the limit.
// In constructor
this.RichTextBox.PreviewKeyDown += this.EditBox_KeyDown;
private void EditBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Delete && e.Key != Key.Back)
{
var range = new TextRange(this.RichTextBox.Document.ContentStart, this.RichTextBox.Document.ContentEnd);
if (range.Text.Length > this.MaxLength)
{
e.Handled = true;
return;
}
}
}
You should also allow the arrow keys, because you wouldn't expect them to be disabled.
To disable pasting, put this in the constructor DataObject.AddPastingHandler(this.RichTextBox, this.EditBox_Paste); and
private void EditBox_Paste(object sender, DataObjectPastingEventArgs e)
{
e.CancelCommand();
}
However, you might want to allow pasting unless it breaks the MaxLength, in which case you'll need to check the text being inserted, and the text it is replacing. At that point I decided not to implement a MaxLength in the control, but to handle it with the rest of the validation in the form.
Add this code in yout KeyDown Event.
private void rLetter_KeyDown(object sender, KeyEventArgs e)
{
TextRange tr= new TextRange(rLetter.Document.ContentStart, rLetter.Document.ContentEnd);
if (tr.Text.Length > 4000 || e.Key == Key.Space || e.Key == Key.Enter)
{
e.Handled = true;
return;
}
}
I have some problem, The bug is :
Test this code using copy and paste text max 4000.
Sorry for my English ..

Resources