Explanation of Insert Key in WPF - c#

I am trying to notify the user when some Keys are pressed. If Caps, Num, Scroll and Insert are pressed, the relative BarStaticItem (the application uses some devexpress controls) changes color to White.
void DxMainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.CapsLock)
{
if (e.KeyboardDevice.IsKeyToggled(Key.CapsLock))
{
bCaps.Tag = new SolidColorBrush(Colors.White);
}
else
{
bCaps.Tag = new SolidColorBrush(Colors.DarkGray);
}
}
if (e.Key == Key.NumLock)
{
if (e.KeyboardDevice.IsKeyToggled(Key.NumLock))
{
bNum.Tag = new SolidColorBrush(Colors.White);
}
else
{
bNum.Tag = new SolidColorBrush(Colors.DarkGray);
}
}
if (e.Key == Key.Scroll)
{
if (e.KeyboardDevice.IsKeyToggled(Key.Scroll))
{
bScrl.Tag = new SolidColorBrush(Colors.White);
}
else
{
bScrl.Tag = new SolidColorBrush(Colors.DarkGray);
}
}
if (e.Key == Key.Insert)
{
if (e.KeyboardDevice.IsKeyToggled(Key.Insert))
{
bIns.Tag = new SolidColorBrush(Colors.White);
}
else
{
bIns.Tag = new SolidColorBrush(Colors.DarkGray);
}
}
}
Everything works fine but when i press Insert inside a TextBox it does not work. The weird thing is that in dispute of the other KeyEvents the event for Insert is like never happening (breakpoint does not break), even if the functionality works fine (overwrite text when pressed etc).
Can someone explain me why?
Thanks.

The Insert key is handled by the TextBox control, so that the event is not routed up the UI element tree.
You may however attach a handler for the PreviewKeyDown event instead of KeyDown:
<Window ... PreviewKeyDown="DxMainWindow_KeyDown">
Please refer to the Routed Events Overview article on MSDN for more details.

Related

Handle navigation keys in TextBox inside DataGridView

We have a DataGridView with data in a form. To enable quick search, we added TextBox to DataGridView.Controls and highlight cells which contain text from TextBox.
However, there is an issue. DataGridView consumes the Left arrow ←, Right arrow →, Home and End (with or without Shift) keys even if the cursor is in TextBox, and the user cannot change the caret position or select text from the keyboard.
TextBox generates a PreviewKeyDown event and nothing more happens.
Simplified code:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
Width = 400;
Height = 400;
var txt = new TextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
var dgv = new DataGridView
{
Dock = DockStyle.Fill,
ColumnCount = 3,
RowCount = 5
};
dgv.Controls.Add(txt);
Controls.Add(dgv);
dgv.PreviewKeyDown += DgvOnPreviewKeyDown;
dgv.KeyDown += DgvOnKeyDown;
txt.PreviewKeyDown += TxtOnPreviewKeyDown;
txt.KeyDown += TxtOnKeyDown;
}
private void DgvOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key Preview {0}", e.KeyCode));
e.IsInputKey = true;
}
private void DgvOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key {0}", e.KeyCode));
}
private void TxtOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key Preview {0}", e.KeyCode));
}
private void TxtOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key {0}", e.KeyCode));
}
}
Type 123 in TextBox and then try the Right arrow, Left arrow, End, or Home. DataGridView change the selected cell, but the TextBox caret doesn't move.
TextBox works just fine if not inside a DataGridView (no problem at all when using the same method adding it into TreeView for example). TextBox acts similar to the Quick search Panel in the browser and has to be on top of the DataGridView. Adding a TextBox to a Form (or to be more specific, to a DataGridView parent) creates its own set of issues (tracking Location, Size, Visibility, ...) and is not acceptable.
What can be done to make sure that TextBox receive those keys and change the caret position or select text?
TextBox works just fine if not inside DataGridView (no problem at all when using the same method adding it into TreeView for example)
Apparently the problem is in DataGridView. It's because DataGridView overrides the Control.ProcessKeyPreview method:
This method is called by a child control when the child control receives a keyboard message. The child control calls this method before generating any keyboard events for the message. If this method returns true, the child control considers the message processed and does not generate any keyboard events.
The DataGridView implementation does just that - it maintains zero or one child controls internally (EditingControl), and when there is no such control active, it handles many keys (navigation, tab, enter, escape, etc.) by returning true, thus preventing the child TextBox keyboard events generation. The return value is controlled by the ProcessDataGridViewKey method.
Since the method is virtual, you can replace the DataGridView with a custom derived class which overrides the aforementioned method and prevents the undesired behavior when neither the view nor the view active editor (if any) has the keyboard focus.
Something like this:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
}
The above is just the half of the story and solves the cursor navigation and selection keys issue. However DataGridView intercepts another key message preprocessing infrastructure method - Control.ProcessDialogKey and handles Tab, Esc, Return, etc. keys there. So in order to prevent that, the method has to be overridden as well and redirected to the parent of the data grid view. The later needs a little reflection trickery to call a protected method, but using one time compiled delegate at least avoids the performance hit.
With that addition, the final custom class would be like this:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (SuppressDataGridViewKeyProcessing)
{
if (Parent != null) return DefaultProcessDialogKey(Parent, keyData);
return false;
}
return base.ProcessDialogKey(keyData);
}
static readonly Func<Control, Keys, bool> DefaultProcessDialogKey =
(Func<Control, Keys, bool>)Delegate.CreateDelegate(typeof(Func<Control, Keys, bool>),
typeof(Control).GetMethod(nameof(ProcessDialogKey), BindingFlags.NonPublic | BindingFlags.Instance));
}
You can try this.
I created my own textbox and overrode method ProcessKeyMessage.
public class MyTextBox : TextBox
{
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
protected override bool ProcessKeyMessage(ref Message m)
{
if (m.Msg != WM_SYSKEYDOWN && m.Msg != WM_KEYDOWN)
{
return base.ProcessKeyMessage(ref m);
}
Keys keyData = (Keys)((int)m.WParam);
switch (keyData)
{
case Keys.Left:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.ShiftKey:
return base.ProcessKeyEventArgs(ref m);
default:
return base.ProcessKeyMessage(ref m);
}
}
}
And then you can call:
var txt = new MyTextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
Try to just add the TextBox to the main form instead of the DataGridView:
Controls.Add(txt);
Controls.Add(dgv);
txt.PreviewKeyDown += DgvOnPreviewKeyDown;
txt.KeyDown += DgvOnKeyDown;
txt.PreviewKeyDown += TxtOnPreviewKeyDown;
txt.KeyDown += TxtOnKeyDown;
It sounds a bit like an exercise in futility.
It may be easier to encapsulate the behavior of both the TextBox and DataGridView controls by placing them into a UserControl together with a little code to handle events.
Here is a partial solution to the issue. TextBox still doesn't receive navigation keys input natively, but I reproduced a normal caret and selection behavior.
PreviewKeyDownEventArgs contains information about the pressed key and modifiers (Shift). For each key combination I set a new SelectionStart and SelectionLength for the TextBox.
private void TxtOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
TextBox txt = (TextBox)sender;
if (e.KeyCode == Keys.Home)
{
int idx = txt.SelectionStart;
txt.SelectionStart = 0;
txt.SelectionLength = e.Shift ? idx : 0;
}
else if (e.KeyCode == Keys.End)
{
int idx = txt.SelectionStart;
if (e.Shift)
txt.SelectionLength = txt.TextLength - idx;
else
{
txt.SelectionStart = txt.TextLength;
txt.SelectionLength = 0;
}
}
else if (e.KeyCode == Keys.Left)
{
if (e.Shift)
{
if (txt.SelectionStart > 0)
{
txt.SelectionStart--;
txt.SelectionLength++;
}
}
else
{
txt.SelectionStart = Math.Max(0, txt.SelectionStart - 1);
txt.SelectionLength = 0;
}
}
else if (e.KeyCode == Keys.Right)
{
if (e.Shift)
txt.SelectionLength++;
else
{
txt.SelectionStart = Math.Min(txt.TextLength, txt.SelectionStart + 1);
txt.SelectionLength = 0;
}
}
}

How to detect control + v for pasting

I am new to C#. I am using the following code to detect Ctrl+v when pressed on the keyboard:
while(true)
{
bool check = (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl));
if (check && Keyboard.IsKeyDown(Key.V))
{
if (Clipboard.ContainsText())
history.Dispatcher.Invoke(new invoke_method2(update2),
new object[] { Clipboard.GetText(), history });
}
}
The program is running in the background. The problem is, it works when the user presses Ctrl and then v. But the conditions also stand true if the user presses v and then Ctrl, which is an unwanted trigger. Is there a way to overcome it?
To capture a shortcut in a window in WPF, implement a KeyDown event, so creating a new thread isn't necessary:
public MainWindow()
{
InitializeComponent();
KeyDown += MainWindow_KeyDown;
}
void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
if (e.Key == Key.V)
{
}
}
}
Edit:
If you want to go with your solution, then you're practically searching for a point in time when V isn't pressed, but Ctrl is, so the following works:
while (true)
{
if (!Keyboard.IsKeyDown(Key.V))
{
while (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
if (Keyboard.IsKeyDown(Key.V))
{
}
}
}
}

Selecting dynamically created listboxs items in C#

I dynamically created a Listbox and filled it with some items, Upon typing a dot in a Textbox i want to show the Listbox so that the user can select any item by using arrow keys .
I did everything up to this point. When the user types a dot in the Textbox, The Listbox gets shown, But the arrow keys wont select any items!
private void txtResults_KeyDown(object sender, KeyEventArgs e)
{
string[] words= ((TextBox)sender).Text.Split(' ');
string s = sampleWord.Text = words[words.Length - 1];
if (e.KeyCode == Keys.OemPeriod)
{
ShowPopUpList(s);
}
else if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
{
lst.Focus();//doesnt work :-/
}
else
{
lst.Hide();
txtResults.Focus();
}
}
This is the code for creating the listbox on FormLoad()
private void CreateListBox()
{
lst = new ListBox();
lst.Size = new Size(70, 130);
lst.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
lst.KeyDown += lst_KeyDown;
lst.DoubleClick += lst_DoubleClick;
//adding some test input
lst.Items.Add("بسم");
lst.Items.Add("الله ");
lst.Items.Add("الرحمن ");
lst.Items.Add("الرحیم ");
lst.Items.Add("بنام ");
lst.Items.Add("خداوند ");
lst.Items.Add("بخشنده ");
lst.Items.Add("مهربان ");
lst.Items.Add("الهی شکرت ");
}
private void ShowListbox()
{
txtResults.SelectionStart = txtResults.Text.Length;
txtResults.SelectionLength = 0;
Point index = txtResults.GetPositionFromCharIndex(txtResults.SelectionStart-1);
lst.Location = new Point (index.X-50, index.Y+70);
this.Controls.Add(lst);
lst.BringToFront();
lst.Show();
}
In ShowPopUpList(s) the ShowListbox() method is called. nothing fancy about it!
Note:
I only need the list box to get focus when i use UP or DOWN arrow keys to explicitly select an item. unless then i need to be able to freely continue typing and dont lose focus to listbox.
Whats the way around doing it ?
Remove focus from the textbox keydown handler and place it here:
private void ShowPopUpList(string s)
{
//your initialization of the
//listbox here and after..
listBox1.Focus();
}
Another way:
if (e.KeyCode == Keys.OemPeriod)
{
ShowPopUpList(s);
listBox1.Focus();
}
The real big difference is it gets focus rightaway.with your old code would first check the keydown and on the second hit it would already contain the focus.
Final Edit:
If Listbox needs to get the focus on up/down arrow keys(and only with those keys):
if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
{
ShowPopUpList();
listBox1.Focus();
listBox1.SelectedIndex = 0;
}
This worked just fine and dandy for me :)
ListBox lb;
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.Text.Contains("."))
{
lb = new ListBox();
lb.Location = textBox1.Location;
this.Controls.Add(lb);
lb.Items.Add("Item 1");
lb.Items.Add("Item 2");
lb.Items.Add("Item 3");
lb.Show();
}
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
lb.Focus();
}
}

How do I simulate a Tab key press when Return is pressed in a WPF application?

In a WPF application, i have a window that has a lot of fields.
When the user uses the TAB key after filling each field, windows understands that it moves on to the next. This is pretty know behavior.
Now what I want to to, is make it simulate the TAB key, when in fact the RETURN gets hit.
So in my WPF xaml I added imply KeyDown="userPressEnter"
And in the code behind it:
private void userPressEnter(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
e.Key = Key.Tab // THIS IS NOT WORKING
}
}
Now, obviously this is not working. But what I don't know is, how DO I make this work?
EDIT 1 ==> FOUND A SOLUTION
I found something that helped me out =)
private void userPressEnter(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
MoveFocus(request);
}
}
This way the Focus moves on the the next it can find :)
You can look at a post here:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/c85892ca-08e3-40ca-ae9f-23396df6f3bd
Here's an example:
private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
((TextBox)sender).MoveFocus(request);
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
System.Diagnostics.Debug.WriteLine(keyData.ToString());
switch (keyData)
{
case Keys.Enter:
SendKeys.Send("{TAB}");
break;
}
base.ProcessDialogKey(keyData);
return false;
}
How about make SendKeys Class Working like Winforms.SendKeys
https://michlg.wordpress.com/2013/02/05/wpf-send-keys/
public static class SendKeys
{
public static void Send(Key key)
{
if (Keyboard.PrimaryDevice != null) {
if (Keyboard.PrimaryDevice.ActiveSource != null) {
var e1 = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key) { RoutedEvent = Keyboard.KeyDownEvent };
InputManager.Current.ProcessInput(e1);
}
}
}
}
I think you should use that to simulate TAB :
SendKeys.Send("{TAB}");
Instead of
e.Key = Key.Tab
Sources : http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
SendKeys.Send or SendKeys.SendWait will not work in a WPF application, so to answer the original question
if (e.Key == Key.Return)
{
KeyEventArgs tabPressEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Tab) { RoutedEvent = Keyboard.KeyDownEvent };
InputManager.Current.ProcessInput(tabPressEventArgs);
}
Use Method SelectNextControl of your Form
I think the best solution is:
var ue = e.OriginalSource as FrameworkElement;
if (e.Key == Key.Return)
{
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}

Find active textbox WPF

I am creating a entry page for kiosk device using WPF. There a 3 text boxes in the page and keyboard(created using buttons). To perform the action when we press the key board button in want to display the text in the corresponding text box.
Need: How to find the currently focused text box.
code using:
void buttonElement_Click(object sender, RoutedEventArgs e)
{
// create variable for holding string
String sendString = "";
try
{
// stop all event handling
e.Handled = true;
Button btn = ((Button)sender);
// set sendstring to key
if (btn.Content.ToString().Length == 1 && btn.CommandParameter.ToString() != btn.Content.ToString())
{
sendString = btn.Content.ToString();
}
else
{
sendString = btn.CommandParameter.ToString();
}
// sendString = ((Button)sender).CommandParameter.ToString();
int position = txtAuto.SelectionStart;
// if something to send
if (!String.IsNullOrEmpty(sendString))
{
// if sending a string
if (sendString.Length > 1)
{
switch (sendString)
{
case "Del":
if (position != txtAuto.Text.Length)
{
txtAuto.Text = txtAuto.Text.Remove(position, 1);
txtAuto.SelectionStart = position;
}
break;
case "BACKSPACE":
if (position != 0)
{
txtAuto.Text = txtAuto.Text.Remove(position - 1, 1);
txtAuto.SelectionStart = position;
}
break;
case "Clear":
txtAuto.Text = string.Empty;
break;
case "ENTER":
popup.IsOpen = false;
// lbSuggestion.ItemsSource = null;
this.FetchSearchResult(txtAuto.Text.Trim());
if (lbResult.Items.Count != 0)
{
lbResult.ScrollIntoView(lbResult.Items[0]);
}
break;
}
}
else
{
txtAuto.Text = txtAuto.Text.Insert(txtAuto.SelectionStart, sendString);
txtAuto.SelectionStart = position + 1;
}
// set keyboard focus
System.Windows.Input.Keyboard.Focus(this.txtAuto);
// set normal focus
this.txtAuto.Focus();
}
}
catch (Exception)
{
// do nothing - not important for now
Console.WriteLine("Could not send key press: {0}", sendString);
}
}
This code is working fine for single textbox how to make it work for other textboxes.
Normaly the focus got lost if you click a button. So you can "save" the last focused text box in a class variable if a textbox is loosing the focus.
private TextBox _currentTextbox;
private void TextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
_currentTextbox = e.Source as TextBox;
}
attach this handler to all Text boxes and use _currentTextbox in your function.
See more at http://msdn.microsoft.com/en-us/library/aa969768.aspx
Need: How to find the currently focused text box.
You can use the FocusManager.GetFocusedElement method.
When you click the button, the button receives the focus and the textbox loses it. So, one approach would be to subscribe to the LostFocus events of all textboxes and remember which one lost the focus. The one that lost the focus last is the one that lost the focus because of the button click and hence was the one that had the focus before the click.

Categories

Resources