How do I get a TextBox to only accept numeric input in WPF?
Categories:
How to Restrict WPF TextBox Input to Numeric Values Only

Learn various techniques to ensure a WPF TextBox accepts only numeric input, from basic event handling to advanced attached behaviors and custom controls.
In Windows Presentation Foundation (WPF) applications, it's a common requirement to restrict user input in a TextBox
to specific data types, such as numbers. This prevents invalid data from being entered, improving data integrity and user experience. This article explores several robust methods to achieve numeric-only input, ranging from simple event-based validation to more sophisticated attached behaviors and custom controls.
Method 1: Event-Based Validation (PreviewTextInput)
The PreviewTextInput
event is a good starting point for basic input validation. This event fires before the text is actually entered into the TextBox
. By handling this event, you can inspect the incoming text and mark the event as handled if the input is not numeric, effectively preventing it from appearing in the TextBox
.
<TextBox PreviewTextInput="TextBox_PreviewTextInput" />
XAML for a TextBox using PreviewTextInput
using System.Text.RegularExpressions;
using System.Windows.Input;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
// Use a regular expression to check if the input is a digit
// This allows for integers only. For decimals, see the next example.
Regex regex = new Regex("[^0-9]+"); // Match non-digits
e.Handled = regex.IsMatch(e.Text);
}
}
C# code for PreviewTextInput event handler (integer only)
PreviewTextInput
event only checks the new character, not the entire TextBox
content. Full validation should occur on TextChanged
or LostFocus
.private void TextBox_PreviewTextInput_Decimal(object sender, TextCompositionEventArgs e)
{
TextBox textBox = sender as TextBox;
// Allow digits and one decimal point
if (!char.IsDigit(e.Text, 0) && e.Text != ".")
{
e.Handled = true; // Reject non-digit and non-decimal point characters
}
else if (e.Text == "." && textBox.Text.Contains("."))
{
e.Handled = true; // Reject if a decimal point already exists
}
}
C# code for PreviewTextInput event handler (decimal numbers)
Method 2: Attached Behavior for Reusability
Attached behaviors provide a more elegant and reusable solution compared to direct event handling in the code-behind. They allow you to encapsulate the numeric input logic into a separate class that can be easily attached to any TextBox
in XAML, promoting cleaner code and better separation of concerns.
flowchart TD A[TextBox] --> B{Attached Behavior: NumericOnly} B --> C{PreviewTextInput Event} C --> D{Validate Input (Regex)} D -- Invalid --> E[e.Handled = true] D -- Valid --> F[Allow Input] E --> G[Input Blocked] F --> H[Input Accepted]
Flowchart of Attached Behavior for Numeric Input
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
public static class NumericOnlyBehavior
{
public static readonly DependencyProperty IsNumericOnlyProperty =
DependencyProperty.RegisterAttached(
"IsNumericOnly",
typeof(bool),
typeof(NumericOnlyBehavior),
new PropertyMetadata(false, OnIsNumericOnlyChanged));
public static bool GetIsNumericOnly(DependencyObject obj)
{
return (bool)obj.GetValue(IsNumericOnlyProperty);
}
public static void SetIsNumericOnly(DependencyObject obj, bool value)
{
obj.SetValue(IsNumericOnlyProperty, value);
}
private static void OnIsNumericOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if ((bool)e.NewValue)
{
textBox.PreviewTextInput += TextBox_PreviewTextInput;
// Optional: Handle pasting to prevent non-numeric text
textBox.AddHandler(DataObject.PastingEvent, new DataObjectPastingEventHandler(TextBox_Pasting));
}
else
{
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
textBox.RemoveHandler(DataObject.PastingEvent, new DataObjectPastingEventHandler(TextBox_Pasting));
}
}
}
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9.-]+"); // Allow digits, decimal point, and negative sign
TextBox textBox = sender as TextBox;
// Handle decimal point
if (e.Text == ".")
{
if (textBox.Text.Contains(".") || textBox.SelectionStart == 0 && textBox.Text.Length > 0 && !char.IsDigit(textBox.Text[0]))
{
e.Handled = true; // Prevent multiple decimal points or decimal at start if not followed by digit
}
}
// Handle negative sign
else if (e.Text == "-")
{
if (textBox.Text.Contains("-") || textBox.SelectionStart != 0)
{
e.Handled = true; // Prevent multiple negative signs or negative sign not at start
}
}
// Handle other non-numeric characters
else if (regex.IsMatch(e.Text))
{
e.Handled = true;
}
}
private static void TextBox_Pasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(string)))
{
string text = (string)e.DataObject.GetData(typeof(string));
Regex regex = new Regex("[^0-9.-]+");
if (regex.IsMatch(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
}
C# code for NumericOnlyBehavior attached property
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp" <!-- Adjust namespace -->
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox local:NumericOnlyBehavior.IsNumericOnly="True" Width="200" Height="30" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</Window>
XAML using the NumericOnlyBehavior attached property
Method 3: Custom Control (NumericUpDown)
For a more feature-rich numeric input experience, consider creating a custom NumericUpDown
control or using a third-party library. A custom control can encapsulate not only the input validation but also provide increment/decrement buttons, min/max value enforcement, and formatting options. This is ideal when you need consistent numeric input across your application with additional UI elements.
NumericUpDown
control is beyond the scope of a single example, the core idea involves inheriting from TextBox
or UserControl
and implementing custom logic for input validation and UI elements. Many open-source libraries (e.g., WPF Extended Toolkit) offer robust NumericUpDown
controls.Considerations and Best Practices
When implementing numeric input, keep the following in mind:
- Culture-Specific Decimals: Different cultures use different decimal separators (e.g., '.' vs. ','). Ensure your validation accounts for the current culture if your application needs to be globalized.
- Pasting: Users can paste non-numeric text. The
DataObject.PastingEvent
handler in the attached behavior example addresses this. - Negative Numbers: If negative numbers are allowed, ensure the '-' sign is permitted only at the beginning of the input.
- Min/Max Values: For more robust validation, consider adding logic to enforce minimum and maximum allowed values, typically on the
TextChanged
orLostFocus
events, or when the data is bound. - Input Method Editors (IMEs): For languages that use IMEs,
PreviewTextInput
might not always fire as expected. For most Western numeric input, it's sufficient. - Data Binding: If you're using data binding, ensure your ViewModel properties are of a numeric type (e.g.,
int
,double
) and implementINotifyPropertyChanged
. WPF's binding engine can also perform type conversion and validation.