I am looking to validate an input to a DataGridView what has DataGridViewTextBoxCells. The textbox cells can be either signed or unsigned and int or double based on the column in focus. The issue that I am having is determining the caret position when a key is pressed.
For example if the cell allows signed doubles(caret <=> '^'):
a valid keypress event : ^12.3456 with a key press of '-' would give -12.3456
an invalid keypress event: 12.34-56 with a key press of '-' would give 12.34-56
I have not been able to find anything that will allow me to access the caret position at the time of a keypress.
private void SomeGridView_KeyPress(object sender, KeyPressEventArgs e)
{
DataGridView DGV = SomeGridView;
string curStr;
bool isFirst = DGV.CurrentCell.EditedFormattedValue == null;
curStr = isFirst ? "" : DGV.CurrentCell.EditedFormattedValue.ToString();
Type type = DGV.CurrentCell.GetType();
if (DGV.CurrentCell.GetType() == typeof(DataGridViewTextBoxCell))
{
DataGridViewTextBoxCell DGVTB = (DataGridViewTextBoxCell)DGV.CurrentCell;
//Not sure how to get caret here
}
switch ((GridDataEnum)DGV.CurrentCell.ColumnIndex)
{
case GridDataEnum.setpoint:
case GridDataEnum.SlopePoint:
e.Handled = !HelperUtils.isValidNumber(curStr, e.KeyChar, HelperUtils.TargetNumberTypeEnum.signedDouble);
break;
case GridDataEnum.lowerX:
case GridDataEnum.upperX:
case GridDataEnum.TransX:
case GridDataEnum.constY:
e.Handled = !HelperUtils.isValidNumber(curStr, e.KeyChar, HelperUtils.TargetNumberTypeEnum.unsignedDouble);
break;
}
}
Thanks
In case anyone else is looking for it I found the answer thanks to LarsTech.
private void SomeGridView_KeyPress(object sender, KeyPressEventArgs e)
{
DataGridView DGV = SomeGridView;
string curStr;
bool isFirst = DGV.CurrentCell.EditedFormattedValue == null;
curStr = isFirst ? "" : DGV.CurrentCell.EditedFormattedValue.ToString();
Type type = DGV.CurrentCell.GetType();
if (DGV.CurrentCell.GetType() == typeof(DataGridViewTextBoxCell))
{
DataGridViewTextBoxCell DGVTB = (DataGridViewTextBoxCell)DGV.CurrentCell;
if (DGV.CurrentCell.EditType == typeof(DataGridViewTextBoxEditingControl))
if(DGV.EditingControl != null)
charIndex = ((TextBox)DGV.EditingControl).SelectionStart;
}
switch ((GridDataEnum)DGV.CurrentCell.ColumnIndex)
{
case GridDataEnum.setpoint:
case GridDataEnum.SlopePoint:
e.Handled = !HelperUtils.isValidNumber(curStr, e.KeyChar, HelperUtils.TargetNumberTypeEnum.signedDouble, charIndex);
break;
case GridDataEnum.lowerX:
case GridDataEnum.upperX:
case GridDataEnum.TransX:
case GridDataEnum.constY:
e.Handled = !HelperUtils.isValidNumber(curStr, e.KeyChar, HelperUtils.TargetNumberTypeEnum.unsignedDouble, charIndex);
break;
}
}
Related
Within our application, there is a usercontrol, which allows to enter a number (inside a numericupdown) followed by a combobox which allows to select the SI-Prefix ([p, n, µ, m, -, k, M, G, T] small to big)
Now, for easier usage, I thought it would be nice to capture the KeyPress-Event on the numericUpDown and set the combobox accordingly. (If m is pressed, select mili (m), if G is pressed, select Giga (G))
This works flawless with the following handler / selector:
private void numericUpDown1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsLetter(e.KeyChar))
{
//Check, if its valid for si prefixes.
if (this.siSelector1.TrySelect(e.KeyChar)
{
e.Handled = true;
}
}
}
Where TrySelect does nothing but the following:
public Boolean TrySelect(Char chr)
{
var entry = this.comboBox_siPrefix.Items.Cast<KeyValuePair<String,Double>>().Where(e => e.Key.Contains("(" + chr + ")")).FirstOrDefault();
if (!entry.Equals(new KeyValuePair<String, Double>()))
{
this.comboBox_siPrefix.SelectedItem = entry;
return true;
}
return false;
}
That's fine, but everytime the user hears a "BING" whenever a non-numeric Key is pressed on the numericupdown.
I read about e.SuppressKeyPress, which unfortunately isn't available with KeyPressEventArgs - it's only available for KeyEventArgs.
So, trying the whole thing with the KeyDown-Event works. (No "BING") - but I wasn't able to capture capital keys, as every KeyDown will fire the Event immediately...
private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
{
KeysConverter kc = new KeysConverter();
if (char.IsLetter(kc.ConvertToString(e.KeyCode)[0]))
{
//Check, if its valid for si prefixes.
if (this.siSelector1.TrySelect(kc.ConvertToString(e.KeyCode)[0]))
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
}
Any Ideas?
Figured out a way:
When Using KeyDown-Event, you can use e.Modifiers to check, if another key is down'd at the same time.
I dunno why, but for the KeyDown-Event e.KeyValue as well as e.KeyCode always return the CAPITAL version of the key.
So, I modified the handler to convert every char to lower-case, and only convert it to upper-Case if SHIFT is pressed at the same time:
Works - no "BING" (for valid SI-Prefixes). :-)
private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
{
KeysConverter kc = new KeysConverter();
char c = char.ToLower(kc.ConvertToString(e.KeyValue)[0]);
if (char.IsLetter(c))
{
//Caps?
if (e.Modifiers == Keys.Shift)
{
c = char.ToUpper(c);
}
//Check, if its valid for si prefixes.
if (this.siSelector1.TrySelect(c))
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
}
The above solution does not apply for µ (CTRL+ALT+m or ALT GR+m)
Update: This is not 100%, but I didn't get it by now:
IF the ALT-Key is pressed, The Char-Code always reports a "M".
Update 2:
So, I had to exclude the "m" from beeing matched with char.isLetter() (if alt is pressed) and finally add another check.
I found, that comparing e.KeyValue==77 worked as expected, while comparing c=='m' didn't... (then µ was inserted into the numericupdown, which it shouldn't)
if (ctrl && alt && c=='m'):
if (ctrl && alt && e.KeyValue==77)
Dunno why - Ideas?
private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
{
KeysConverter kc = new KeysConverter();
char c = char.ToLower(kc.ConvertToString(e.KeyValue)[0]);
Boolean ctrl = e.Control;
Boolean alt = e.Alt;
Boolean shift = e.Shift;
int keyPadStart = (int)Keys.NumPad0;
int keyPadEnd = (int)Keys.NumPad9;
if (e.KeyValue >= keyPadStart && e.KeyValue <= keyPadEnd)
{
//send to numeric updown.
return;
}
if (char.IsLetter(c) && !alt)
{
if (shift) c = char.ToUpper(c);
//Check, if its valid for si prefixes.
if (this.siSelector1.TrySelect(c))
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
//not working: if (ctrl && alt && c=='m')
if (ctrl && alt && e.KeyValue==77)
{
//Check, if its valid for si prefixes.
if (this.siSelector1.TrySelect('µ'))
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
}
Several places in my program, the RadioButton matching the selected item has to be checked, and I have a lot of if statements like so:
DataRowView TempRow = (DataRowView)ScheduleDataGrid.SelectedItem;
if (Convert.ToString(TempRow["Bio"]) == "Bio1")
{
BioRB1.IsChecked = true;
}
if (Convert.ToString(TempRow["Bio"]) == "Bio2")
{
BioRB2.IsChecked = true;
}
if (Convert.ToString(TempRow["Bio"]) == "Bio3")
and so on... I want to replace all this with something short and smart.
I tried using the number of the bio to relate to the button like so:
string bioselected = Convert.ToString(TempRow["Bio"]);
int i = Convert.ToInt16(bioselected.Substring(bioselected.Length - 1, 1));
BioRB[i].IsChecked = true;
but doing a BioRB[i] doesn't work, it ignores the [i] and says BioRB does not exist. Any other suggestions?
BioRB[i] is not doing anything like what you think it's doing. All variable references (controls included) have to be well-defined at compile time - you can't refer to a control's name by building a string that matches the name.**
Try creating a list of your radio buttons. Then you can index into the list:
List<RadioButton> radioButtons = new List<RadioButton>()
{
BioRB1,
BioRB2
};
string bioselected = Convert.ToString(TempRow["Bio"]);
int i = Convert.ToInt16(bioselected.Substring(bioselected.Length - 1, 1));
radioButtons[i].IsChecked = true;
** Technically you can do this via reflection, but it's far more complex than what you've tried.
Maybe this will look better:
string caseSwitch = Convert.ToString(TempRow["Bio"]);
switch (caseSwitch)
{
case "Bio1":
BioRB1.IsChecked = true;
break;
case "Bio2":
BioRB2.IsChecked = true;
break;
case "Bio3":
BioRB3.IsChecked = true;
break;
default:
Console.WriteLine("Default case...is optional");
break;
}
Also, try doing what Alybaba726 said and use CellContentClick or something like this:
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if(e.ColumnIndex == dgv.Columns["Bio"].Index)
{
string bioSelected = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
switch (bioSelected)
{
case "Bio1":
BioRB1.IsChecked = true;
break;
case "Bio2":
BioRB2.IsChecked = true;
break;
case "Bio3":
BioRB3.IsChecked = true;
break;
default:
Console.WriteLine("Default case...this is optional");
break;
}
}
}
I need a validation to check whether key pressed down is numeric or not. I tried with different code but they cant help me out.In the textbox if the user press Shift+numbers it displays special characters like !,#,#...... I need to validate the Shift + key down event.
//Code
private void txtNumericTextbox_KeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.Key < Key.D0 || e.Key > Key.D9)
{
if (e.Key < Key.NumPad0 || e.Key > Key.NumPad9)
{
if (e.Key != Key.Back)
{
txtNumericTextbox_.BorderBrush = new SolidColorBrush(Colors.Red);
lblErrorMessage.Visibility = Visibility.Visible;
lblErrorMessage.Text = "Please Enter Numbers Only";
}
else
{
txtNumericTextbox_.BorderBrush = new SolidColorBrush(Colors.DarkGray);
lblErrorMessage.Visibility = Visibility.Collapsed;
lblErrorMessage.Text = "";
}
}
}
}
}
How can I achieve that?
You can use the ModifierKeys property on control to determine if the shift key is being held.
//Code
Use this to accept only numeric values.
Event you can choose textBox1_KeyDown nonnumberenter = false;
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
if (e.KeyCode != Keys.Back)
{
nonnumberenter = true;
string abc = "Please enter numbers only.";
DialogResult result1 = MessageBox.Show(abc.ToString(), "Validate numbers", MessageBoxButtons.OK);
}
}
}
if (Control.ModifierKeys == Keys.Shift)
{
nonnumberenter = true;
string abc = "Please enter numbers only.";
DialogResult result1 = MessageBox.Show(abc.ToString(), "Validate numbers", MessageBoxButtons.OK);
}
Use this to accept only characters. Event you can choose textBox1_KeyPress
if (Char.IsNumber(e.KeyChar) || Char.IsSymbol(e.KeyChar) || Char.IsWhiteSpace(e.KeyChar) || Char.IsPunctuation(e.KeyChar))
{
MessageBox.Show("Only Char are allowed");
e.Handled = true;
}
Hope this helps.
You can use text box key press event
private void txt_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) )
{
e.Handled = true;
}
}
I was looking for the same thing: Accept numbers only. However, I did not find any e.KeyCode on my _KeyDown event, so I adapted Kumar's code to suit my needs, and am sharing with you, should it be more fitting to you:
Use e.Handled = true to cancel the input of that character.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.NumPad0:
case Key.NumPad1:
case Key.NumPad2:
case Key.NumPad3:
case Key.NumPad4:
case Key.NumPad5:
case Key.NumPad6:
case Key.NumPad7:
case Key.NumPad8:
case Key.NumPad9:
case Key.D0:
case Key.D1:
case Key.D2:
case Key.D3:
case Key.D4:
case Key.D5:
case Key.D6:
case Key.D7:
case Key.D8:
case Key.D9:
break;
default:
e.Handled = true;
break;
}
}
The title is only somewhat accurate. I'm trying to do something special with a textbox and the lack of certain events firing is causing some trouble, but I'd also like some general advice of how to do what it is I'm trying to do here. I'm trying to recreate a special textbox that exists on an old access application. It's for entering in a social security number, and when blank it displays ___-__-____.
When you click on any of the spaces it highlights the particular character. If you enter a number, it replaces the _ with that number. If you hit delete or backspace, it just replaces whatever character is highlighted with an _ or a -, depending.
I can sort of recreate this by having a textbox that is readonly and firing a PreviewMouseUp event to call a method that highlights the current cursor position. But since it's readonly it won't fire any KeyUp or KeyDown events to alter the selection. I can get it to do that if I put a KeyUp in the main UI grid, but it only works on KeyUp, so it winds up looking very laggy. Same issue with the PreviewMouseUp, I want it to highlight when the mouse is pressed down, not up, but PreviewMouseDown fires nothing.
I feel like I'm going about this in a messier and messier way. I've described what I want, does anyone have any better ideas of how to accomplish this that isn't super complicated? I want to keep the textbox readonly to allow me to handle the key inputs manually. What I mean is, My original way of formatting this was to simply run a method on KeyUp which checked the length of what you added and format it appropriately (add dashes, etc), but that results in this moment where everything looks unformatted until you release the key. For example, if I press '2' in the middle of the textbox it shifts all of the dashes over by a character until the '2' button is released, and then the formatting is fixed.
Thoughts?
It's funny how long I'd been working on this for now to finally get it, but the key was in AddHandler. For those of you who want a textbox like this, here's how I did it. There's a few messy bits in here, and those are simply to recreate the exact functionality of the access textbox. The most annoying part was implementing the backspace button, since it deletes what's before the selected section. Also, make sure your textbox is IsReadOnly.
In the constructor put:
textBox_ssn.AddHandler(Control.MouseDownEvent, new MouseButtonEventHandler(ClickSS), true);
textBox_ssn.AddHandler(Control.KeyDownEvent, new KeyEventHandler(ButtonSS), true);
Then use these two methods:
public void ClickSS(object sender, EventArgs e)
{
textBox_ssn.SelectionLength = 1;
}
public void ButtonSS(object sender, KeyEventArgs e)
{
bool on_first_char = false;
if (textBox_ssn.SelectionStart == 0) on_first_char = true;
if (e.Key == Key.Right && textBox_ssn.SelectionStart < 10)
{
++textBox_ssn.SelectionStart;
textBox_ssn.SelectionLength = 1; //Without this, it will move around large blocks of selection
if (textBox_ssn.SelectedText == "-") ++textBox_ssn.SelectionStart;
}
else if (e.Key == Key.Left && textBox_ssn.SelectionStart > 0)
{
--textBox_ssn.SelectionStart;
textBox_ssn.SelectionLength = 1;
if (textBox_ssn.SelectedText == "-") --textBox_ssn.SelectionStart;
}
else
{
string temp = "";
switch (e.Key)
{
case Key.D0:
temp = "0";
break;
case Key.D1:
temp = "1";
break;
case Key.D2:
temp = "2";
break;
case Key.D3:
temp = "3";
break;
case Key.D4:
temp = "4";
break;
case Key.D5:
temp = "5";
break;
case Key.D6:
temp = "6";
break;
case Key.D7:
temp = "7";
break;
case Key.D8:
temp = "8";
break;
case Key.D9:
temp = "9";
break;
case Key.Delete:
temp = "_";
break;
case Key.Back:
temp = "_";
if (textBox_ssn.SelectionStart > 0) --textBox_ssn.SelectionStart;
if (textBox_ssn.SelectedText == "-") --textBox_ssn.SelectionStart;
//else return; //or could do temp = selection text but only if selection length is 1 ectect
break;
}
if (temp != "")
{
if (textBox_ssn.SelectionLength > 1)
{
string underscores = "";
foreach (char c in textBox_ssn.SelectedText)
{
if (c == '-') underscores += "-";
else underscores += "_";
}
textBox_ssn.SelectedText = underscores;
textBox_ssn.SelectionLength = 1;
}
if (textBox_ssn.SelectedText == "-") ++textBox_ssn.SelectionStart;
if (textBox_ssn.SelectionLength == 1)
{
if (!(on_first_char && e.Key == Key.Back)) textBox_ssn.SelectedText = temp;
if (e.Key == Key.Delete) ;
else if (e.Key == Key.Back)
{
if (textBox_ssn.SelectionStart > 0)
{
//--textBox_ssn.SelectionStart;
if (textBox_ssn.SelectedText == "-") --textBox_ssn.SelectionStart;
}
}
else
{
++textBox_ssn.SelectionStart;
if (textBox_ssn.SelectedText == "-") ++textBox_ssn.SelectionStart;
}
}
}
}
}
What you are describing is known as a Masked Textbox. There is a free one in the VisualStudioGallery and in the Extended WPF ToolKit
I have a textbox using UseSystemPasswordChar, so it will not display the password that the user enters. The issue is that the password is still able to be read by something like Spy++. I'm looking for a way to hide this like they do in the password fields in the Services.msc > Log On tab.
Here is what I've got so far.
You can improve this by having some unique events to indicate whether a pressed key has been accepted, if InputFilter or RealText has been changed, etc...
Another great thing to improve would be the default usage of InputFilter, because working with char and Keys doesn't really work for many special keys. For example - at the moment, if you press Alt+F4 when the PasswordBox is in focus, it will type in 's'... So there's a bag of bugs to fix.
And lastly, there's probably a more elegant way to handle capital vs non-capital letters input than what I did there.
So here it is:
public class PasswordBox : TextBox
{
private string _realText;
public string RealText
{
get { return this._realText; }
set
{
var i = this.SelectionStart;
this._realText = value ?? "";
this.Text = "";
this.Text = new string('*', this._realText.Length);
this.SelectionStart = i > this.Text.Length ? this.Text.Length : i;
}
}
private Func<KeyEventArgs, bool> _inputFilter;
public Func<KeyEventArgs, bool> InputFilter
{
get { return this._inputFilter; }
set { this._inputFilter = value ?? (e => true); }
}
public PasswordBox()
{
this.RealText = "";
this.InputFilter = e => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Any(c => c == e.KeyValue);
}
protected override void OnKeyDown(KeyEventArgs e)
{
e.SuppressKeyPress = true;
switch (e.KeyCode)
{
case Keys.Back:
if (this.SelectionStart > 0 || this.SelectionLength > 0)
{
this.RealText = this.SelectionLength == 0
? this.RealText.Remove(--this.SelectionStart, 1)
: this.RealText.Remove(this.SelectionStart, this.SelectionLength);
}
break;
case Keys.Delete:
if (this.SelectionStart == this.TextLength)
{
return;
}
this.RealText = this.RealText.Remove(this.SelectionStart, this.SelectionLength == 0 ? 1 : this.SelectionLength);
break;
case Keys.X:
case Keys.C:
case Keys.V:
if (e.Control)
{
return;
}
goto default;
case Keys.Right:
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Shift:
case Keys.Home:
case Keys.End:
e.SuppressKeyPress = false;
base.OnKeyDown(e);
break;
default:
if (e.Control)
{
e.SuppressKeyPress = false;
base.OnKeyDown(e);
break;
}
if (this.InputFilter(e))
{
var c = (char)e.KeyValue;
if (e.Shift == IsKeyLocked(Keys.CapsLock))
{
c = char.ToLower(c);
}
this.RealText = this.RealText.Remove(this.SelectionStart, this.SelectionLength)
.Insert(this.SelectionStart, c.ToString());
this.SelectionStart++;
}
break;
}
}
}
So try something like this
private string realpass = "";
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (Char) Keys.Back)
realpass += realpass.Substring(0, realpass.Length - 2);
realpass += e.KeyChar.ToString();
textBox1.Text = "";
for (int i = 0; i < realpass.Length; i++)
textBox1.Text += "*";
}
You should not use your own dialog if you intend to collect Windows/domain user credentials. You should use what Windows provides via PInvoke or simply use a wrapper like this,
http://weblogs.asp.net/hernandl/archive/2005/11/21/usercredentialsdialog.aspx
and this,
http://credentials.codeplex.com/