I am building a XAML app (winrt) to be used in enterprise. Some forms in the app can be complex: some inputs gets shown/hidden depending on other inputs. I would like to control the tab key navigation using a behavior on all inputs (TextBox, PasswordBow, ComboBox...) to optimize the user activity.
I subscribed to the KeyUp event of the TextBox but the event is not raised when the user strikes the Tab key. As a consequence, the next element in the visual tree is given keyboard focus.
I found not method to override like the winform's IsInputKey.
How can I subscribe to the use of the Tab key on a TextBox?
Is looks like the newly focus element receives the KeyUp event.
What I did is subscribe to the KeyDown event, checked for the Tab key and marked the event as handled.
protected override void OnAttached()
{
var textBox = (TextBox)this.AssociatedObject;
textBox.KeyDown += this.OnKeyDown;
textBox.KeyUp += this.OnKeyUp;
// don't forget to unsubscribe in OnDetached
}
private void OnKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Tab && !e.Handled)
{
e.Handled = this.Work(sender, e);
}
}
private void OnKeyUp(object sender, KeyRoutedEventArgs e)
{
if (e.Key != VirtualKey.Tab && !e.Handled)
{
e.Handled = this.Work(sender, e);
}
}
Here is the method that does the focusing work. The code was in OnKeyUp before I knew how to do.
/// <returns>true if an action has been performed (focus next or execute command)</returns>
private bool Work(object sender, KeyRoutedEventArgs e)
{
var isEnterKey = e.Key == VirtualKey.Enter;
var isTabKey = e.Key == VirtualKey.Tab;
if (/* there is something else to focus */)
{
// focus it
return true;
}
return false;
}
My problem was that the Tab virtual key wasn't being received in the KeyUp event, while other keys (Esc,Enter, alphanumeric keys) were. I just changed it to a KeyDown with the same handler. The difference seems to be imperceptible to the user.
Related
I have a situation where I'm provided with a WinForms TextBox instance which I want to attach autocomplete functionality to.
I've got the autocomplete (string matching + dropdown) all figured out and it works reliable so far.
What is the ability to navigate the dropdown with the keyboard (as is the norm with this sort of UI).
The natural solution would be to handle KeyDown (or somesuch) event for the textbox and moving the selection in the dropdown accordingly.
However, it happens that to do this, you need to override the IsInputKey() event to allow capture of arrow key events. The alternative is to override ProcessCmdKey() and handle the event there. The problem with these two is that I cannot override anything since I can't replace the textbox instance.
Edit: Let's assume I have the code below:
void _textBox_KeyDown(object sender, KeyEventArgs e)
{
if (_dropdown.Visible)
{
// TODO The stuff below fails because we need to either handle ProcessCmdKey or override IsInputKey
switch (e.KeyCode)
{
case Keys.Tab:
{
// click selected item
_dropdown.Items[GetSelectedItemIndex()].PerformClick();
break;
}
case Keys.Down:
{
// select next (or first) item
int i = GetSelectedItemIndex() + 1;
if (i >= _dropdown.Items.Count) i = 0;
_dropdown.Items[i].Select();
break;
}
case Keys.Up:
{
// select previous (or last) item
int i = GetSelectedItemIndex() - 1;
if (i < 0) i = _dropdown.Items.Count - 1;
_dropdown.Items[i].Select();
break;
}
}
}
}
Them problem with the code above is that it is never called. The event is never triggered for arrow keys. More info: Up, Down, Left and Right arrow keys do not trigger KeyDown event
I hope i haven't missunderstood you, but is this a solution:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
// Place logic for textbox here
}
}
I'd use a KeyDown event on the form and then compare the keycode with the Keys.Down keycode
Not working
see here: Up, Down, Left and Right arrow keys do not trigger KeyDown event
I may not be understanding your question entirely, but wouldn't an approach like this work?
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
comboBox1.Text = //results of your matching algorithm.
}
private void textBox1_Validated(object sender, EventArgs e)
{
textBox1.Text = (string) comboBox1.Text;
}
I've 5 buttons in my windows application. When I click arrow keys the focus changing between buttons, then only
KeyUp
event firing. How to stop this?
Subscribe to the PreviewKeyDown event instead.
Occurs before the KeyDown event when a key is pressed while focus is on this control.
As you move through the buttons, the sender parameter will contain the previously selected button.
I found a solution that should work for you, adapted from here. Apparently, MS made the decision that the arrow keys wouldn't trigger the KeyDown event, so you can't cancel them.
One workaround is to specify that your arrow keys are normal input keys, like any other key. Then the KeyDown event will fire and you can cancel the button press if you want.
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right || e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)
e.IsInputKey = true;
}
private void button1_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
You may want to read the other answers and comments in that post to see what would work best in your situation.
Answer for your question in comment
void button1_LostFocus(object sender, EventArgs e)
{
button1.Focus();
}
To prevent Up from moving focus from a Button you have to utilize at least 3 methods:
bool _focus;
private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Up)
_focus = true;
}
private void button1_KeyUp(object sender, KeyEventArgs e)
{
_focus = false;
}
private void button1_Leave(object sender, EventArgs e)
{
if(_focus)
button1.Focus(); // or (sender as Control)
}
Trick is to use flag when user press Up and to return focus in Leave. You have to unflag in KeyUp, otherwise it would be impossible to change focus (by pressing Tab to example).
You could possible unflag in Leave, I didn't test it.
I am checking the keycode for a textbox, and I want a certain task to be performed when the user presses Enter.
It has been working perfectly, but the task that I am trying to perform now, usually is done using a mouse click. So on the OK on that task (a FolderBrowserDialog), it keeps calling the dialog control.
Oddly enough, even though the debugger shows me into the if branch, for e it shows {KeyData = LButton|MButton|Back}, but KeyValue is still 13...
I think it may be that the textbox remembers its last entry... True ?
In my troubleshooting, I have added a boolean variable so I only go into FolderBrowser when it is true, I have tried to add and delete a space from the textbox after the Browse, and even clear the textbox... Each attempt seemed to make things worse.
It seemed that I was in a quasi-infinite loop - yet it would go away after lots of "ok"'s, and stepping through, I found that for every letter I type in the textbox, I spend 4 to 5 rounds in the CheckKeys. I don't understand why... Or how to fix it.
I added a "e.Handled" which did me no good.
Here's the code:
private void txtDir_TextChanged(object sender, EventArgs e)
{
this.txtDir.KeyUp += new System.Windows.Forms.KeyEventHandler(CheckKeys);
}
private void CheckKeys(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (sender == txtDir && txtDir.Text != "" && System.IO.Directory.Exists(txtDir.Text))
{
btnBrowse_Click(this, e);
}
}
}
Why am I going through this check so many times ? Can I add a different test ? Am I doing something wrong ? (nothing is set as default action, for form or textbox...)
Thank you.
In your code you added handler for KeyUp at TextChanged Event. so, When TextChanged new handler will be added for KeyUp Event. Thats why multiple time each letter is checked. put Handler at Form load event.
e.g. If I have entered five letter in TextBox so, 5 Handler will be added for KeyUp. means Number of KeyUp Event Handler equals to number of time TextChanged Event called.
this.txtDir.KeyUp += new System.Windows.Forms.KeyEventHandler(CheckKeys);
this add new handler for KeyUp event. So, when this line execute new handler will be added.
By putting Handler at Form Load event, you can solve multiple letter checked problem.
Try with,
private void FormLoad(object sender, EventArgs e)
{
this.txtDir.KeyUp += new System.Windows.Forms.KeyEventHandler(CheckKeys);
}
private void CheckKeys(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (sender == txtDir && txtDir.Text != "" && System.IO.Directory.Exists(txtDir.Text))
{
btnBrowse_Click(this, e);
}
}
}
And one more thing As I understand your code, you want to execute btnBrowse_Click if Enter pressed in TextBox control. But Enter key not handled with KeyUp event you need KeyDown Event handler to handle Enter key.
Code:
private void FormLoad(object sender, EventArgs e)
{
this.txtDir.KeyDown += new System.Windows.Forms.KeyEventHandler(CheckKeys);
}
private void CheckKeys(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (sender == txtDir && txtDir.Text != "" && System.IO.Directory.Exists(txtDir.Text))
{
btnBrowse_Click(this, e);
}
}
}
Use KeyDown event instead of TextChanged and write down e.Handle = True in it.
write down following code in your textBox.KeyDown event:
if (e.KeyCode == Keys.Enter)
{
if (sender == txtDir && txtDir.Text != "" && System.IO.Directory.Exists(txtDir.Text))
{
e.Hanlde = true; //it will be close enter keydown handling at this time
btnBrowse_Click(this, e);
}
}
Replaced TextChanged with KeyDown directly and it works again !
(on a side note, I still don't understand why each letter is tested several times...)
Edit: Now after reading the accepted answer, I do understand...
I want to handle LostFocus event of TextBox to perform some actions. But I'm also wouldn't want to perform that actions if TextBox lost it focus because special Button (which opens OpenFileDialog) was clicked or Key.Enter was catched. When Key.Enter was pressed, first of all KeyDown event is raised. Here my event handler of KeyDown:
public void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e != null && sender != null)
if (e.Key == System.Windows.Input.Key.Enter && !String.IsNullOrWhiteSpace(((TextBox)sender).Text))
{
e.Handled = true;
isEnterClicked = true;
((System.Windows.Controls.TextBox)sender).Visibility = System.Windows.Visibility.Collapsed;
}
}
After Key.Enter was pressed, TextBox.Visibility is changed, and this operator will raise LostFocus event.
public void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
try
{
if (!isEnterClicked)
{
DependencyObject dob = (DependencyObject)sender;
while ( !(dob is ItemsControl))
{
dob = VisualTreeHelper.GetParent(dob);
}
dynamic myCmd = dob.GetValue(Control.DataContextProperty);
myCmd.SomeCommand.Execute(((TextBox)sender).GetValue(Control.DataContextProperty));
}
}
finally
{
isEnterClicked = false;
}
}
LostFocus handler firstly watch whether isEnterPressed equals to false, its mean, TextBox lost it focus not because enter was pressed. SomeCommand will delete some item which was bind to TextBox, and it will disappear.
Q: So, how to do the same with Button.Click event?
First of all, BEFORE Button clicked, TextBox lost it focus. The same way is not acceptable. Button.Focusable="False", creating new ControlTemplate or handling Timer.Elapsed event do not satisfy my requirements.
If I understood problem correctly, try to check if button is focused, if so dont perform actions in textbox lostfocus event. If Iam correct button should be focused before textbox lostfocus event raised.
if (!isEnterClicked && !button.Focused)
{
//do stuff
}
Despite me working with C# (Windows Forms) for years, I'm having a brain fail moment, and can't for the life of me figure out how to catch a user typing Ctrl + C into a textbox.
My application is basically a terminal application, and I want Ctrl + C to send a (byte)3 to a serial port, rather than be the shortcut for Copy to Clipboard.
I've set the shortcuts enabled property to false on the textbox. Yet when the user hits Ctrl + C, the keypress event doesn't fire.
If I catch keydown, the event fires when the user presses Ctrl (that is, before they hit the C key).
It's probably something stupidly simple that I'm missing.
Go ahead and use the KeyDown event, but in that event check for both Ctrl and C, like so:
if (e.Control && e.KeyCode == Keys.C) {
//...
e.SuppressKeyPress = true;
}
Also, to prevent processing the keystroke by the underlying TextBox, set the SuppressKeyPress property to true as shown.
Key events occur in the following order:
KeyDown
KeyPress
KeyUp
The KeyPress event is not raised by noncharacter keys; however, the noncharacter keys do raise the KeyDown and KeyUp events.
Control is a noncharacter key.
You can check with this line of code:
if (e.KeyData == (Keys.Control | Keys.C))
I had a problem catching Ctrl + C on a TextBox by KeyDown. I only got Control key when both Control and C were pressed. The solution was using PreviewKeyDown:
private void OnLoad()
{
textBox.PreviewKeyDown += OnPreviewKeyDown;
textBox.KeyDown += OnKeyDown;
}
private void OnPreviewKeyDown( object sender, PreviewKeyDownEventArgs e)
{
if (e.Control)
{
e.IsInputKey = true;
}
}
private void OnKeyDown( object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.C) {
textBox.Copy();
}
}
D'oh! Just figured it out. Out of the three possible events, the one I haven't tried is the one I needed! The KeyUp event is the important one:
private void txtConsole_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == (Keys.C | Keys.Control))
{
_consolePort.Write(new byte[] { 3 }, 0, 1);
e.Handled = true;
}
}
If you want to catch such combinations of keys in KeyPress Event look at this table here:
http://www.physics.udel.edu/~watson/scen103/ascii.html
in Non-Printing Characters section you can see the Dec numbers for each combination.
For example, Dec number for Ctrl + C is 3. So you can catch it in KeyPress Event like this:
private void btnTarget_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar != 3) // if it is not Ctrl + C
{
// do something
}
}
Try the following: capture the up arrow and down arrow events. When you detect down arrow for CTRL, set a flag; when you detect up arrow, reset the flag. If you detect the C key while the flag is set, you have Ctrl+C.
Edit. Ouch... Jay's answer is definitely better. :-)
I don't know if it's because some change in newer version or because I am trying to use this on ListBox, but there is no e.Control in KeyEventArgs e that I get from KeyDown.
I had to work around solution, I came up with this (it's not the prettiest one, but it works fine):
private List<Key> KeyBuff = new List<Key>();
private void ListBox_KeyDown(object sender, KeyEventArgs e)
{
if (!KeyBuff.Exists(k => k == e.Key))
KeyBuff.Add(e.Key);
if (KeyBuff.Exists(k => k == Key.LeftCtrl || k == Key.RightCtrl) &&
KeyBuff.Exists(k => k == Key.C))
{
// Desired detection
Clipboard.SetText(SelectedText);
}
}
private void ListBox_KeyUp(object sender, KeyEventArgs e)
{
KeyBuff.Clear();
}
For me, it's not working with KeyDown event so I tried with PreviewKeyDown and it's worked.
private void txt_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.Control == true && e.KeyCode == Keys.C)
{
Clipboard.SetText(txt.SelectedText);
}
}