¿Cómo hago para que un TextBox solo acepte entradas numéricas en WPF?

335

Estoy buscando aceptar dígitos y el punto decimal, pero ningún signo.

He mirado ejemplos usando el control NumericUpDown para Windows Forms, y este ejemplo 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. Por la forma en que 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 solicitud.

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

Giffyguy
fuente

Respuestas:

418

Agregue un evento de entrada de texto de vista previa. De este modo: <TextBox PreviewTextInput="PreviewTextInput" />.

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

Utilizo un IsTextAllowedmétodo simple de expresión regular 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 el pegado de datos incorrectos, conecte el DataObject.Pastingevento DataObject.Pasting="TextBoxPasting"como se muestra aquí (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();
    }
}
Rayo
fuente
55
Su expresión regular no permite la notación científica (1e5) si eso es importante.
Ron Warholic
14
Tenga en cuenta que esta respuesta solo verifica lo que escribe, por lo que puede ingresar 3-.3
David Sykes
153
El punto de la respuesta no era especificar la expresión regular perfecta, era mostrar cómo usar WPF para filtrar lo que alguien teclea.
Ray
28
[Espacio] no activa el evento PreviewTextInput.
PeterG
55
Algo así double.TryParse()probablemente se implementaría con el mismo número de líneas y sería más flexible.
Thomas Weller
190

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 se hace en 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"/>

ARCHIVO XAML.CS

using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
    Regex regex = new Regex("[^0-9]+");
    e.Handled = regex.IsMatch(e.Text);
}
Kishor
fuente
1
El controlador de eventos es una entrada de texto de vista previa. Aquí la expresión regular coincide con la entrada de texto solo si no es un número, entonces no se hace en el cuadro de texto de entrada. Si solo desea alfabetos, reemplace la expresión regular como [^ a-zA-Z].
Kishor
¿Qué pasa con los números, decimales y operadores?
Jason Ebersey
Por favor, avíseme cómo usarlo cuando declare en alguna otra clase ESTÁTICA y aplique al cuadro de texto
SHEKHAR SHETE
3
Esta me gusta más que la respuesta real, corta y simple. La respuesta real necesita dos métodos para obtener el mismo resultado.
Sliver
1
@Jagd La respuesta sugerida es una mejor separación de las preocupaciones. Sin embargo, también puede establecer tantos cuadros de texto en esta validación. simplemente agregue PreviewTextInput = "NumberValidationTextBox". (¡al igual que la otra respuesta!)
Sliver
84

Usé algo de lo que ya estaba aquí y le di mi propio toque usando un comportamiento para no tener que propagar este código a través de un montón 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>
Wil P
fuente
1
Inspirado por esta increíble solución, implementé algunas mejoras. Por favor échale un vistazo abajo en el hilo.
Alex Klaus
2
Hola. Sé que esto es un poco tarde, pero estoy tratando de implementar esto, pero sigo recibiendo errores. Supongo que me faltan algunas referencias. ¿Hay alguna que deba escribirse aparte de los valores predeterminados después de crear una clase?
Oferta
1
@Oferta Sí, asegúrese de incluir xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " en la parte superior de su ventana de xaml.
WiteCastle
La expresión ahora es obsoleta. Si bien este enfoque es limpio, utiliza código que ya no se mantiene.
Robert Baker
1
Entonces, si edita la función IsValid para devolver! ExceedsMaxLength (newText, paste) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); entonces esto evaluará toda la cadena. Por cierto - ¡Me encanta esta opción con los comportamientos!
Rogala
59

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

  • Comportamiento mejorado en los botones Supr y Retroceso
  • EmptyValuePropiedad agregada , si la cadena vacía es inapropiada
  • Se corrigieron algunos errores 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>
Alex Klaus
fuente
1
Esta solución es bastante mejor. Pero ha cometido un pequeño error: cuando no establece la MaxLengthcondición, su condición (this.MaxLength == 0 || text.Length <= this.MaxLength)vuelve siempre falseal probar el nuevo texto. Esto debería ser mejor (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)ya que establece int.MinValuecomo valor predeterminado para MaxLength.
Christoph Meißner
1
Gracias @Christoph, sí, tienes razón. He modificado mi respuesta.
Alex Klaus
@AlexKlaus esto funciona muy bien hasta que agregue UpdateSourceTrigger=PropertyChangedal enlace. ¿Alguna idea de cómo hacer que este código funcione cuando se cambia la UpdateSourceTriggerconfiguración PropertyChanged? Gracias por compartir este código.
Junior
32

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

Vincula tu textBox con una propiedad de entero en el modelo de vista, y esto funcionará como una gema ... incluso mostrará la 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 del modelo:

private long _contactNo;
public long contactNo
{
    get { return _contactNo; }
    set
    {
        if (value == _contactNo)
            return;
        _contactNo = value;
        OnPropertyChanged();
    }
}
Snziv Gupta
fuente
Pero la pregunta contenía "Estoy buscando aceptar dígitos y el punto decimal" . ¿Se acepta el punto decimal para esta respuesta?
Peter Mortensen
Intenté cambiar longa float, pero no funcionó del todo bien con la validación inmediata. Agregué UpdateSourceTrigger="PropertyChanged"el enlace, por lo que realizaría la validación a medida que se escribía cada carácter y ya no podría escribir un '.' en el cuadro de texto a menos que haya un carácter ilegal presente (tuvo que escribir "1x.234" y luego eliminar la 'x'). También se siente un poco lento en este modo. Esto parece usarse System.Number.ParseSingle()para hacer el trabajo, por lo que acepta una variedad de anotaciones.
fadden
@wolle probablemente no sea votado porque no explica cómo funciona la validación.
Paul McCarthy
26

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

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

Stephen Wrighton
fuente
66
Esto no es realmente una respuesta para los estándares SO.
Robert Baker
Esta parece ser la forma .net de hacerlo.
Telemat
1
El es la correcta respuesta: la validación debe ser de al modelo Viene o nivel de modelo. Además, simplemente puede vincularse a un tipo numérico como doubley eso ya le proporciona una validación estándar.
24

El kit de herramientas extendido de WPF tiene uno: NumericUpDown ingrese la descripción de la imagen aquí

Brian Lagunas
fuente
He intentado este control pero da problemas al usar el control giratorio con UpdateSourceTrigger = PropertyChanged y, en general, es difícil para el usuario ingresar notación científica.
Menno Deij - van Rijswijk
55
Tenga en cuenta que NumericUpDownahora está obsoleto. puede usar DecimalUpDowndesde el kit de herramientas actualizado Extended WPF Toolkit ™ Community Edition
itsho
20

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 de la siguiente manera (usando el mismo Regex propuesto 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;
    }
}
goul
fuente
Si desea ingresar dígitos decimales, no devuelva "válido" cuando el texto termine en "." Consulte stackoverflow.com/a/27838893/417939
YantingChen el
15

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 precisos para un número máximo de decimales, etc.


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

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

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

private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var textBox = sender as TextBox;
    // Use SelectionStart property to find the caret position.
    // Insert the previewed text into the existing text in the textbox.
    var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);

    double val;
    // If parsing is successful, set Handled to false
    e.Handled = !double.TryParse(fullText, out val);
}
Antonio
fuente
44
Me gusta mucho esta respuesta, simple y eficaz +
Pulle
Dios y simple pero es feo que permite espacios
Momo
2
Esto todavía le permite a alguien pegar una cadena en el cuadro de texto
FCin
8

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);
    }
Hamzeh Soboh
fuente
8
Recomiendo usar los valores de enumeración en lugar de los números mágicos :var keyEnum = (System.Windows.Input.Key) e.Key; e.Handled = !(keyEnum >= System.Windows.Input.Key.D0 && keyEnum <= System.Windows.Input.Key.D9 || keyEnum >= System.Windows.Input.Key.NumPad0 && keyEnum <= System.Windows.Input.Key.NumPad9 || keyEnum == System.Windows.Input.Key.Back);
itsho
7

Asumiré que:

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

  2. Tu Textbox es hijo de tu ventana principal

  3. Su ventana principal es de clase Window1

  4. Su nombre de TextBox es numericTB

Idea básica:

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

  2. Agregue: previousText = numericTB.Text;a su constructor de ventana principal

  3. Cree un controlador para el evento numericTB.TextChanged para que sea así:

    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á la configuración de anteriorText a numericTB.Text siempre que sea válido, y establecerá numericTB.Text 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 simplemente "resistente a los 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, díganme:

  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, para que pueda envolver anteriorText y numericTB_TextChanged en un archivo separado.

usuario666535
fuente
¡Wow esto es genial! Sin embargo, ¿cómo podría permitir un símbolo negativo en el frente?
theNoobGuy
6

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 ingresar 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.-]+.

Danny Beckett
fuente
1
Muy buena solución, excepto por un pequeño problema: no le impedirá ingresar espacios en blanco, ya que no activan el evento PreviewTextInput.
Tim Pohlmann
Retroceso no lo dispara tan bien.
Winger Sendon
6

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

  1. Añadir espacio de nombres:

    using System.Text.RegularExpressions;
  2. En XAML, establezca 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]+", "");
    }
iato
fuente
2
Es mucho más limpio usar un comportamiento o una propiedad adjunta. No hay violín de código subyacente en cada vista / para cada cuadro de texto
Sir Rufo
Puede funcionar pero es feo ya que la zanahoria saltará en la posición delantera del cuadro de texto
Momo
6

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 suscribirse 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
  • Manejo del controlador de eventos pegar para evitar pegar texto no válido en nuestros cuadros de texto numéricos
  • Puede establecer un valor predeterminado que se utilizará para evitar datos no válidos como la última toma suscribiéndose a los cuadros de texto Evento TextChanged

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 un ejemplo 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 viewHelpers xmlns.

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

Amir Mahdi Nassiri
fuente
1
Esto es genial cuando se usa un cuadro de texto dentro de un DataTemplate, ¡gracias!
NucS
3
Gran respuesta, pero encuentro que sus métodos son difíciles de leer. Probablemente deberías dividirlos en otros más pequeños. Vea ¿Cuál es la longitud ideal de un método para usted?
Anthony
Gracias por los comentarios constructivos @Anthony
Amir Mahdi Nassiri
4
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

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

Principiante
fuente
3
Sin embargo, no permite retroceder.
sventevit
2
Retroceso es 2, tab es 3
Daniel
66
-1 porque, según mi experiencia, este tipo de trucos inteligentes finalmente te muerden el culo, como han señalado otros comentaristas.
DonkeyMaster
La flecha izquierda es 23, la flecha derecha es 25.
Aaron
4
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};
Julian Kowalczuk
fuente
2
esto tampoco aceptará punto ., ya que e.Textsolo devuelve el último carácter de entrada y un punto fallará la IsDigitverificación.
Anthony
4

Para aquellos que buscan una implementación rápida y muy simple para este tipo de problema usando solo enteros y decimales, agregue una PreviewTextInputpropiedad a su archivo XAML TextBoxy 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 algo redundante seguir revisando toda la cadena cada vez, a menos que, como otros han mencionado, esté haciendo algo con notación científica (aunque, si está agregando ciertos caracteres como 'e', ​​una expresión regular simple que agrega símbolos / caracteres es realmente simple e ilustrado en otras respuestas). Pero para valores simples de coma flotante, esta solución será suficiente.

Escrito como una 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() == '.');
Chris
fuente
3

Podemos hacer la validación en el evento de cambio de cuadro de texto. La siguiente implementación evita la entrada de teclas que no sean numéricas 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;     
} 
kumar Gouraw
fuente
3

¿Qué tal esto? Funciona bien para mi Espero no haberme perdido ningún caso de borde ...

MyTextBox.PreviewTextInput += (sender, args) =>
{
    if (!int.TryParse(args.Text, out _))
    {
        args.Handled = true;
    }
};

DataObject.AddPastingHandler(MyTextBox, (sender, args) =>
{
    var isUnicodeText = args.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
    if (!isUnicodeText)
    {
        args.CancelCommand();
    }

    var data = args.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
    if (!int.TryParse(data, out _))
    {
        args.CancelCommand();
    }
});
Shahin Dohan
fuente
2

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.

Entonces 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 regexdeclaración @[^\d].

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

Todo el mundo
fuente
2

Ahora sé que esta pregunta tiene una respuesta aceptada , pero personalmente, me resulta un poco confusa y creo que debería ser más fácil que eso. Así que intentaré demostrar cómo conseguí que funcione lo mejor que pueda:

En Windows Forms , hay un evento llamado KeyPressque es perfectamente bueno para este tipo de tarea. Pero eso no existe en WPF , por lo tanto, utilizaremos el PreviewTextInputevento. Además, para la validación, creo que se puede usar a foreachpara recorrer textbox.Texty verificar si coincide ;) la condición, pero honestamente, para eso están las expresiones regulares .

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

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

¡Creo que el segundo método es mejor porque en situaciones como esta, se le requerirá que aplique la misma condición ( regex) a más de uno TextBoxy no quiera 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);
}
Amir A. Shabani
fuente
2

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

Para un entero de 32 bits, hay una clase Int32TextBox que solo se compara con un int. También he agregado clases de validación de coma 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: Al usar el enlace WPF, debe asegurarse de usar la clase que se ajuste al tipo de propiedad enlazado, de lo contrario, puede dar lugar a resultados extraños.

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

Simon Mourier
fuente
1

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
Johnny
fuente
Una explicación estaría en orden.
Peter Mortensen
1

Estaba trabajando con un cuadro independiente para un proyecto simple en el que estaba trabajando, por lo que no pude usar el enfoque de enlace estándar. En consecuencia, creé un truco simple que otros podrían encontrar bastante ú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, desearía analizarlo como flotante, etc. 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, puede usarlo como control regular:

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

Después de usar algunas de las soluciones aquí por algún tiempo, desarrollé una propia que funciona bien para mi configuración MVVM. Tenga en cuenta que no es tan dinámico como algunos de los otros en el sentido de que todavía 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 botones en gris cuando no se pueden realizar acciones.

Tengo TextBoxun usuario que debe ingresar varias páginas del documento 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 vinculante:

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 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 usando la Commandpropiedad.

BK
fuente
1

En la aplicación WPF, puede manejar esto manejando el TextChangedevento:

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;
    }
}
Mehdi Khademloo
fuente
1

Para los desarrolladores que desean que sus campos de texto acepten números sin firmar solo, como puertos de socket, etc.

WPF

<TextBox PreviewTextInput="Port_PreviewTextInput" MaxLines="1"/>

C#

private void Port_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    e.Handled = !int.TryParse(e.Text, out int x);
}
Beyondo
fuente
2
Tenga en cuenta que si realmente desea utilizar este método con un campo de puerto de socket; Debería verificar si el número entero es menor o igual que 65535. Si es mayor, entonces no es un puerto válido. Además, configurar TextBox.MaxLengtha 5ayudaría programáticamente o en XAML .
Beyondo
0

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);
    }
}

Ponga 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 compile la solución. El control numericTextBox aparecerá en la parte superior de la caja de herramientas.

matsolof
fuente
1
Vea la solución MUCHO más fácil anterior usando NumberValidationTextBox y expresiones regulares. Esto es ridículo.
Scott Shaw-Smith
@ ScottShaw-Smith Quizás la solución aceptada es menos código, pero no es más rápido que esto. Siempre hay algunos proyectos que requieren mucha potencia de procesamiento en lugar de utilizar expresiones regulares.
Beyondo