validar vacio solo punto para numeros letras c# wpf xaml textbox numericupdown

c# - solo - validar textbox vacio wpf



¿Cómo obtengo un TextBox para aceptar solo entradas numéricas en WPF? (28)

Estoy buscando aceptar dígitos y el punto decimal, pero sin signo.

He examinado muestras utilizando el control NumericUpDown para formularios de Windows, y esta muestra de un control personalizado NumericUpDown de Microsoft . Pero hasta ahora parece que NumericUpDown (compatible con WPF o no) no va a proporcionar la funcionalidad que quiero. Tal como está diseñada mi aplicación, nadie en su sano juicio querrá meterse con las flechas. No tienen ningún sentido práctico, en el contexto de mi aplicación.

Así que estoy buscando una forma sencilla de hacer que un cuadro de texto de WPF estándar acepte solo los caracteres que quiero. es posible? ¿Es práctico?


Agregue una REGLA DE VALIDACIÓN para que cuando cambie el texto, verifique si los datos son numéricos, y si lo es, permite que el procesamiento continúe, y si no lo es, le solicita al usuario que solo se acepten datos numéricos en ese campo.

Leer más en Validación en Windows Presentation Foundation.


Ahora sé que esta pregunta tiene una respuesta aceptada , pero personalmente, me parece un poco confusa y creo que debería ser más fácil que eso. Así que trataré de demostrar cómo funcionó lo mejor que puedo:

En Windows Forms , hay un evento llamado KeyPress que es perfectamente bueno para este tipo de tarea. Pero eso no existe en WPF , por lo tanto, PreviewTextInput evento PreviewTextInput . Además, para la validación, creo que se puede usar un foreach para recorrer el textbox.Text y verifique si coincide ;) la condición, pero honestamente, para eso son las expresiones regulares .

Una cosa más antes de sumergirnos en el código sagrado . Para que el evento sea despedido, uno puede hacer dos cosas:

  1. Use XAML para decirle al programa a qué función llamar: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Hágalo en el evento Loaded del formulario (en el que se encuentra el TextBox): textBox.PreviewTextInput += onlyNumeric;

Creo que el segundo método es mejor porque en situaciones como esta, en su mayoría se le pedirá que aplique la misma condición ( regex ) a más de un TextBox y no quiere repetirlo. .

Finalmente, así es como lo harías:

private void onlyNumeric(object sender, TextCompositionEventArgs e) { string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$"; Regex regex = new Regex(onlyNumeric); e.Handled = !regex.IsMatch(e.Text); }



Aquí hay una manera muy simple y fácil de hacer esto usando MVVM.

Vincule su caja de texto con una propiedad de entero en el modelo de vista, y esto funcionará como una gema ... incluso mostrará una validación cuando se ingrese un no-entero en el cuadro de texto.

Código XAML:

<TextBox x:Name="contactNoTxtBox" Text="{Binding contactNo}" />

Ver código de modelo:

private long _contactNo; public long contactNo { get { return _contactNo; } set { if (value == _contactNo) return; _contactNo = value; OnPropertyChanged(); } }


Aquí tengo una solución simple inspirada en la respuesta de Ray . Esto debería ser suficiente para identificar cualquier forma de número.

Esta solución también se puede modificar fácilmente si solo desea números positivos, valores enteros o valores con un número máximo de decimales, etc.

Como se sugiere en la respuesta de Ray , primero debe agregar un evento PreviewTextInput :

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Luego ponga lo siguiente en el código detrás:

private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e) { var s = sender as TextBox; // Use SelectionStart property to find the caret position. // Insert the previewed text into the existing text in the textbox. var text = s.Text.Insert(s.SelectionStart, e.Text); double d; // If parsing is successful, set Handled to false e.Handled = !double.TryParse(text, out d); }


Asumiré que:

  1. Su TextBox para el que desea permitir la entrada numérica solo tiene su propiedad de Texto inicialmente establecida en algún valor numérico válido (por ejemplo, 2.7172).

  2. Tu cuadro de texto es un hijo de tu ventana principal

  3. Su ventana principal es de clase Window1

  4. Su nombre de TextBox es numéricoTB

Idea básica:

  1. Añadir: private string previousText; a su clase de ventana principal (Window1)

  2. Añadir: previousText = numericTB.Text; a tu ventana principal del constructor

  3. Cree un controlador para que el evento numericTB.TextChanged sea algo como esto:

    private void numericTB_TextChanged(object sender, TextChangedEventArgs e) { double num = 0; bool success = double.TryParse(((TextBox)sender).Text, out num); if (success & num >= 0) previousText = ((TextBox)sender).Text; else ((TextBox)sender).Text = previousText; }

Esto mantendrá configurando el texto anterior en el texto numérico. Texto mientras sea válido, y establecerá el texto numérico en su último valor válido si el usuario escribe algo que no le gusta. Por supuesto, esto es solo una idea básica, y es solo "resistente a idiotas", no "a prueba de idiotas". No maneja el caso en el que el usuario se mete con espacios, por ejemplo. Así que aquí hay una solución completa que creo que es "a prueba de idiotas", y si me equivoco, por favor dígame:

  1. Contenido de su archivo Window1.xaml:

    <Window x:Class="IdiotProofNumericTextBox.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/> </Grid> </Window>

  2. Contenido de su archivo Window.xaml.cs:

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace IdiotProofNumericTextBox { public partial class Window1 : Window { private string previousText; public Window1() { InitializeComponent(); previousText = numericTB.Text; } private void numericTB_TextChanged(object sender, TextChangedEventArgs e) { if (string.IsNullOrEmpty(((TextBox)sender).Text)) previousText = ""; else { double num = 0; bool success = double.TryParse(((TextBox)sender).Text, out num); if (success & num >= 0) { ((TextBox)sender).Text.Trim(); previousText = ((TextBox)sender).Text; } else { ((TextBox)sender).Text = previousText; ((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length; } } } } }

Y eso es. Si tiene muchos TextBoxes, le recomiendo crear un CustomControl que herede de TextBox, de modo que pueda ajustar el Texto anterior y el texto numéricoTB_TextChanged en un archivo separado.


Después de usar algunas de las soluciones aquí por algún tiempo, desarrollé mi propia solución que funciona bien para mi configuración de MVVM. Tenga en cuenta que no es tan dinámico como algunos de los otros en el sentido de que aún permite a los usuarios ingresar caracteres erróneos, pero les impide presionar el botón y, por lo tanto, hacer cualquier cosa. Esto va bien con mi tema de los botones de color gris cuando no se pueden realizar acciones.

Tengo un TextBox que un usuario debe ingresar una cantidad de páginas de documentos para imprimir:

<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>

... con esta propiedad vinculante:

private string _numberPagesToPrint; public string NumberPagesToPrint { get { return _numberPagesToPrint; } set { if (_numberPagesToPrint == value) { return; } _numberPagesToPrint = value; OnPropertyChanged("NumberPagesToPrint"); } }

También tengo un botón:

<Button Template="{DynamicResource CustomButton_Flat}" Content="Set" Command="{Binding SetNumberPagesCommand}"/>

... con este comando de enlace:

private RelayCommand _setNumberPagesCommand; public ICommand SetNumberPagesCommand { get { if (_setNumberPagesCommand == null) { int num; _setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(), () => Int32.TryParse(NumberPagesToPrint, out num)); } return _setNumberPagesCommand; } }

Y luego está el método de SetNumberOfPages() , pero no es importante para este tema. Funciona bien en mi caso porque no tengo que agregar ningún código al archivo de código subyacente de la Vista y me permite controlar el comportamiento mediante la propiedad Command .


El controlador de eventos está previsualizando la entrada de texto. Aquí, una expresión regular coincide con la entrada de texto solo si no es un número, y luego no está hecha para el cuadro de texto de entrada.

Si solo desea letras, reemplace la expresión regular como [^a-zA-Z] .

XAML

<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>

XAML.CS ARCHIVO

using System.Text.RegularExpressions; private void NumberValidationTextBox(object sender, TextCompositionEventArgs e) { Regex regex = new Regex("[^0-9]+"); e.Handled = regex.IsMatch(e.Text); }



En Windows Forms fue fácil; Puedes agregar un evento para KeyPress y todo funciona fácilmente. Sin embargo, en WPF ese evento no está allí. Pero hay una manera mucho más fácil de hacerlo.

El TextBox de WPF tiene el evento TextChanged que es general para todo. Incluye pegar, escribir y lo que sea que se te ocurra.

Así que puedes hacer algo como esto:

XAML:

<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>

CÓDIGO DETRÁS:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { string s = Regex.Replace(((TextBox)sender).Text, @"[^/d.]", ""); ((TextBox)sender).Text = s; }

Esto también acepta . , si no lo desea, simplemente elimínelo de la declaración de regex para que sea @[^/d] .

Nota : Este evento se puede usar en muchos TextBox''es, ya que usa el Texto del objeto sender . Solo escribe el evento una vez y puede usarlo para varios TextBox''es.


En la aplicación WPF, puedes manejar esto manejando el evento TextChanged :

void arsDigitTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { Regex regex = new Regex("[^0-9]+"); bool handle = regex.IsMatch(this.Text); if (handle) { StringBuilder dd = new StringBuilder(); int i = -1; int cursor = -1; foreach (char item in this.Text) { i++; if (char.IsDigit(item)) dd.Append(item); else if(cursor == -1) cursor = i; } this.Text = dd.ToString(); if (i == -1) this.SelectionStart = this.Text.Length; else this.SelectionStart = cursor; } }


Esta es una solución mejorada de la respuesta de share . Mis mejoras son:

  • Comportamiento mejorado en los botones Del y Retroceso
  • Se EmptyValue propiedad EmptyValue , si la cadena vacía es inapropiada
  • Se corrigieron algunos errores tipográficos menores.

/// <summary> /// Regular expression for Textbox with properties: /// <see cref="RegularExpression"/>, /// <see cref="MaxLength"/>, /// <see cref="EmptyValue"/>. /// </summary> public class TextBoxInputRegExBehaviour : Behavior<TextBox> { #region DependencyProperties public static readonly DependencyProperty RegularExpressionProperty = DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*")); public string RegularExpression { get { return (string)GetValue(RegularExpressionProperty); } set { SetValue(RegularExpressionProperty, value); } } public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(int.MinValue)); public int MaxLength { get { return (int)GetValue(MaxLengthProperty); } set { SetValue(MaxLengthProperty, value); } } public static readonly DependencyProperty EmptyValueProperty = DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null); public string EmptyValue { get { return (string)GetValue(EmptyValueProperty); } set { SetValue(EmptyValueProperty, value); } } #endregion /// <summary> /// Attach our behaviour. Add event handlers /// </summary> protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewTextInput += PreviewTextInputHandler; AssociatedObject.PreviewKeyDown += PreviewKeyDownHandler; DataObject.AddPastingHandler(AssociatedObject, PastingHandler); } /// <summary> /// Deattach our behaviour. remove event handlers /// </summary> protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewTextInput -= PreviewTextInputHandler; AssociatedObject.PreviewKeyDown -= PreviewKeyDownHandler; DataObject.RemovePastingHandler(AssociatedObject, PastingHandler); } #region Event handlers [PRIVATE] -------------------------------------- void PreviewTextInputHandler(object sender, TextCompositionEventArgs e) { string text; if (this.AssociatedObject.Text.Length < this.AssociatedObject.CaretIndex) text = this.AssociatedObject.Text; else { // Remaining text after removing selected text. string remainingTextAfterRemoveSelection; text = TreatSelectedText(out remainingTextAfterRemoveSelection) ? remainingTextAfterRemoveSelection.Insert(AssociatedObject.SelectionStart, e.Text) : AssociatedObject.Text.Insert(this.AssociatedObject.CaretIndex, e.Text); } e.Handled = !ValidateText(text); } /// <summary> /// PreviewKeyDown event handler /// </summary> void PreviewKeyDownHandler(object sender, KeyEventArgs e) { if (string.IsNullOrEmpty(this.EmptyValue)) return; string text = null; // Handle the Backspace key if (e.Key == Key.Back) { if (!this.TreatSelectedText(out text)) { if (AssociatedObject.SelectionStart > 0) text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart - 1, 1); } } // Handle the Delete key else if (e.Key == Key.Delete) { // If text was selected, delete it if (!this.TreatSelectedText(out text) && this.AssociatedObject.Text.Length > AssociatedObject.SelectionStart) { // Otherwise delete next symbol text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, 1); } } if (text == string.Empty) { this.AssociatedObject.Text = this.EmptyValue; if (e.Key == Key.Back) AssociatedObject.SelectionStart++; e.Handled = true; } } private void PastingHandler(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(DataFormats.Text)) { string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text)); if (!ValidateText(text)) e.CancelCommand(); } else e.CancelCommand(); } #endregion Event handlers [PRIVATE] ----------------------------------- #region Auxiliary methods [PRIVATE] ----------------------------------- /// <summary> /// Validate certain text by our regular expression and text length conditions /// </summary> /// <param name="text"> Text for validation </param> /// <returns> True - valid, False - invalid </returns> private bool ValidateText(string text) { return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength); } /// <summary> /// Handle text selection /// </summary> /// <returns>true if the character was successfully removed; otherwise, false. </returns> private bool TreatSelectedText(out string text) { text = null; if (AssociatedObject.SelectionLength <= 0) return false; var length = this.AssociatedObject.Text.Length; if (AssociatedObject.SelectionStart >= length) return true; if (AssociatedObject.SelectionStart + AssociatedObject.SelectionLength >= length) AssociatedObject.SelectionLength = length - AssociatedObject.SelectionStart; text = this.AssociatedObject.Text.Remove(AssociatedObject.SelectionStart, AssociatedObject.SelectionLength); return true; } #endregion Auxiliary methods [PRIVATE] -------------------------------- }

El uso es bastante sencillo:

<i:Interaction.Behaviors> <behaviours:TextBoxInputRegExBehaviour RegularExpression="^/d+$" MaxLength="9" EmptyValue="0" /> </i:Interaction.Behaviors>


Estaba trabajando con una caja independiente para un proyecto simple en el que estaba trabajando, por lo que no podía usar el enfoque de enlace estándar. En consecuencia, creé un truco simple que otros podrían encontrar muy útil simplemente extendiendo el control TextBox existente:

namespace MyApplication.InterfaceSupport { public class NumericTextBox : TextBox { public NumericTextBox() : base() { TextChanged += OnTextChanged; } public void OnTextChanged(object sender, TextChangedEventArgs changed) { if (!String.IsNullOrWhiteSpace(Text)) { try { int value = Convert.ToInt32(Text); } catch (Exception e) { MessageBox.Show(String.Format("{0} only accepts numeric input.", Name)); Text = ""; } } } public int? Value { set { if (value != null) { this.Text = value.ToString(); } else Text = ""; } get { try { return Convert.ToInt32(this.Text); } catch (Exception ef) { // Not numeric. } return null; } } } }

Obviamente, para un tipo flotante, querría analizarlo como un flotador y así sucesivamente. Se aplican los mismos principios.

Luego, en el archivo XAML debe incluir el espacio de nombres relevante:

<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl" [ Snip ] xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport" >

Después de eso puedes usarlo como un control regular:

<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >


Este es el único código necesario:

void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { e.Handled = new Regex("[^0-9]+").IsMatch(e.Text); }

Esto solo permite que se ingresen números en el cuadro de texto.

Para permitir un punto decimal o un signo menos, puede cambiar la expresión regular a [^0-9.-]+ .


Otro enfoque será usar un comportamiento adjunto, implementé mi clase personalizada TextBoxHelper , que se puede usar en cuadros de texto en todo mi proyecto. Porque pensé que la suscripción a los eventos para cada cuadro de texto y en cada archivo XAML individual para este propósito puede llevar mucho tiempo.

La clase TextBoxHelper que implementé tiene estas características:

  • Filtrar y aceptar solo números en formato Doble , Int , Uint y Natural
  • Filtrado y aceptando sólo Incluso o impares números
  • Manejar el controlador de eventos de pegado para evitar pegar texto no válido en nuestros cuadros de texto numéricos
  • Puede establecer un valor predeterminado que se utilizará para evitar que los datos no válidos sean la última toma suscribiéndose al evento TextChanged de los cuadros de texto

Aquí está la implementación de la clase TextBoxHelper:

public static class TextBoxHelper { #region Enum Declarations public enum NumericFormat { Double, Int, Uint, Natural } public enum EvenOddConstraint { All, OnlyEven, OnlyOdd } #endregion #region Dependency Properties & CLR Wrappers public static readonly DependencyProperty OnlyNumericProperty = DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper), new PropertyMetadata(null, DependencyPropertiesChanged)); public static void SetOnlyNumeric(TextBox element, NumericFormat value) => element.SetValue(OnlyNumericProperty, value); public static NumericFormat GetOnlyNumeric(TextBox element) => (NumericFormat) element.GetValue(OnlyNumericProperty); public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper), new PropertyMetadata(null, DependencyPropertiesChanged)); public static void SetDefaultValue(TextBox element, string value) => element.SetValue(DefaultValueProperty, value); public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty); public static readonly DependencyProperty EvenOddConstraintProperty = DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper), new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged)); public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) => element.SetValue(EvenOddConstraintProperty, value); public static EvenOddConstraint GetEvenOddConstraint(TextBox element) => (EvenOddConstraint)element.GetValue(EvenOddConstraintProperty); #endregion #region Dependency Properties Methods private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (!(d is TextBox textBox)) throw new Exception("Attached property must be used with TextBox."); switch (e.Property.Name) { case "OnlyNumeric": { var castedValue = (NumericFormat?) e.NewValue; if (castedValue.HasValue) { textBox.PreviewTextInput += TextBox_PreviewTextInput; DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler); } else { textBox.PreviewTextInput -= TextBox_PreviewTextInput; DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler); } break; } case "DefaultValue": { var castedValue = (string) e.NewValue; if (castedValue != null) { textBox.TextChanged += TextBox_TextChanged; } else { textBox.TextChanged -= TextBox_TextChanged; } break; } } } #endregion private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { var textBox = (TextBox)sender; string newText; if (textBox.SelectionLength == 0) { newText = textBox.Text.Insert(textBox.SelectionStart, e.Text); } else { var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength); newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text); } var evenOddConstraint = GetEvenOddConstraint(textBox); switch (GetOnlyNumeric(textBox)) { case NumericFormat.Double: { if (double.TryParse(newText, out double number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.Handled = true; else e.Handled = false; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.Handled = true; else e.Handled = false; break; } } else e.Handled = true; break; } case NumericFormat.Int: { if (int.TryParse(newText, out int number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.Handled = true; else e.Handled = false; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.Handled = true; else e.Handled = false; break; } } else e.Handled = true; break; } case NumericFormat.Uint: { if (uint.TryParse(newText, out uint number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.Handled = true; else e.Handled = false; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.Handled = true; else e.Handled = false; break; } } else e.Handled = true; break; } case NumericFormat.Natural: { if (uint.TryParse(newText, out uint number)) { if (number == 0) e.Handled = true; else { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.Handled = true; else e.Handled = false; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.Handled = true; else e.Handled = false; break; } } } else e.Handled = true; break; } } } private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e) { var textBox = (TextBox)sender; if (e.DataObject.GetDataPresent(typeof(string))) { var clipboardText = (string) e.DataObject.GetData(typeof(string)); var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText); var evenOddConstraint = GetEvenOddConstraint(textBox); switch (GetOnlyNumeric(textBox)) { case NumericFormat.Double: { if (double.TryParse(newText, out double number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.CancelCommand(); break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.CancelCommand(); break; } } else e.CancelCommand(); break; } case NumericFormat.Int: { if (int.TryParse(newText, out int number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.CancelCommand(); break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.CancelCommand(); break; } } else e.CancelCommand(); break; } case NumericFormat.Uint: { if (uint.TryParse(newText, out uint number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.CancelCommand(); break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.CancelCommand(); break; } } else e.CancelCommand(); break; } case NumericFormat.Natural: { if (uint.TryParse(newText, out uint number)) { if (number == 0) e.CancelCommand(); else { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) e.CancelCommand(); break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) e.CancelCommand(); break; } } } else { e.CancelCommand(); } break; } } } else { e.CancelCommand(); } } private static void TextBox_TextChanged(object sender, TextChangedEventArgs e) { var textBox = (TextBox)sender; var defaultValue = GetDefaultValue(textBox); var evenOddConstraint = GetEvenOddConstraint(textBox); switch (GetOnlyNumeric(textBox)) { case NumericFormat.Double: { if (double.TryParse(textBox.Text, out double number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) textBox.Text = defaultValue; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) textBox.Text = defaultValue; break; } } else textBox.Text = defaultValue; break; } case NumericFormat.Int: { if (int.TryParse(textBox.Text, out int number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) textBox.Text = defaultValue; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) textBox.Text = defaultValue; break; } } else textBox.Text = defaultValue; break; } case NumericFormat.Uint: { if (uint.TryParse(textBox.Text, out uint number)) { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) textBox.Text = defaultValue; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) textBox.Text = defaultValue; break; } } else textBox.Text = defaultValue; break; } case NumericFormat.Natural: { if (uint.TryParse(textBox.Text, out uint number)) { if(number == 0) textBox.Text = defaultValue; else { switch (evenOddConstraint) { case EvenOddConstraint.OnlyEven: if (number % 2 != 0) textBox.Text = defaultValue; break; case EvenOddConstraint.OnlyOdd: if (number % 2 == 0) textBox.Text = defaultValue; break; } } } else { textBox.Text = defaultValue; } break; } } } }

Y aquí hay algunos ejemplos de su fácil uso:

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double" viewHelpers:TextBoxHelper.DefaultValue="1"/>

O

<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural" viewHelpers:TextBoxHelper.DefaultValue="3" viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>

Tenga en cuenta que mi TextBoxHelper reside en el alias xmlns de viewHelpers.

Espero que esta implementación facilite el trabajo de otros :)


Para aquellos que buscan una implementación rápida y muy simple para este tipo de problema usando solo números enteros y decimales, en su archivo XAML, agregue una propiedad PreviewTextInput a su TextBox y luego en su archivo xaml.cs use:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) { e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == ''.''; }

Es un poco redundante seguir revisando toda la cadena a menos que, como han mencionado otros, esté haciendo algo con notación científica (aunque, si está agregando ciertos caracteres como ''e'', ​​una simple expresión regular que agrega símbolos / caracteres es Realmente simple e ilustrado en otras respuestas). Pero para valores de punto flotante simples, esta solución será suficiente.

Escrito como una sola línea con una expresión lambda:

private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == ''.'');


Permití números de teclado numérico y retroceso:

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { int key = (int)e.Key; e.Handled = !(key >= 34 && key <= 43 || key >= 74 && key <= 83 || key == 2); }


Podemos hacer la validación en el cuadro de texto evento cambiado. La siguiente implementación evita la entrada de pulsación de tecla distinta de numérica y un punto decimal.

private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e) { TextBox textBox = sender as TextBox; Int32 selectionStart = textBox.SelectionStart; Int32 selectionLength = textBox.SelectionLength; String newText = String.Empty; int count = 0; foreach (Char c in textBox.Text.ToCharArray()) { if (Char.IsDigit(c) || Char.IsControl(c) || (c == ''.'' && count == 0)) { newText += c; if (c == ''.'') count += 1; } } textBox.Text = newText; textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart : textBox.Text.Length; }


Si no desea escribir mucho código para realizar una función básica (no sé por qué la gente usa métodos largos), puede hacer esto:

  1. Añadir espacio de nombres:

    using System.Text.RegularExpressions;

  2. En XAML, establece una propiedad TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>

  3. En WPF bajo el método txt1_TextChanged, agregue Regex.Replace :

    private void txt1_TextChanged(object sender, TextChangedEventArgs e) { txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", ""); }


También podría simplemente implementar una regla de validación y aplicarla al TextBox:

<TextBox> <TextBox.Text> <Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <conv:OnlyDigitsValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text>

Con la implementación de la regla como sigue (usando el mismo Regex que se propone en otras respuestas):

public class OnlyDigitsValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { var validationResult = new ValidationResult(true, null); if(value != null) { if (!string.IsNullOrEmpty(value.ToString())) { var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text var parsingOk = !regex.IsMatch(value.ToString()); if (!parsingOk) { validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value"); } } } return validationResult; } }


Utilicé algo de lo que ya estaba aquí y le puse un giro propio utilizando un comportamiento para no tener que propagar este código a lo largo de una tonelada de Vistas ...

public class AllowableCharactersTextBoxBehavior : Behavior<TextBox> { public static readonly DependencyProperty RegularExpressionProperty = DependencyProperty.Register("RegularExpression", typeof(string), typeof(AllowableCharactersTextBoxBehavior), new FrameworkPropertyMetadata(".*")); public string RegularExpression { get { return (string)base.GetValue(RegularExpressionProperty); } set { base.SetValue(RegularExpressionProperty, value); } } public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register("MaxLength", typeof(int), typeof(AllowableCharactersTextBoxBehavior), new FrameworkPropertyMetadata(int.MinValue)); public int MaxLength { get { return (int)base.GetValue(MaxLengthProperty); } set { base.SetValue(MaxLengthProperty, value); } } protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewTextInput += OnPreviewTextInput; DataObject.AddPastingHandler(AssociatedObject, OnPaste); } private void OnPaste(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(DataFormats.Text)) { string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text)); if (!IsValid(text, true)) { e.CancelCommand(); } } else { e.CancelCommand(); } } void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) { e.Handled = !IsValid(e.Text, false); } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewTextInput -= OnPreviewTextInput; DataObject.RemovePastingHandler(AssociatedObject, OnPaste); } private bool IsValid(string newText, bool paste) { return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression); } private bool ExceedsMaxLength(string newText, bool paste) { if (MaxLength == 0) return false; return LengthOfModifiedText(newText, paste) > MaxLength; } private int LengthOfModifiedText(string newText, bool paste) { var countOfSelectedChars = this.AssociatedObject.SelectedText.Length; var caretIndex = this.AssociatedObject.CaretIndex; string text = this.AssociatedObject.Text; if (countOfSelectedChars > 0 || paste) { text = text.Remove(caretIndex, countOfSelectedChars); return text.Length + newText.Length; } else { var insert = Keyboard.IsKeyToggled(Key.Insert); return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length; } } }

Aquí está el código de vista relevante:

<TextBox MaxLength="50" TextWrapping="Wrap" MaxWidth="150" Margin="4" Text="{Binding Path=FileNameToPublish}" > <interactivity:Interaction.Behaviors> <v:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9./-]+$" MaxLength="50" /> </interactivity:Interaction.Behaviors> </TextBox>


Utilizar:

Private Sub DetailTextBox_PreviewTextInput( _ ByVal sender As Object, _ ByVal e As System.Windows.Input.TextCompositionEventArgs) _ Handles DetailTextBox.PreviewTextInput If _IsANumber Then If Not Char.IsNumber(e.Text) Then e.Handled = True End If End If End Sub



Añadir un evento de entrada de texto de vista previa. Como así: <TextBox PreviewTextInput="PreviewTextInput" /> .

Luego, dentro de ese conjunto, establezca e.Handled si el texto no está permitido. e.Handled = !IsTextAllowed(e.Text);

Utilizo una expresión regular simple en el método IsTextAllowed para ver si debo permitir lo que han escrito. En mi caso solo quiero permitir números, puntos y guiones.

private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text private static bool IsTextAllowed(string text) { return !_regex.IsMatch(text); }

Si desea evitar que se DataObject.Pasting datos incorrectos, conecte el evento DataObject.Pasting="TextBoxPasting" como se muestra here (código extraído):

// Use the DataObject.Pasting Handler private void TextBoxPasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(typeof(String))) { String text = (String)e.DataObject.GetData(typeof(String)); if (!IsTextAllowed(text)) { e.CancelCommand(); } } else { e.CancelCommand(); } }


Esto es lo que usaría para obtener un cuadro de texto WPF que acepte dígitos y el punto decimal:

class numericTextBox : TextBox { protected override void OnKeyDown(KeyEventArgs e) { bool b = false; switch (e.Key) { case Key.Back: b = true; break; case Key.D0: b = true; break; case Key.D1: b = true; break; case Key.D2: b = true; break; case Key.D3: b = true; break; case Key.D4: b = true; break; case Key.D5: b = true; break; case Key.D6: b = true; break; case Key.D7: b = true; break; case Key.D8: b = true; break; case Key.D9: b = true; break; case Key.OemPeriod: b = true; break; } if (b == false) { e.Handled = true; } base.OnKeyDown(e); } }

Coloque el código en un nuevo archivo de clase, agregue

using System.Windows.Controls; using System.Windows.Input;

en la parte superior del archivo y construir la solución. El control numericTextBox aparecerá en la parte superior de la caja de herramientas.


Aquí está mi versión de ello. Se basa en una ValidatingTextBoxclase base que simplemente deshace lo que se ha hecho si no es "válido". Es compatible con pegar, cortar, eliminar, retroceder, +, - etc.

Para un entero de 32 bits, hay una clase Int32TextBox que solo se compara con un int. También he añadido clases de validación de punto flotante.

public class ValidatingTextBox : TextBox { private bool _inEvents; private string _textBefore; private int _selectionStart; private int _selectionLength; public event EventHandler<ValidateTextEventArgs> ValidateText; protected override void OnPreviewKeyDown(KeyEventArgs e) { if (_inEvents) return; _selectionStart = SelectionStart; _selectionLength = SelectionLength; _textBefore = Text; } protected override void OnTextChanged(TextChangedEventArgs e) { if (_inEvents) return; _inEvents = true; var ev = new ValidateTextEventArgs(Text); OnValidateText(this, ev); if (ev.Cancel) { Text = _textBefore; SelectionStart = _selectionStart; SelectionLength = _selectionLength; } _inEvents = false; } protected virtual void OnValidateText(object sender, ValidateTextEventArgs e) => ValidateText?.Invoke(this, e); } public class ValidateTextEventArgs : CancelEventArgs { public ValidateTextEventArgs(string text) => Text = text; public string Text { get; } } public class Int32TextBox : ValidatingTextBox { protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !int.TryParse(e.Text, out var value); } public class Int64TextBox : ValidatingTextBox { protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !long.TryParse(e.Text, out var value); } public class DoubleTextBox : ValidatingTextBox { protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !double.TryParse(e.Text, out var value); } public class SingleTextBox : ValidatingTextBox { protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !float.TryParse(e.Text, out var value); } public class DecimalTextBox : ValidatingTextBox { protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !decimal.TryParse(e.Text, out var value); }

Nota 1: cuando se utiliza el enlace WPF, debe asegurarse de que utiliza la clase que se ajusta al tipo de propiedad vinculada, de lo contrario, puede dar lugar a resultados extraños.

Nota 2: Al usar clases de punto flotante con enlace WPF, asegúrese de que el enlace use la cultura actual para coincidir con el método TryParse que he usado.


PreviewTextInput += (s, e) => { e.Handled = !e.Text.All(char.IsDigit); };


e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

en la vista previa del evento keydown del cuadro de texto.