I have the following TextBox:
<TextBox Text="{Binding SearchString,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
Bound to the following property:
private string _searchString;
public string SearchString
{
get
{
return _searchString;
}
set
{
value = Regex.Replace(value, "[^0-9]", string.Empty);
_searchString = value;
DoNotifyPropertyChanged("SearchString");
}
}
The class inherits from a base class that implements INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void DoNotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
All I want is a quick and dirty way to disallow non-numerical characters for an integer-only text box (I know it's not complete, just for demonstration). I don't want a mere notification that there is illegal text or anything, I want to discard right away all characters on input that are disallowed.
However, the TextBox is behaving weirdly. I can still enter any text I want, it will display as entered, e.g. "1aaa". Even though the property has been properly cleaned to "1" in this example, the Textbox still shows "1aaa". Only when I enter an actual digit that would cause _searchString to change does it also update the displayed text, for example when I have "1aaa2" it will correctly update to "12". What's the matter here?
This sounds like view-specific logic, so I see no reason not to use code-behind the view to control it. Personally I would implement this kind of behavior with a PreviewKeyDown
on the TextBox
that discards any non-numeric characters.
It probably wouldn't hurt to have something generic that you could reuse, such as a custom NumbersOnlyTextBox
control, or an AttachedProperty
you could attach to your TextBox
to specify that it only allows numbers.
In fact, I remember creating an attached property that allows you to specify a regex for a textbox, and it will limit character entry to just that regex. I haven't used it in a while, so you'll probably want to test it or possible update it, but here's the code.
// When set to a Regex, the TextBox will only accept characters that match the RegEx
#region AllowedCharactersRegex Property
/// <summary>
/// Lets you enter a RegexPattern of what characters are allowed as input in a TextBox
/// </summary>
public static readonly DependencyProperty AllowedCharactersRegexProperty =
DependencyProperty.RegisterAttached("AllowedCharactersRegex",
typeof(string), typeof(TextBoxProperties),
new UIPropertyMetadata(null, AllowedCharactersRegexChanged));
// Get
public static string GetAllowedCharactersRegex(DependencyObject obj)
{
return (string)obj.GetValue(AllowedCharactersRegexProperty);
}
// Set
public static void SetAllowedCharactersRegex(DependencyObject obj, string value)
{
obj.SetValue(AllowedCharactersRegexProperty, value);
}
// Events
public static void AllowedCharactersRegexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var tb = obj as TextBox;
if (tb != null)
{
if (e.NewValue != null)
{
tb.PreviewTextInput += Textbox_PreviewTextChanged;
DataObject.AddPastingHandler(tb, TextBox_OnPaste);
}
else
{
tb.PreviewTextInput -= Textbox_PreviewTextChanged;
DataObject.RemovePastingHandler(tb, TextBox_OnPaste);
}
}
}
public static void TextBox_OnPaste(object sender, DataObjectPastingEventArgs e)
{
var tb = sender as TextBox;
bool isText = e.SourceDataObject.GetDataPresent(DataFormats.Text, true);
if (!isText) return;
var newText = e.SourceDataObject.GetData(DataFormats.Text) as string;
string re = GetAllowedCharactersRegex(tb);
re = "[^" + re + "]";
if (Regex.IsMatch(newText.Trim(), re, RegexOptions.IgnoreCase))
{
e.CancelCommand();
}
}
public static void Textbox_PreviewTextChanged(object sender, TextCompositionEventArgs e)
{
var tb = sender as TextBox;
if (tb != null)
{
string re = GetAllowedCharactersRegex(tb);
re = "[^" + re + "]";
if (Regex.IsMatch(e.Text, re, RegexOptions.IgnoreCase))
{
e.Handled = true;
}
}
}
#endregion // AllowedCharactersRegex Property
It would be used like this:
<TextBox Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}"
local:TextBoxHelpers.AllowedCharactersRegex="[0-9]" />
But as to why it won't update the UI. The UI knows the value hasn't actually changed, so doesn't bother re-evaluating the binding when it receives the PropertyChange notification.
To get around that, you could try temporarily setting the value to something else before setting it to the regex value, and raising a PropertyChange
notification so the UI re-evaluates the bindings, but honestly that isn't really an ideal solution.
private string _searchString;
public string SearchString
{
get
{
return _searchString;
}
set
{
value = Regex.Replace(value, "[^0-9]", string.Empty);
// If regex value is the same as the existing value,
// change value to null to force bindings to re-evaluate
if (_searchString == value)
{
_searchString = null;
DoNotifyPropertyChanged("SearchString");
}
_searchString = value;
DoNotifyPropertyChanged("SearchString");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With