I am trying to make text box accept only specific characters.
My TextBox is bound to the following:
private string _CompanyID;
public string
You should use a custom UI element there that restricts the input on the view-side using “classic” solutions like change listeners.
For example, you can just create a simple subtype of TextBox
that overrides the OnPreviewTextInput
method. There, you can decide when some input should go through, or when you want to prevent it.
For example, this is a custom TextBox that takes only characters from the ASCII alphabet:
public class AlphabetTextBox : TextBox
{
private static readonly Regex regex = new Regex("^[a-zA-Z]+$");
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
if (!regex.IsMatch(e.Text))
e.Handled = true;
base.OnPreviewTextInput(e);
}
}
Of course, you could also make the regular expression a property of the text box and allow people to set it from XAML. That way, you would get a very reusable component which you can use for various applications.
Public Shared Function GetWordCount(str As String) As Integer Dim collection As MatchCollection = Regex.Matches(str, "\S+") Return collection.Count End Function
Public Shared Function GetInWordLimit(str As String, max_words As Integer) As String
Dim final As String = ""
Dim count As Integer = Core.StringOperations.GetWordCount(str)
Dim avg_max_length As Integer = max_words * 7
Dim words = str.Split(" ")
If (words.Length > max_words - 1 And count > max_words - 1) Then
Dim index As Integer = 0
For Each word In words
If index >= max_words Then Exit For
final &= word & " "
If Not (Char.IsSeparator(word) Or Char.IsWhiteSpace(word) Or word = "") Then
index += 1
End If
Next
final = final.TrimEnd
Else
final = str
End If
If final.Length > avg_max_length - 1 Then final = final.Substring(0, avg_max_length)
Return final
End Function
I do this with the PreviewtextInput event. I have a generic event used for multiple TextBoxes which takes the regex from a configuration table, but I have hard-coded the regex in this example.
private void GenericTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsTextAllowed(e.Text, @"[^a-zA-Z]");
}
private static bool IsTextAllowed(string Text, string AllowedRegex)
{
try
{
var regex = new Regex(AllowedRegex);
return !regex.IsMatch(Text);
}
catch
{
return true;
}
}
The problem is, humans type in numbers sequentially, the fools.
To type in "0.1", a legitimate string, you have to type in "0.", which fails.
Also, re the accepted answer from @poke (which is great), the e.Text value is the change to the textbox (keystroke).
You must add this change to the current textbox string, and then validate the concatenated candidate string, and see if that is valid.
Humans are also wiley, so they will paste from the clipboard to get around the restriction.
With a textbox, you will never be able to block all garbage in, because at some point the user will have to go through garbage, to get to a valid string.
So you can block illegal character entry using e.Text, or allow sequential step failure. But you will still have to check the final string for validity too.
Below is an example of a textbox that allows users to type in a decimal value with a max of 8 dec places in, but they could still cheat this by pasting from the clipboard.
////////////////////////
// REGEXTEXTBOX CLASS //
////////////////////////
using System.Windows.Controls; // Textbox
using System.Windows.Input;
using System.Text.RegularExpressions; // Regex
namespace MyNamespace
{
public class RegexTextBox : TextBox
{
private Regex _regex = null;
public Regex Regex
{
get { return _regex; }
set { _regex = value; }
}
///////////////////////////////////////////////////////////////////////
// MEMBERS
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
var prefix = "OnPreviewTextInput() - ";
logger.Debug(prefix + "Entering");
string currentText = this.Text;
string candidateText = currentText + e.Text;
// If we have a set regex, and the current text fails,
// mark as handled so the text is not processed.
if (_regex != null && !_regex.IsMatch(candidateText))
{
e.Handled = true;
}
base.OnPreviewTextInput(e);
}
} // end of class RegexTextbox
} // end of MyNamespace
/////////////////////
// MAINWINDOW.XAML //
/////////////////////
//(Window class needs to know your namespace so it needs xmlns:myNamespace="clr-namespace:MyNamespace")
<myNamespace:RegexTextBox
x:Name="textboxPayToAmount"
Text="{Binding PayToAmount}">
</myNamespace:RegexTextBox>
////////////////////////
// MAINWINDOW.XAML.CS //
////////////////////////
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textboxPayToAmount.Regex =
new System.Text.RegularExpressions.Regex(@"^\d*(\.\d{0,8})?$");
}
}
}