Introduction
I have struggled to try and create a good behavior to allow users only to enter number keys. Of course, most of my problem was I did not know the right approach. Seemed that the event to use was the KeyPress
event, but it is not because there is no easy way to distinguish if the user has entered a digit or not because the event arguments return only the key, and not the resulting character. The only good way that I know of is to attach to the TextInput
event. In this event, I use LINQ to check each if any of the characters of the string
are not a digit, and if they are not, set the Handled
equal to true
. I have seen a lot of implementations that use a Regular Expression to check if all the characters are digits, but I am sure that this is a lot heavier than just checking that all are digits.
I also use the DataObject.AddPastingHandler
to add an event handler that will ensure that pasted values are text and only contain digits.
The Code
There is one DependencyProperty
for this behavior:
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
typeof(NumberOnlyBehaviour), new UIPropertyMetadata(false, OnValueChanged));
public static bool GetIsEnabled(Control o) { return (bool)o.GetValue(IsEnabledProperty); }
public static void SetIsEnabled(Control o, bool value) { o.SetValue(IsEnabledProperty, value); }
As usual for my properties on behaviours, I try to use something familiar, and a bool
IsEnabled
seemed to be an obvious name for the property. To use it just need to set IsEnabled
to true
.
The event handler for this DespendencyProperty
is OnValueChanged
. The code is as follows:
private static void OnValueChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
var uiElement = dependencyObject as Control;
if (uiElement == null) return;
if (e.NewValue is bool && (bool)e.NewValue)
{
uiElement.PreviewTextInput += OnTextInput;
uiElement.PreviewKeyDown += OnPreviewKeyDown;
DataObject.AddPastingHandler(uiElement, OnPaste);
}
else
{
uiElement.PreviewTextInput -= OnTextInput;
uiElement.PreviewKeyDown -= OnPreviewKeyDown;
DataObject.RemovePastingHandler(uiElement, OnPaste);
}
}
Basically this event handler subscribes to the two events that will filter out any user input that is not a number, and also add a handler that will deal with pasting of text when the behavior is enabled by setting the IsEnabled
equal to true, and otherwise removes the handlers.
The two keyboard event handlers are:
private static void OnTextInput(object sender, TextCompositionEventArgs e)
{
if (e.Text.Any(c => !char.IsDigit(c))) { e.Handled = true; }
}
private static void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)e.Handled = true;
}
The OnPreviewKeyDown
event
handler is required because the PreviewTextInput
event
does not fire when the space key is pressed, so also have include an event handler for the PreviewKeyDown
. All other key presses cause a change in the TextInput
, so the checking if the text contains a non-digit value. Unfortunately it is very difficult to know the effect of the key press on the KeyDown
or KeyUp
events because the KeyEventArgs
only provide the enumeration of the Key and not the effect, so handling only one KeyPress
event
is very hard to implement.
And finally the paste handler:
private static void OnPaste(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
var text = Convert.ToString(e.DataObject.GetData(DataFormats.Text)).Trim();
if (text.Any(c => !char.IsDigit(c))) { e.CancelCommand(); }
}
else
{
e.CancelCommand();
}
}
How to use this behaviour
To use this behavior is very easy. On the control you want to allow only numbers, you would add this behavior as shown in bold:
<TextBox Style="{StaticResource EditFormTextBoxNumericStyle}"
behaviours:NumberOnlyBehaviour.IsEnabled="True"
Text="{Binding Sequence,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" />

History
- 1st October, 2015: Initial version
- 3 March 2016 added updated source code to handle the space key as recommended by George Swan.