Description
When working with input data (TextBox) you will usually have a certain data type that you expect from the end user. This may be an integer or string type. Even if it’s a string then you may want to check if it matches a certain pattern.
You can of course use a validation and that will simply color in red the faulty lines but that was not what I was looking for, I needed something that will not allow the user to insert a string in a number field.
There is a nice library (Interactivity) where you can define behaviours, but I was creating an add-in and this proved to be a problem, so I decided to make something simple but pretty powerfull. Of course you may extend it as you like:
Solution
public class TextboxStrict
{
private Regex validate;
private string lastKnowText = "";
private string defaultText = "";
private int defaultCaretIndex = 0;
private int lastKnownCaretIndex = 0;
/// inputChange - marks that the change was made by this class so that
/// when text changed is fired again - caret index will not move it's position
private bool inputChange = false;
public TextboxStrict(string regexPattern, string startWith = "")
{
validate = new Regex(regexPattern);
///lastKnownText will be the placeholder of any good changes in text status
this.lastKnowText = startWith;
///default text will be the holder of the default text on initialize and on text == ""
this.defaultText = startWith;
this.lastKnownCaretIndex = startWith.Length;
this.defaultCaretIndex = startWith.Length;
}
public void AttachTo(TextBox textBox)
{
///Initializing text box data with default entries
textBox.Text = this.defaultText;
textBox.CaretIndex = this.defaultCaretIndex;
textBox.TextChanged += EvtTextChanged;
textBox.SelectionChanged += EvtSelectionChanged;
}
/// <summary>
/// Validate function
/// </summary>
private bool Validate(string text)
{
return validate.IsMatch(text);
}
/// <summary>
/// Making sure last known caret index keeps track
/// </summary>
private void EvtSelectionChanged(object sender, RoutedEventArgs e)
{
TextBox textBox = (TextBox)sender;
lastKnownCaretIndex = textBox.CaretIndex;
}
/// <summary>
/// Validates text and keeps last known good one. Keeps track of caret index also
/// </summary>
private void EvtTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textbox = (TextBox)sender;
if (textbox.Text == "")
{
inputChange = true;
textbox.Text = this.defaultText;
textbox.CaretIndex = this.defaultCaretIndex;
}
///If it doesn't validate - change back. Mark as internal change to not override the last index
else if (!this.Validate(textbox.Text))
{
inputChange = true;
textbox.Text = lastKnowText;
textbox.CaretIndex = lastKnownCaretIndex;
}
else
{
if (!inputChange)
{
lastKnowText = textbox.Text;
lastKnownCaretIndex = textbox.CaretIndex;
}
inputChange = false;
}
}
}
Usage
The class above will attach 2 events to the textbox (textchanged and selectionchanged).
public partial class MainWindow : Window
{
TextboxStrict inputLimit = new TextboxStrict("^[0-9]{1,10}$", "0");
public MainWindow()
{
InitializeComponent();
inputLimit.AttachTo(TestTextBox);
}
}







