I have a RichTextBox control and I need to handle CTRL + = keydown combination. = sign itself is Key.OemPlus. So the problem is that when I press this combination, the KeyDown event is not raised.
I tried different code variants, with Keyboard.IsKeyDown for both pressed keys, with Keyboard.Modifiers for CTRL key, etc...
if (Keyboard.IsKeyDown(Key.OemPlus) && Keyboard.IsKeyDown(Key.LeftCtrl))
{
// do smth
}
if (Keyboard.IsKeyDown(Key.OemPlus) && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
// do smth
}
if (args.Key == Key.OemPlus && Keyboard.IsKeyDown(Key.LeftCtrl))
{
// do smth
}
if (args.Key == Key.OemPlus && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
// do smth
}
So, none of the above code attempts worked and I can't find the answer in Google. How to solve this problem?
Classic WPF routed event problem. The KeyDown routed event is being handled by the RichTextBox internally. It thinks Ctrl + = is a command that it recognized, so it eats the KeyDown event before it gets to you. Luckily there is a solution: PreviewKeyDown. If you change your code to handle PreviewKeyDown instead of KeyDown you should find that the events fire as you expect.
Related
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
}
}
My goal is to implement a custom Control + S key press handler to wire up to a custom save method in a winforms app.
There are several ways to accomplish this based on my R&D. First, I tried the obvious KeyPress event handler. This wasn't powerful enough to capture the key presses I need (it wasn't called on the Editor level, which is what I needed).
The second option which looks better is the protected override bool ProcessCmdKey(ref Message msg, Keys keyData) override. This works - it intercepts the CTRL key click, but apparently I need to write extra code to persist the fact the CTRL key is pressed and intercept the next key press (which would be S in my case) and then perform the custom action.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.S | Keys.Control))
{
// This is never called
}
else if (keyData == (Keys.Menu | Keys.Alt))
{
// this is called each time I hit CTRL
}
return true;
}
ProcessCmdKey seems to be called immediately after I hit the CTRL key.
This post suggests creating a KeyTracker class that will persist the keys pressed and do what it needs to do:
Capture Key Sequence via ProcessCmdKey
Which seems like a good option, but before I dig in an implement a memento tracking pattern, does anyone have input on how else to accomplish this seemingly common feature?
Another pattern uses the GetKeyboardState API function:
Capture multiple key downs in C#
This seems interesting, though I'm not sure it will suite my needs.
[DllImport ("user32.dll")]
public static extern int GetKeyboardState( byte[] keystate );
private void Form1_KeyDown( object sender, KeyEventArgs e )
{
byte[] keys = new byte[255];
GetKeyboardState (keys);
if( keys[(int)Keys.Up] == 129 && keys[(int)Keys.Right] == 129 )
{
Console.WriteLine ("Up Arrow key and Right Arrow key down.");
}
}
Thank you for taking a look at my problem.
UPDATE
I've added three events for key handling to my DataPanel. None of these events are being picked up by VS when I set breakpoints in the events, so this is what leads me to believe that ProcessCmdKey is my best option.
If I could get these Events to work, that would be good as well:
// Ctrl + S: Save Support
this.ParentForm.KeyPreview = true;
this.KeyPress += new KeyPressEventHandler(DataPanel_KeyPress);
this.KeyDown += new KeyEventHandler(DataPanel_KeyDown);
this.PreviewKeyDown += new PreviewKeyDownEventHandler(DataPanel_PreviewKeyDown);
None of these events seem to be caught when pressing any keys:
void DataPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == (Keys.S | Keys.Control))
{
SessionManager.Trace.AddTrace("You Hit Save!!");
}
}
void DataPanel_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == (Keys.S | Keys.Control))
{
SessionManager.Trace.AddTrace("You Hit Save!!");
}
}
void DataPanel_KeyPress(object sender, KeyPressEventArgs e)
{
var key = e.KeyChar;
}
UPDATE
I've solved the problem by using a simple KeyUp event and the KeyPreview flag:
void ShipmentDataPanel_KeyUp(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
MessageBox.Show("Control + S Key Hit!");
}
}
Thank you.
Set the KeyPreview property of our form to true. The summary of this property says: "Gets or sets a value indicating whether the form will receive key events before the event is passed to the control that has focus.". Then use the KeyUp event. Unless KeyPressed it gives also information on special keys like control keys.
I solved this by doing,
if(e.KeyData==(Keys.S | Keys.Control))
Though this is a very old question, I still like to add my answer.
The OP writes that he could not use the ProcessCmdKey because it would fire as soon he hits the Control key and would not wait until he hits the S key also.
I don't have that problem, my code below works well and the Delete() method is only called when I first hit Ctrl and then S
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
bool bHandled = false;
// switch case is the easy way, a hash or map would be better, but more work to get set up.
switch (keyData)
{
case Keys.F5:
RefreshList("", "", false); // call my refresh method
bHandled = true;
break;
case Keys.S | Keys.Control: // call my delete method
Delete(false);
bHandled = true;
break;
default:
base.ProcessCmdKey(ref msg, keyData);
break;
}
return bHandled;
}
protected virtual void Delete(bool handled)
{
if (handled == false)
{
MessageBox.Show("delete");
}
}
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;
}
I would like to develop a textbox which restricts special symbols like %. I used textbox's keydown event to restrict '%' . I already used the code as
if(Keyboard.Modifiers == ModifierKeys.Shift && e.key == key.D5)
{
e.handle=true;
return;
}
when i implement this in mvvm architecture, I got problem with the dependency property that recognizes only shift as one key and D5 as another when I converted systemkey into string format.
How can I recognize % symbol?
you can listen to the PreviewTextInput event instead of the KeyDownEvent :
myTextBox.PreviewTextInput += PreviewTextInputHandler;
and then:
private void PreviewTextInputHandler(Object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !AreAllValidChars(e.Text);
}
this is one such function I use in my App, you would have to tweak it a bit to test the right caracters, but this you know how to do.
as for getting the % caracter, well you just have to write something like:
if (e.Text == '%') ...;
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>