Im trying to create a TextBox control and force user to enter only numbers in specyfic format there.
How can I do it in WPF?
I have not found any properties like "TextFormat" or "Format" in TextBox class.
I made TextBox like this (not in visual editor):
TextBox textBox = new TextBox();
I want TextBox behavior like in MS Access forms, (user can put only numbers in that textbox in "000.0" format for example).
Consider using WPF's built in validation techniques. See this MSDN documentation on the ValidationRule class, and this how-to.
What you probably need is a masked input. WPF doesn't have one, so you can either implement it yourself (by using validation, for example), or use one of available third-party controls:
FilteredTextBox from WPFDeveloperTools
MaskedTextBox from Extended WPF Toolkit
etc.
Based on your clarification, you want to limit user input to be a number with decimal points.
You also mentioned you are creating the TextBox programmatically.
Use the TextBox.PreviewTextInput event to determine the type of characters and validate the string inside the TextBox, and then use e.Handled to cancel the user input where appropriate.
This will do the trick:
public MainWindow()
{
InitializeComponent();
TextBox textBox = new TextBox();
textBox.PreviewTextInput += TextBox_PreviewTextInput;
this.SomeCanvas.Children.Add(textBox);
}
Meat and potatoes that does the validation:
void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
// change this for more decimal places after the period
const int maxDecimalLength = 2;
// Let's first make sure the new letter is not illegal
char newChar = char.Parse(e.Text);
if (newChar != '.' && !Char.IsNumber(newChar))
{
e.Handled = true;
return;
}
// combine TextBox current Text with the new character being added
// and split by the period
string text = (sender as TextBox).Text + e.Text;
string[] textParts = text.Split(new char[] { '.' });
// If more than one period, the number is invalid
if (textParts.Length > 2) e.Handled = true;
// validate if period has more than two digits after it
if (textParts.Length == 2 && textParts[1].Length > maxDecimalLength) e.Handled = true;
}
Related
I have a TextBox where a user is meant to enter a product code that looks like this:
XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
What I'm trying to do is automatically placing the dashes in the right place as the user is typing, so that they only have to enter the actual code itself (similar to what's done in various pieces of software).
The problems I'm encountering have got to do with the position of the cursor as the user is typing.
My current solution (partially working) is as the following:
private void textBoxProductKey_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsControl(e.KeyChar))
{
return;
}
if (!char.IsLetterOrDigit(e.KeyChar) || textBoxProductKey.Text.Length == 29)
{
e.Handled = true;
return;
}
var cursorPosition = textBoxProductKey.SelectionStart;
string text;
if (cursorPosition == 0)
{
text = e.KeyChar + textBoxProductKey.Text;
}
else if (cursorPosition == textBoxProductKey.Text.Length)
{
text = textBoxProductKey.Text + e.KeyChar;
}
else
{
text = textBoxProductKey.Text.Insert(cursorPosition, e.KeyChar.ToString());
}
text = Regex.Replace(text, "-", "");
text = Regex.Replace(text, ".{5}", "$0-");
textBoxProductKey.Text = text.Length <= 29 ? text : text.Substring(0, 29);
textBoxProductKey.SelectionStart = cursorPosition / 6 == (cursorPosition + 1) / 6
? cursorPosition + 1
: cursorPosition + 2;
e.Handled = true;
}
The two problems with my current solution are
It doesn't properly handle the delete/backspace keys where potentially the dashes need to be deleted/inserted in different locations and the cursor position re-calculated
It doesn't work properly if the cursorPosition == textBoxProductKey.Text.Length.
The latter is very easy to fix, but I'm struggling with the former and also feel like the code is a bit convoluted.
How can I more easily achieve this (note the plan is to continue using a TextBox object).
EDIT
I am aware of the MaskedTextBox control, I do not want to use a MaskedTextBox, I want to use a TextBox.
What I am trying to do is not outside the realms of possibility for a TextBox, even if there are "easier" ways to accomplish this with other controls.
Also I feel like this is a great question for teaching people how to work with the cursor in a TextBox (among other things probably).
Have you considered using the MaskedTextBox control? It might be more appropriate.
The MaskedTextBox is designed precisely for this type of requirement and would answer your original question.
You may find it more difficult to implement this functionality yourself with a normal TextBox. It's also worth weighing up which solution is more likely to be be bug-free and easier to maintain.
It may helpful if you could to explain to us why you would prefer use a TextBox over a MaskedTextBox.
Continuing our conversation, save your current caret position, parse the whole text as if it's new, and return the caret to its original position.
That way you handle deleting as well as copy-pasting
My Question is to how to extend a TextBox such that it may start behaving like RichTextBox?
There can be various properties that RichTextBox may add: appearance mainly.
Should I use this kind of method where I extend the TextBox class and create a basic TextBox which would contain several other textboxes which would behave like a big container node containing small specialized nodes?
For starters, to have texts with alternate color after '+', I ve used this way:
class CustomTextBox : TextBox
{
List<TextBox> _textboxes = new List<TextBox>();
string _Text="";
List<Color> colorlist = new List<Color>();
public override string Text
{
get{return this._Text;}
set{this._Text = value;}
}
public CustomTextBox()
{
foreach(Color color in (Color[]) Enum.GetValues(typeOf(Color)))
{
colorlist.add(color);
}
this.KeyUp+= new KeyUpEventHandler(TextChangedCheck);
}
int i=0;
private void TextChangedCheck(object sender, KeyUpEventArgs e)
{
if(e.KeyData == Keys.Add)
{
TextBox Temp = new TextBox();
Temp.Text = this.Text;
this.Text = "";
Temp.ForeColor = colorlist[i];
i++;
this._textboxes.Add(Temp);
this.Controls.Add(_textboxes[i]);
e.Handled = true;
}
}
}
EDIT:
The MAIN purpose of this question is to extend a TextBox using its own properties to have a RTB like behavior and not using Graphics or related.
I'd have to guess what you really want to achieve here.
But if you want a Control with formatted text (FT) and really really really don't want a RichTextBox, I think you shouldn't create a new, embedded TextBox for every piece of FT.
Instead you should write the FT yourself, maybe on a panel with DrawString and use only one Textbox for text entry. Interesting project, really, once one accepts the challenge. Of course you must think your format through and also consider all sorts of interface questions..
Last week I have avoided a RTF for a tiny help system by using a ListView; it formats by line only, using the first character to indicate the format from a list of a dozen or so.. Works fine, but only one format per line.
I am attempting to implement a Windows Forms control in C# that resembles a textbox. When the user types 3 or more characters, a search will be performed against a datasource. There will be multiple fields returned (see the class structure below as one possible definition).
public class MyStructure
{
public int Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 {get; set; }
}
My requirements are to display an autocomplete list containing multiple columns (Note: this can be a string that contains padded fields from the list that are concatenated together). When the user either types all characters, hits the down arrow to select an item, or hits the enter key the value in the textbox will take the ValueMember of the list (where the DisplayMember of the list would be the whole data source). Every keystroke that the user enters that is not an up or down arrow or the enter key will perform another search and refresh the list.
I have seen how to implement a textbox with a single column in an auto-suggest, but cannot find a relatively "simple" example of how to do this for multiple columns. Should the control be a textbox or a combobox that is somehow styled to resemble a textbox (if this is possible) or a user control?
Should the event to monitor keystrokes be the TextEntered or the KeyPress event? Can I reset the AutoCompleteStringCollection without having the contents entered affected (I keep losing my input or my place in the input in any attempts)?
Can anyone provide examples of how to do this in framework 4.0 or above or point me to an example?
EDIT 1:
After much searching, I have found that essentially I need to implement a ContextMenuStrip on the TextBox (anything else and other controls below the user control will be overlapped). My problem is that I cannot determine how to handle the Key press events such as Tab and Enter. In addition, I need to handle if the user continues typing (in this event, I want to switch focus back to the textbox and add the key). Below is my code:
private void textBox1_TextChanged(object sender, EventArgs e)
{
ContextMenuStrip menuStrip;
string szMenuItem = string.Empty;
// This would actually be a call to a web service
List<MStarDeal> deals = DealInfo.Where(i => i.Value1.StartsWith(textBox1.Text.ToUpper()) || i.Value2.StartsWith(textBox1.Text.ToUpper()) || i.Value3.StartsWith(textBox1.Text.ToUpper()))
.Select(i => i).ToList();
if (textBox1.Text.Length >= 3 && !bSelected)
{
menuStrip = new System.Windows.Forms.ContextMenuStrip();
foreach (MStarDeal item in deals)
{
szMenuItem = item.Value1.PadRight(15) + item.Value2.PadRight(20) + item.Value3.PadRight(80);
ToolStripItem tsItem = new ToolStripMenuItem();
tsItem.Text = szMenuItem;
tsItem.Name = item.Value1;
tsItem.MouseUp += tsItem_MouseUp;
menuStrip.Items.Add(tsItem);
}
textBox1.ContextMenuStrip = menuStrip;
textBox1.ContextMenuStrip.Show(textBox1, new Point(0, 20));
}
else if (bSelected)
{
bSelected = false;
}
}
void tsItem_MouseUp(object sender, EventArgs e)
{
bSelected = true;
textBox1.Text = ((ToolStripMenuItem)sender).Name;
}
Thanks,
Lee
I think I understand your question. How about using the TextChanged() event instead of KeyPress? As far as the columns, a flowLayoutPanel will render columns if you set it up to flow in the right direction and make its size appropriate to the width of the two columns combined.
I have this code:
public static void AddDefaultTextFromTag(params TextBox[] textBoxes)
{
foreach (TextBox oTextBox in textBoxes)
{
bool isPasswordChar = oTextBox.UseSystemPasswordChar;
oTextBox.Enter += (sndr, evnt) =>
{
if (((TextBox)sndr).Text == ((TextBox)sndr).Tag.ToString())
{
((TextBox)sndr).Text = "";
((TextBox)sndr).UseSystemPasswordChar = isPasswordChar;
((TextBox)sndr).ForeColor = SystemColors.WindowText;
}
};
oTextBox.Leave += (sndr, evnt) =>
{
if (((TextBox)sndr).Text.Trim().Count() == 0)
{
((TextBox)sndr).UseSystemPasswordChar = false;
((TextBox)sndr).CharacterCasing = CharacterCasing.Normal;
((TextBox)sndr).Text = ((TextBox)sndr).Tag.ToString();
((TextBox)sndr).ForeColor = SystemColors.GrayText;
}
};
if (oTextBox.Text.Trim().Count() == 0)
{
oTextBox.UseSystemPasswordChar = false;
oTextBox.CharacterCasing = CharacterCasing.Normal;
oTextBox.Text = oTextBox.Tag.ToString();
oTextBox.ForeColor = SystemColors.GrayText;
}
}
}
But when the TextBox.UseSystemPasswordChar I input in this method's parameter is true and it's TextBox.Text property is empty, the TextBox can't leave using a Tab button on the keyboard, only a MouseClick can be used to lose the focus of that TextBox.
Why is this happening?
My code is in C#, framework 4, build in VS2010 Pro, project is in WinForms.
I use a TextBox from the VS ToolBox.
Please help. Thanks in advance.
The reason you can't leave the textbox is because you are changing the CharacterCasing property in the textbox.
Not sure why it works like this, but it has happened to me before, what I ended up doing was capture the keypress event, and if it was a letter I'd switch it to it's uppercase value. It's not optimal, but it works
I did something similar to this (writing it from the top of my head, but it should work):
void YourTextbox_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsLetter(e.KeyChar))
{
if (this.CharacterCasing == CharacterCasing.Upper && char.IsLower(e.KeyChar))
{
this.Text = this.Text.Insert(this.SelectionStart, char.ToUpper(e.KeyChar) + string.Empty);
this.SelectionStart++;
e.Handled = true;
}
else if (this.CharacterCasing == System.Windows.Forms.CharacterCasing.Lower && char.IsUpper(e.KeyChar))
{
this.Text = this.Text.Insert(this.SelectionStart, char.ToLower(e.KeyChar) + string.Empty);
this.SelectionStart++;
e.Handled = true;
}
}
}
You also should use the new keyword to "override" (I know that's not the right term here) the Character casing, so it doesn't do it's own thing
public new CharacterCasing CharacterCasing { get; set; }
The code basically checks if the pressed key is a letter, then, if it's marked as Upper, and the char is lower, replaces it with it's upper version (in the position of the cursor) then moves the cursor to the next part, and Viceversa (toLower)
NOTE:
This code will have may (should) have some trouble if the user has more than one character selected (SelectionLenght > 0), if you want to keep the normal Textbox functionality, you should delete all the selected characters
So I set up a WinForms app, drew two textboxes, set one to UseSystemPasswordChar=true then set it up like so:
private void Form1_Load(object sender, EventArgs e)
{
textBox2.Tag = "test2";
textBox1.Tag = "test1";
TextBox[] tb = { textBox1, textBox2 };
AddDefaultTextFromTag(tb);
}
Your function works fine and I have no problems tabbing through the controls on the form no matter what the textboxes contain. (added a button also that does nothing for tabbing test) so... no repro unless my test setup is not valid
What I found in the answer of this post was the solution for me. Instead of setting UseSystemPasswordChar to true and then to false, you can set PasswordChar to '●' and then to '\0' to have normal text. You should not set the UseSystemPasswordChar because it has precedence over PasswordChar.
I have TextBox which allow insert only numeric values (filtering), But when I paste copied text it's allow any kind of symbol. How can I prevent or filter text before pasting?
You could backup your text before any manual input and then when the input provided isn't valid restore the previous text like so:
_backupText = string.Empty;
doNotPasteTextBox.TextInputStart += (sender, e) =>
{
int textParsed;
if(int.TryParse(e.Text,out textParsed))
{
_backupText = doNotPasteTextBox.Text.Insert(doNotPasteTextBox.SelectionStart, e.Text);
}else
{
e.Handled = true;
}
};
doNotPasteTextBox.TextChanged += (sender, e) =>
{
int textParsed;
int selectionStart = doNotPasteTextBox.SelectionStart;
if(!int.TryParse(doNotPasteTextBox.Text, out textParsed))
{
doNotPasteTextBox.Text = _backupText;
}
doNotPasteTextBox.SelectionStart = selectionStart;
};
I wouldn't recommend trying to capture the control keys or anything because when you're on a mac or on linux then you're screwed.
Adjust my sample and pour it inside a new textbox control to make it cleaner but you get the idea.
You could use Clipboard.GetText() to get the text that is inserted into the textbox, but this will pop up a message, and the user must give the application access to the Clipboard.
If its no problem for you then i would use this.