Need more control over a textbox's MouseDown and KeyDown - c#

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

Related

Can't remove text from string using button

I'm trying to make a super simple program where you use 10 different buttons to write in a code (kind of like the code to the door of an appartment complex). All the buttons have this click event:
private void number_click(object sender, EventArgs e)
{
var button = (sender as Button);
if (code.Length == 4)
{
code.Remove(0, 1);
}
switch (button.Name)
{
case "button_1":
code += "1";
break;
case "button_2":
code += "2";
break;
case "button_3":
code += "3";
break;
case "button_4":
code += "4";
break;
case "button_5":
code += "5";
break;
case "button_6":
code += "6";
break;
case "button_7":
code += "7";
break;
case "button_8":
code += "8";
break;
case "button_9":
code += "9";
break;
case "button_0":
code += "0";
break;
}
label1.Text = code;
}
I'm simply trying to make so the number the user presses get added to the code string. When there length of the string reaches 4 it is supposed to remove the first character so that there is never more than 4 characters in the string. For some reason this doesn't seem to work. What am I doing wrong?
The immediate cause of the error is that string is immutable and so
code.Remove(0, 1);
computes result string and throws it away. You should assign the result string back:
...
code = code.Remove(0, 1);
...
You can get rid of long switch case and obtain digit from the button's name:
private void number_click(object sender, EventArgs e) {
var button = (sender as Button);
// Add new digit to the right
code += button.Name[button.Name.Length - 1].ToString();
// Ensure code is at most 5 digits long by removing digits from the left
code = code.Substring(Math.Clamp(code.Length - 5, 0, code.Length));
}
If you want to update the code variable you can use Substring method.
if (code.Length == 4)
{
code = code.Substring(1);
}
Your problem is caused by the fact that System.String instances are immutable. This means you can't modify the string code points to, but you can make it point to another string. The Remove method of System.String doesn't actually remove anything, it just creates a new string without the unwanted characters and returns a reference to it. This means that instead of
code.Remove(0, 1);
You want:
code = code.Remove(0, 1);

getting caret position from DataGridView

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;
}
}

How I can limit the introduced digits in the cell of the datagridview?

I want to create a datagridview that only accept numbers (Integers or doubles). When the number is double, I only want to save 2 digits after the point. (E.g. 1.22) and automatically change the cell. I create an event to make this.
I was able to get a working event but when I put the number with 2 digits after the point, the last digit doesn’t appear in the cell and the cell change.
When I’m debugging with a breakpoint in the line “dgvDatos[column, row].Value = txtInCell;” . The variable txtInCell (is used to see what is written in the cell ) has the correct/complete value .
void dText_KeyPress(object sender, KeyPressEventArgs e)
{
bool dot;
if (txtInCell.Contains(".") == true)
dot = true;
else
dot = false;
//Only accept numbers
if (Char.IsDigit(e.KeyChar) || e.KeyChar == '.' || e.KeyChar == '-')
{
if (e.KeyChar == '.')
if (!dot)
{
txtInCell += e.KeyChar;
e.Handled = false;
}
else
e.Handled = true;
else
txtInCell += e.KeyChar;
if (txtInCell.Contains("."))
{
int row = dgvDatos.CurrentCell.RowIndex;
int column = dgvDatos.CurrentCell.ColumnIndex;
string[] elements = txtInCell.Split('.');
if (elements[1].Length > 1)
{
dgvDatos[column, row].Value = txtInCell;
dgvDatos.CurrentCell = this.dgvDatos[column + 1, row];
}
}
}
else if (Char.IsControl(e.KeyChar)) //Use backspace as control
{
if (e.KeyChar == '\b')
txtInCell = txtInCell.Remove(txtInCell.Length - 1);
e.Handled = false;
}
else
{
//all the other keys are disabled
e.Handled = true;
}
}
Use validate event
dataGridView1.CellValidating += (s, eargs) => eargs.Cancel = !new Regex(#"^\d*(.\d|.\d\d)$").IsMatch(eargs.FormattedValue.ToString());
You can also give a tooltip about why you are not allowing the user to enter the value like you cannot enter more than two decimal digits etc you can also add some coloring flashing to add more spice

How to format textbox to display SSN as the user types

I have a textbox which I would like to format in real time: ###-##-#### as the user types.
I have the following code:
private bool bFlag;
private void tbSSN_TextChanged(object sender, EventArgs e)
{
string str = tbSSN.Text;
if (!bFlag)
{
if (str.Length == 3 || str.Length == 6)
{
tbSSN.Text += "-";
bFlag = true;
tbSSN.SelectionStart = tbSSN.Text.Length;
}
else if (str.Length > 11)
{
tbSSN.Text = tbSSN.Text.Remove(tbSSN.Text.Length - 1);
tbSSN.SelectionStart = tbSSN.Text.Length;
}
}
else
{
bFlag = false;
}
}
The only issue is, when I hit backspace and try to enter the numbers again, it is allowing me to enter this format: #####--#### or ##-#####-##
I know I have to add a KeyPress event to ensure the above issue is resolved, but I am not sure how I would add it.
How would I code the KeyPress event?
It is a WinForm application.
Use MaskedTextBox instead of normal TextBox and define a Mask for your TextBox.
yourMaskedTextBox.Mask = "000-00-0000";

Hide password text

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/

Categories

Resources