Comment puis-je obtenir un TextBox pour accepter uniquement la saisie numérique dans WPF?

335

Je cherche à accepter les chiffres et le point décimal, mais aucun signe.

J'ai examiné des exemples à l'aide du contrôle NumericUpDown pour Windows Forms et cet exemple d'un contrôle personnalisé NumericUpDown de Microsoft . Mais jusqu'à présent, il semble que NumericUpDown (pris en charge par WPF ou non) ne fournira pas les fonctionnalités que je souhaite. La façon dont mon application est conçue, personne sensé ne voudra jouer avec les flèches. Ils n'ont aucun sens pratique, dans le cadre de ma candidature.

Je recherche donc un moyen simple de faire en sorte qu'une TextBox WPF standard n'accepte que les caractères que je veux. Est-ce possible? Est-ce pratique?

Giffyguy
la source

Réponses:

418

Ajoutez un événement de saisie de texte d'aperçu. Comme ceci: <TextBox PreviewTextInput="PreviewTextInput" />.

Ensuite, à l'intérieur, définissez le e.Handledsi le texte n'est pas autorisé.e.Handled = !IsTextAllowed(e.Text);

J'utilise une regex simple dans la IsTextAllowedméthode pour voir si je dois autoriser ce qu'ils ont tapé. Dans mon cas, je veux uniquement autoriser les nombres, les points et les tirets.

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 vous souhaitez empêcher le collage de données incorrectes, connectez l' DataObject.Pastingévénement DataObject.Pasting="TextBoxPasting"comme indiqué ici (code extrait):

// 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();
    }
}
Rayon
la source
5
Votre expression régulière n'autorise pas la notation scientifique (1e5) si cela est important.
Ron Warholic
14
Notez que cette réponse ne vérifie que ce que vous tapez, vous pouvez donc entrer 3-.3
David Sykes
153
Le but de la réponse n'était pas de spécifier la regex parfaite, c'était de montrer comment utiliser WPF pour filtrer ce que quelqu'un tape.
Ray
28
[Espace] ne déclenche pas l'événement PreviewTextInput.
peterG
5
Quelque chose comme double.TryParse()serait probablement implémenté avec le même nombre de lignes et serait plus flexible.
Thomas Weller
190

Le gestionnaire d'événements prévisualise la saisie de texte. Ici, une expression régulière ne correspond à la saisie de texte que si elle n'est pas un nombre, puis elle n'est pas effectuée dans la zone de texte d'entrée.

Si vous ne voulez que des lettres, remplacez l'expression régulière par [^a-zA-Z].

XAML

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

FICHIER 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
la source
1
Le gestionnaire d'événements est un aperçu de texte. Ici, l'expression régulière ne correspond à la saisie de texte que si ce n'est pas un nombre, elle n'est pas effectuée dans la zone de texte d'entrée. Si vous ne voulez que des alphabets, remplacez l'expression régulière par [^ a-zA-Z].
Kishor
Qu'en est-il des nombres, des décimales et des opérateurs?
Jason Ebersey,
S'il vous plaît laissez-moi savoir comment l'utiliser lorsque déclare dans une autre classe STATIC et appliquer à textbox?
SHEKHAR SHETE
3
J'aime celui-ci plus que la réponse réelle, courte et simple. La réponse réelle nécessite deux méthodes pour obtenir le même résultat.
Sliver
1
@Jagd La réponse suggérée est une meilleure séparation des préoccupations. Cependant, vous pouvez également définir autant de zones de texte sur cette validation. ajoutez simplement le PreviewTextInput = "NumberValidationTextBox". (tout comme l'autre réponse!)
Sliver
84

J'ai utilisé une partie de ce qui était déjà là et y ai apporté ma propre touche en utilisant un comportement, donc je n'ai pas à propager ce code sur une tonne de vues ...

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

Voici le code de vue pertinent:

<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
la source
1
Inspiré par cette solution géniale, j'ai implémenté quelques améliorations. Veuillez le vérifier ci-dessous dans le fil.
Alex Klaus
2
Salut. Je sais que c'est un peu tard mais j'essaye de l'implémenter mais je continue à recevoir des erreurs. Je suppose que je manque des références. Y en a-t-il qui sont destinés à être saisis à part les valeurs par défaut après avoir créé une classe?
Offre
1
@Offer Oui, assurez-vous d'inclure xmlns: interactivity = " schemas.microsoft.com/expression/2010/interactivity " en haut de votre fenêtre xaml.
WiteCastle
L'expression est désormais obsolète. Bien que cette approche soit propre, elle utilise du code qui n'est plus maintenu.
Robert Baker
1
Donc, si vous modifiez la fonction IsValid pour retourner! ExceedsMaxLength (newText, paste) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); alors cela évaluera la chaîne entière. Btw - J'adore cette option avec les comportements !!
Rogala
59

Il s'agit d'une solution améliorée de la réponse de WilP . Mes améliorations sont:

  • Amélioration du comportement sur Del et Backspace boutons
  • EmptyValuePropriété ajoutée , si une chaîne vide est inappropriée
  • Correction de quelques fautes de frappe mineures
/// <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] --------------------------------
}

L'utilisation est assez simple:

<i:Interaction.Behaviors>
    <behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
Alex Klaus
la source
1
Cette solution est plutôt meilleure. Mais vous avez fait une petite erreur: lorsque vous ne définissez pas le, MaxLengthvotre condition (this.MaxLength == 0 || text.Length <= this.MaxLength)revient toujours falselors du test du nouveau texte. Cela devrait être mieux (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)puisque vous définissez int.MinValuecomme valeur par défaut pour MaxLength.
Christoph Meißner
1
Merci @Christoph, oui, tu as raison. J'ai modifié ma réponse.
Alex Klaus
@AlexKlaus, cela fonctionne très bien jusqu'à ce que j'ajoute UpdateSourceTrigger=PropertyChangedà la liaison. Une idée sur la façon de faire fonctionner ce code lorsque vous changez le UpdateSourceTriggerest défini sur PropertyChanged? Merci d'avoir partagé ce code.
Junior
32

Voici un moyen très simple et facile de le faire en utilisant MVVM.

Liez votre textBox avec une propriété entière dans le modèle de vue, et cela fonctionnera comme une gemme ... cela affichera même la validation lorsqu'un non-entier est entré dans la zone de texte.

Code XAML:

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

Voir le code du modèle:

private long _contactNo;
public long contactNo
{
    get { return _contactNo; }
    set
    {
        if (value == _contactNo)
            return;
        _contactNo = value;
        OnPropertyChanged();
    }
}
Snziv Gupta
la source
Mais la question contenait "je cherche à accepter les chiffres et le point décimal" . Le point décimal est-il accepté pour cette réponse?
Peter Mortensen
J'ai essayé de passer longà float, mais cela n'a pas fonctionné correctement avec une validation immédiate. J'ai ajouté UpdateSourceTrigger="PropertyChanged"à la liaison, afin qu'elle effectue la validation lorsque chaque caractère a été tapé, et ne pouvait plus taper un '.' dans la TextBox, sauf si un caractère illégal était présent (a dû taper "1x.234" puis supprimer le "x"). Cela semble également un peu lent dans ce mode. Cela semble utiliser System.Number.ParseSingle()pour faire le travail, donc il accepte une variété de notations.
fadden
@wolle n'est probablement pas voté car il n'explique pas comment fonctionne la validation.
Paul McCarthy
26

Ajoutez une RÈGLE DE VALIDATION pour que lorsque le texte change, vérifiez si les données sont numériques et si c'est le cas, permettez au traitement de continuer, et si ce n'est pas le cas, demandez à l'utilisateur que seules les données numériques sont acceptées dans ce champ.

En savoir plus sur la validation dans Windows Presentation Foundation

Stephen Wrighton
la source
6
Ce n'est pas vraiment une réponse pour les normes SO.
Robert Baker
Cela semble être la manière .net de le faire.
Telemat
1
La est la bonne réponse: la validation doit être au modèle viene ou au niveau du modèle. De plus, vous pouvez simplement vous lier à un type numérique comme doubleet cela vous fournit déjà une validation standard.
24

La boîte à outils WPF étendue en a une: NumericUpDown entrez la description de l'image ici

Brian Lagunas
la source
J'ai essayé ce contrôle mais cela pose des problèmes lors de l'utilisation du spinner avec UpdateSourceTrigger = PropertyChanged et il est en général difficile pour l'utilisateur d'entrer la notation scientifique.
Menno Deij - van Rijswijk
5
Veuillez noter que NumericUpDownc'est désormais obsolète. vous pouvez utiliser à DecimalUpDownpartir de la boîte à outils étendue WPF Toolkit ™ Community Edition
itsho
20

Pourrait également simplement implémenter une règle de validation et l'appliquer à la TextBox:

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

Avec la mise en œuvre de la règle comme suit (en utilisant le même Regex que proposé dans d'autres réponses):

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
la source
Si vous souhaitez saisir des chiffres décimaux, ne renvoyez pas "valide" lorsque le texte se termine par "." Veuillez vous référer à stackoverflow.com/a/27838893/417939
YantingChen
15

Ici, j'ai une solution simple inspirée de la réponse de Ray . Cela devrait être suffisant pour identifier toute forme de numéro.

Cette solution peut également être facilement modifiée si vous ne souhaitez que des nombres positifs, des valeurs entières ou des valeurs précises avec un nombre maximum de décimales, etc.


Comme suggéré dans la réponse de Ray , vous devez d'abord ajouter un PreviewTextInputévénement:

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Mettez ensuite ce qui suit dans le code:

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);
}
Anthony
la source
4
j'aime beaucoup cette réponse, simple et efficace +
Pulle
dieu et simple mais c'est moche qu'il laisse des espaces
Momo
2
Cela permet toujours à quelqu'un de simplement coller une chaîne dans la zone de texte
FCin
8

J'ai autorisé les numéros du clavier numérique et le retour arrière:

    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
la source
8
Je recommanderais d'utiliser les valeurs d'énumération au lieu des nombres magiques :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

Je suppose que:

  1. Votre TextBox pour laquelle vous souhaitez autoriser la saisie numérique a uniquement sa propriété Text initialement définie sur une valeur numérique valide (par exemple, 2,7172).

  2. Votre Textbox est un enfant de votre fenêtre principale

  3. Votre fenêtre principale est de la classe Window1

  4. Votre nom TextBox est numericTB

Idée basique:

  1. Ajouter: private string previousText;à votre classe de fenêtre principale (Window1)

  2. Ajouter: previousText = numericTB.Text;à votre constructeur de fenêtre principale

  3. Créez un gestionnaire pour que l'événement numericTB.TextChanged soit quelque chose comme ceci:

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

Cela conservera la valeur de previousText sur numericTB.Text tant qu'il est valide et définira numericTB.Text sur sa dernière valeur valide si l'utilisateur écrit quelque chose que vous n'aimez pas. Bien sûr, ce n'est qu'une idée de base, et c'est juste "résistant aux idiots", pas "à l'épreuve des idiots". Il ne gère pas le cas dans lequel l'utilisateur joue avec des espaces, par exemple. Voici donc une solution complète qui, à mon avis, est "à l'épreuve des idiots", et si je me trompe, dites-le moi:

  1. Contenu de votre fichier 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. Contenu de votre fichier 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;
                    }
                }
            }
        }
    }

Et c'est tout. Si vous avez plusieurs TextBox, je vous recommande de créer un CustomControl qui hérite de TextBox, afin que vous puissiez envelopper previousText et numericTB_TextChanged dans un fichier séparé.

user666535
la source
Wow c'est génial! Comment pourrais-je autoriser un symbole négatif à l'avant?
theNoobGuy
6

C'est le seul code nécessaire:

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

Cela permet uniquement de saisir des nombres dans la zone de texte.

Pour autoriser un point décimal ou un signe moins, vous pouvez remplacer l'expression régulière par [^0-9.-]+.

Danny Beckett
la source
1
Très bonne solution, sauf pour un nitpick mineur: cela ne vous empêchera pas d'entrer des espaces blancs, car ceux-ci ne déclenchent pas l'événement PreviewTextInput.
Tim Pohlmann
Le retour arrière ne le déclenche pas aussi bien.
Ailier Sendon du
6

Si vous ne voulez pas écrire beaucoup de code pour faire une fonction de base (je ne sais pas pourquoi les gens font des méthodes longues), vous pouvez simplement faire ceci:

  1. Ajouter un espace de noms:

    using System.Text.RegularExpressions;
  2. En XAML, définissez une propriété TextChanged:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. Dans WPF sous la méthode txt1_TextChanged, ajoutez Regex.Replace:

    private void txt1_TextChanged(object sender, TextChangedEventArgs e)
    {
        txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
    }
iato
la source
2
Il est beaucoup plus propre d'utiliser un comportement ou une propriété attachée. Pas de violon codé dans chaque vue / pour chaque zone de texte
Sir Rufo
Peut fonctionner mais est moche car la carotte sautera en position avant de la zone de texte
Momo
6

Une autre approche consistera à utiliser un comportement attaché, j'ai implémenté ma classe TextBoxHelper personnalisée , qui peut être utilisée sur des zones de texte partout dans mon projet. Parce que je pensais que l'abonnement aux événements pour chaque zone de texte et dans chaque fichier XAML individuel à cette fin peut prendre du temps.

La classe TextBoxHelper que j'ai implémentée présente les caractéristiques suivantes:

  • Filtrage et accepter uniquement des chiffres en double , Int , Uint et naturel Format
  • Filtrage et accepter seulement Même ou Odd numéros
  • Gestion du gestionnaire d'événements de collage pour éviter de coller du texte non valide dans nos zones de texte numériques
  • Peut définir une valeur par défaut qui sera utilisée pour empêcher les données non valides comme dernière prise en vous abonnant à l'événement TextChanged des zones de texte

Voici l'implémentation de la classe 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;
            }
        }
    }
}

Et voici un exemple de son utilisation facile:

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

Ou

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

Notez que mon TextBoxHelper réside dans l'alias xmlns viewHelpers.

J'espère que cette implémentation facilite le travail de quelqu'un d'autre :)

Amir Mahdi Nassiri
la source
1
C'est génial lorsque vous utilisez une zone de texte dans un DataTemplate, merci!
2018
3
Excellente réponse mais je trouve vos méthodes difficiles à lire. Vous devriez probablement les décomposer en plus petits. Voir Quelle est la durée idéale d'une méthode pour vous?
Anthony
Merci pour les commentaires constructifs @Anthony
Amir Mahdi Nassiri
4
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;

dans l'aperçu de l'événement keydown de la zone de texte.

Novice
la source
3
Ne permet pas le retour arrière cependant.
sventevit
2
Le retour arrière est de 2, l'onglet est de 3
Daniel
6
-1 parce que d'après mon expérience, ce genre de trucs intelligents finit par vous mordre dans le cul, comme certains autres commentateurs l'ont noté.
DonkeyMaster
La flèche gauche est 23, la flèche droite est 25.
Aaron
4
PreviewTextInput += (s, e) =>
{
    e.Handled = !e.Text.All(char.IsDigit);
};
Julian Kowalczuk
la source
2
cela n'acceptera pas non plus le point ., car e.Textne renvoie que le dernier caractère saisi et un point échouera à la IsDigitvérification.
Anthony
4

Pour ceux qui recherchent une implémentation rapide et très simple pour ce type de problème en utilisant uniquement des nombres entiers et décimaux, dans votre fichier XAML, ajoutez une PreviewTextInputpropriété à votre TextBoxpuis dans votre fichier xaml.cs utilisez:

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

C'est un peu redondant de continuer à vérifier la chaîne entière à chaque fois, sauf si, comme d'autres l'ont mentionné, vous faites quelque chose avec une notation scientifique (bien que, si vous ajoutez certains caractères comme «e», une simple expression régulière ajoutant des symboles / caractères soit vraiment simple et illustré dans d'autres réponses). Mais pour les valeurs à virgule flottante simples, cette solution suffira.

Écrit comme une ligne avec une expression lambda:

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

Nous pouvons faire la validation sur l'événement modifié de la zone de texte. L'implémentation suivante empêche la saisie au clavier autre que numérique et un point décimal.

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
la source
3

Que dis-tu de ça? Fonctionne bien pour moi. J'espère que je n'ai raté aucun cas de bord ...

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
la source
2

Dans Windows Forms, c'était facile; vous pouvez ajouter un événement pour KeyPress et tout fonctionne facilement. Cependant, dans WPF, cet événement n'existe pas. Mais il existe un moyen beaucoup plus simple.

Le WPF TextBox a l'événement TextChanged qui est général pour tout. Cela comprend le collage, la dactylographie et tout ce qui peut vous venir à l'esprit.

Vous pouvez donc faire quelque chose comme ceci:

XAML:

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

CODE DERRIÈRE:

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

Cela accepte également ., si vous ne le souhaitez pas, supprimez-le simplement de la regexdéclaration@[^\d] .

Remarque : Cet événement peut être utilisé sur de nombreuses TextBox car il utilise le sendertexte de l' objet. Vous écrivez l'événement une seule fois et pouvez l'utiliser pour plusieurs TextBox'es.

Toutes les personnes
la source
2

Maintenant, je sais que cette question a une réponse acceptée , mais personnellement, je trouve cela un peu déroutant et je pense que cela devrait être plus facile que cela. Je vais donc essayer de montrer comment je l'ai fait fonctionner du mieux que je peux:

Dans Windows Forms , il existe un événement appelé KeyPressqui convient parfaitement à ce type de tâche. Mais cela n'existe pas dans WPF , donc à la place, nous utiliserons l' PreviewTextInputévénement. De plus, pour la validation, je crois que l'on peut utiliser un foreachpour parcourir le textbox.Textet vérifier s'il correspond ;) la condition, mais honnêtement, c'est ce que les expressions régulières servent les .

Encore une chose avant de plonger dans le code sacré . Pour que l'événement soit déclenché, on peut faire deux choses:

  1. Utilisez XAML pour indiquer au programme quelle fonction appeler: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Faites-le dans le Loadedcas du formulaire (dans lequel se trouve le textBox): textBox.PreviewTextInput += onlyNumeric;

Je pense que la deuxième méthode est meilleure parce que dans des situations comme celle-ci, vous devrez surtout appliquer la même condition ( regex) à plusieurs TextBoxet vous ne voulez pas vous répéter!.

Enfin, voici comment procéder:

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
la source
2

En voici ma version. C'est basé sur une baseValidatingTextBox classe de qui annule simplement ce qui a été fait s'il n'est pas "valide". Il prend en charge le collage, la coupe, la suppression, le retour arrière, +, - etc.

Pour un entier 32 bits, il existe une classe Int32TextBox qui se compare simplement à un int. J'ai également ajouté des classes de validation à virgule flottante.

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

Remarque 1: lorsque vous utilisez la liaison WPF, vous devez vous assurer que vous utilisez la classe qui correspond au type de propriété lié sinon, cela peut conduire à des résultats étranges.

Remarque 2: lorsque vous utilisez des classes à virgule flottante avec une liaison WPF, assurez-vous que la liaison utilise la culture actuelle pour correspondre à la méthode TryParse que j'ai utilisée.

Simon Mourier
la source
1

Utilisation:

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
la source
Une explication serait de mise.
Peter Mortensen
1

Je travaillais avec une boîte non liée pour un projet simple sur lequel je travaillais, donc je ne pouvais pas utiliser l'approche de reliure standard. Par conséquent, j'ai créé un hack simple que d'autres pourraient trouver très pratique en étendant simplement le contrôle TextBox existant:

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

Évidemment, pour un type flottant, vous voudrez l'analyser en tant que flottant et ainsi de suite. Les mêmes principes s'appliquent.

Ensuite, dans le fichier XAML, vous devez inclure l'espace de nom approprié:

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

Après cela, vous pouvez l'utiliser comme un contrôle régulier:

<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
glénatron
la source
1

Après avoir utilisé certaines des solutions ici pendant un certain temps, j'ai développé la mienne qui fonctionne bien pour ma configuration MVVM. Notez qu'il n'est pas aussi dynamique que certains des autres dans le sens de permettre toujours aux utilisateurs d'entrer des caractères erronés, mais cela les empêche d'appuyer sur le bouton et donc de faire quoi que ce soit. Cela va bien avec mon thème de grisonner les boutons lorsque les actions ne peuvent pas être effectuées.

Je dois TextBoxqu'un utilisateur doit entrer un certain nombre de pages de document à imprimer:

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

... avec cette propriété de liaison:

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

        _numberPagesToPrint = value;
        OnPropertyChanged("NumberPagesToPrint");
    }
}

J'ai aussi un bouton:

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

... avec cette liaison de commande:

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

        return _setNumberPagesCommand;
    }
}

Et puis il y a la méthode de SetNumberOfPages(), mais ce n'est pas important pour ce sujet. Cela fonctionne bien dans mon cas, car je n'ai pas besoin d'ajouter de code dans le fichier code-behind de View et cela me permet de contrôler le comportement à l'aide de la Commandpropriété.

BK
la source
1

Dans l'application WPF, vous pouvez gérer cela en gérant l' TextChangedévénement:

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
la source
1

Pour les développeurs qui souhaitent que leurs champs de texte acceptent uniquement les numéros non signés, tels que les ports 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
la source
2
Notez que si vous voulez vraiment utiliser cette méthode avec un champ de port de socket; Vous devez vérifier si l'entier est inférieur ou égal à 65535. S'il est supérieur, ce n'est pas un port valide. En outre, le réglage de la TextBox.MaxLengthà 5contribuerait par programmation ou en XAML .
Beyondo
0

Voici ce que j'utiliserais pour obtenir une zone de texte WPF qui accepte les chiffres et le point décimal:

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

Mettez le code dans un nouveau fichier de classe, ajoutez

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

en haut du fichier et créez la solution. Le contrôle numericTextBox apparaîtra alors en haut de la boîte à outils.

matsolof
la source
1
Voir la solution beaucoup plus simple antérieure utilisant NumberValidationTextBox et des expressions régulières. C'est ridicule.
Scott Shaw-Smith
@ ScottShaw-Smith La solution acceptée est peut-être moins de code, mais ce n'est pas plus rapide que cela. Il y a toujours des projets qui nécessitent beaucoup de puissance de traitement plutôt que d'utiliser regex.
Beyondo