Wie kann ich eine TextBox dazu bringen, nur numerische Eingaben in WPF zu akzeptieren?


334

Ich möchte Ziffern und den Dezimalpunkt akzeptieren, aber kein Vorzeichen.

Ich habe mir Beispiele mit dem NumericUpDown-Steuerelement für Windows Forms und dieses Beispiel eines benutzerdefinierten NumericUpDown-Steuerelements von Microsoft angesehen . Bisher scheint es jedoch so, als würde NumericUpDown (von WPF unterstützt oder nicht) nicht die gewünschte Funktionalität bieten. So wie meine Anwendung gestaltet ist, wird niemand, der bei klarem Verstand ist, mit den Pfeilen herumspielen wollen. Sie ergeben im Rahmen meiner Bewerbung keinen praktischen Sinn.

Daher suche ich nach einer einfachen Möglichkeit, eine Standard-WPF-Textbox so zu gestalten, dass sie nur die gewünschten Zeichen akzeptiert. Ist das möglich? Ist es praktisch?

Antworten:


417

Fügen Sie ein Vorschau-Texteingabeereignis hinzu. Wie so : <TextBox PreviewTextInput="PreviewTextInput" />.

Dann in diesem Set die, e.Handledwenn der Text nicht erlaubt ist.e.Handled = !IsTextAllowed(e.Text);

Ich verwende eine einfache Regex- IsTextAllowedMethode, um zu sehen, ob ich zulassen soll, was sie eingegeben haben. In meinem Fall möchte ich nur Zahlen, Punkte und Striche zulassen.

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

Wenn Sie das Einfügen falscher Daten verhindern möchten, schließen Sie das DataObject.PastingEreignis DataObject.Pasting="TextBoxPasting"wie hier gezeigt an (Code-Auszug):

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

5
Ihre Regex erlaubt keine wissenschaftliche Notation (1e5), wenn dies wichtig ist.
Ron Warholic

14
Beachten Sie, dass diese Antwort nur überprüft, was Sie eingeben, so dass Sie 3-.3
David Sykes

153
Der Sinn der Antwort bestand nicht darin, den perfekten Regex anzugeben, sondern zu zeigen, wie WPF verwendet wird, um zu filtern, was jemand eingibt.
Ray

27
[Space] löst kein PreviewTextInput-Ereignis aus.
PeterG

5
So etwas double.TryParse()würde wahrscheinlich mit der gleichen Anzahl von Zeilen implementiert und flexibler sein.
Thomas Weller

190

Der Ereignishandler zeigt eine Vorschau der Texteingabe an. Hier stimmt ein regulärer Ausdruck nur dann mit der Texteingabe überein, wenn es sich nicht um eine Zahl handelt, und wird dann nicht zum Eingabe-Textfeld gemacht.

Wenn Sie nur Buchstaben möchten, ersetzen Sie den regulären Ausdruck als [^a-zA-Z].

XAML

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

XAML.CS-DATEI

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

1
Der Ereignishandler ist eine Vorschau-Texteingabe. Hier stimmt der reguläre Ausdruck nur dann mit der Texteingabe überein, wenn es sich nicht um eine Zahl handelt, und wird dann nicht zum Eingabe-Textfeld gemacht. Wenn Sie nur Alphabete möchten, ersetzen Sie den regulären Ausdruck als [^ a-zA-Z].
Kishor

Was ist mit Zahlen, Dezimalstellen und Operatoren?
Jason Ebersey

Bitte lassen Sie mich wissen, wie es verwendet wird, wenn es in einer anderen STATIC-Klasse deklariert und auf ein Textfeld angewendet wird.
SHEKHAR SHETE

3
Ich mag diese mehr als die eigentliche Antwort, kurz und einfach. Die eigentliche Antwort benötigt zwei Methoden, um das gleiche Ergebnis zu erzielen.
Splitter

1
@Jagd Die vorgeschlagene Antwort ist eine bessere Trennung von Bedenken. Sie können jedoch auch so viele Textfelder für diese Validierung festlegen. Fügen Sie einfach die PreviewTextInput = "NumberValidationTextBox" hinzu. (Genau wie die andere Antwort!)
Splitter

84

Ich habe einiges von dem verwendet, was bereits hier war, und es mit einem Verhalten selbst verändert, damit ich diesen Code nicht über eine Tonne von Ansichten hinweg verbreiten muss ...

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

Hier ist der relevante Ansichtscode:

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

1
Inspiriert von dieser großartigen Lösung habe ich einige Verbesserungen implementiert. Bitte schau es dir unten im Thread an.
Alex Klaus

2
Hallo. Ich weiß, dass dies etwas spät ist, aber ich versuche dies zu implementieren, erhalte aber immer wieder Fehler. Ich vermute, ich vermisse einige Referenzen. Gibt es welche, die anders als die Standardeinstellungen eingegeben werden sollen, nachdem Sie eine Klasse erstellt haben?
Angebot

1
@Offer Ja, stellen Sie sicher, dass xmlns: interaktivity = " schemas.microsoft.com/expression/2010/interactivity " oben in Ihrem xaml-Fenster eingefügt wird.
WiteCastle

Der Ausdruck ist jetzt veraltet. Dieser Ansatz ist zwar sauber, verwendet jedoch Code, der nicht mehr verwaltet wird.
Robert Baker

1
Wenn Sie also die IsValid-Funktion bearbeiten, um zurückzugeben! ExceedsMaxLength (newText, Einfügen) && Regex.IsMatch (String.Concat (this.AssociatedObject.Text, newText), RegularExpression); dann wird die gesamte Zeichenfolge ausgewertet. Übrigens - Ich liebe diese Option mit den Verhaltensweisen!
Rogala

59

Dies ist eine verbesserte Lösung der Antwort von WilP . Meine Verbesserungen sind:

  • Verbessertes Verhalten auf den Schaltflächen Entf und Rücktaste
  • EmptyValueEigenschaft hinzugefügt , wenn leere Zeichenfolge unangemessen ist
  • Einige kleinere Tippfehler wurden behoben
/// <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] --------------------------------
}

Die Verwendung ist ziemlich einfach:

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

1
Diese Lösung ist ziemlich besser. Aber Sie haben einen kleinen Fehler gemacht: Wenn MaxLengthSie den Zustand nicht einstellen, (this.MaxLength == 0 || text.Length <= this.MaxLength)kehrt er immer zurück, falsewenn Sie den neuen Text testen. Dies sollte besser sein, (this.MaxLength == int.MinValue || text.Length <= this.MaxLength)da Sie int.MinValueals Standardwert für festlegen MaxLength.
Christoph Meißner

1
Danke @Christoph, ja, du hast recht. Ich habe meine Antwort geändert.
Alex Klaus

@AlexKlaus das funktioniert super bis ich UpdateSourceTrigger=PropertyChangedzur Bindung hinzufüge . Jede Idee , wie dieser Code zur Arbeit zu kommen , wenn das Ändern der UpdateSourceTriggerfestgelegt ist PropertyChanged? Vielen Dank, dass Sie diesen Code geteilt haben.
Junior

32

Hier ist eine sehr einfache und einfache Möglichkeit, dies mit MVVM zu tun.

Binden Sie Ihre TextBox mit einer Ganzzahl-Eigenschaft im Ansichtsmodell, und dies funktioniert wie ein Juwel. Es wird sogar die Validierung angezeigt, wenn eine Nicht-Ganzzahl in das Textfeld eingegeben wird.

XAML-Code:

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

Modellcode anzeigen:

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

Aber die Frage enthielt "Ich möchte Ziffern und den Dezimalpunkt akzeptieren" . Wird der Dezimalpunkt für diese Antwort akzeptiert?
Peter Mortensen

Ich habe versucht , zu ändern longzu float, aber es hat nicht ganz richtig mit sofortiger Validierung arbeiten. Ich UpdateSourceTrigger="PropertyChanged"habe der Bindung hinzugefügt , damit sie die Validierung durchführt, wenn jedes Zeichen eingegeben wird, und kein 'mehr' eingeben kann. in der TextBox, es sei denn, es war ein unzulässiges Zeichen vorhanden (musste "1x.234" eingeben und dann das 'x' löschen). In diesem Modus fühlt es sich auch etwas träge an. Dies scheint für System.Number.ParseSingle()die Arbeit verwendet zu werden, daher werden verschiedene Notationen akzeptiert.
Fadden

@wolle wird wahrscheinlich nicht hochgestimmt, weil es nicht erklärt, wie die Validierung funktioniert.
Paul McCarthy

26

Fügen Sie eine GÜLTIGKEITSREGEL hinzu, damit Sie bei einer Textänderung überprüfen können, ob die Daten numerisch sind. Wenn dies der Fall ist, kann die Verarbeitung fortgesetzt werden. Wenn dies nicht der Fall ist, wird der Benutzer aufgefordert, nur numerische Daten in diesem Feld zu akzeptieren.

Weitere Informationen finden Sie unter Validierung in Windows Presentation Foundation


6
Dies ist keine wirkliche Antwort auf SO-Standards.
Robert Baker

Dies scheint die .net-Methode zu sein.
Telemat

1
Das ist die richtige Antwort: Die Validierung sollte auf viene-Modell- oder Modellebene erfolgen. Darüber hinaus können Sie einfach an einen numerischen Typ wie binden, doubleder Ihnen bereits eine Standardvalidierung bietet.

24

Das Extented WPF Toolkit verfügt über eines: NumericUpDown Geben Sie hier die Bildbeschreibung ein


Ich habe dieses Steuerelement ausprobiert, aber es gibt Probleme bei der Verwendung des Drehfelds mit UpdateSourceTrigger = PropertyChanged und es ist für den Benutzer im Allgemeinen schwierig, die wissenschaftliche Notation einzugeben.
Menno Deij - van Rijswijk

5
Bitte beachten Sie, dass dies NumericUpDownjetzt veraltet ist. Sie können verwendenDecimalUpDown aus dem aktualisierten Toolkit Extended WPF Toolkit ™ Community Edition verwenden
itsho

20

Könnte auch einfach eine Validierungsregel implementieren und auf die TextBox anwenden:

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

Mit der Implementierung der Regel wie folgt (unter Verwendung des gleichen Regex wie in anderen Antworten vorgeschlagen):

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

Wenn Sie Dezimalstellen eingeben möchten, geben Sie nicht "gültig" zurück, wenn der Text mit "endet". Bitte beziehen Sie sich auf stackoverflow.com/a/27838893/417939
YantingChen

14

Hier habe ich eine einfache Lösung inspiriert von Rays Antwort ist . Dies sollte ausreichen, um jede Form von Nummer zu identifizieren.

Diese Lösung kann auch leicht geändert werden, wenn Sie nur positive Zahlen, ganzzahlige Werte oder Werte mit einer maximalen Anzahl von Dezimalstellen usw. wünschen.


Wie in Rays Antwort vorgeschlagen , müssen Sie zuerst ein PreviewTextInputEreignis hinzufügen :

<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>

Fügen Sie dann Folgendes in den Code dahinter ein:

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

4
Ich mag diese Antwort sehr, einfach und effektiv +
Pulle

Gott und einfach, aber es ist hässlich, dass es Räume erlaubt
Momo

2
Dies erlaubt immer noch jemandem, einfach eine Zeichenfolge in das Textfeld
einzufügen

8

Ich erlaubte Ziffernblocknummern und Rücktaste:

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

8
Ich würde empfehlen, die Aufzählungswerte anstelle von Magic Numbers zu verwenden :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

Ich gehe davon aus, dass:

  1. In Ihrer TextBox, für die Sie nur numerische Eingaben zulassen möchten, ist die Eigenschaft Text zunächst auf einen gültigen Zahlenwert festgelegt (z. B. 2.7172).

  2. Ihre Textbox ist ein untergeordnetes Element Ihres Hauptfensters

  3. Ihr Hauptfenster ist von der Klasse Window1

  4. Ihr TextBox-Name ist numericTB

Grundidee:

  1. Hinzufügen: private string previousText;zu Ihrer Hauptfensterklasse (Window1)

  2. Hinzufügen: previousText = numericTB.Text;zu Ihrem Hauptfensterkonstruktor

  3. Erstellen Sie einen Handler für das numericTB.TextChanged-Ereignis, der ungefähr so ​​aussieht:

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

Dadurch wird previousText so lange wie möglich auf numericTB.Text gesetzt und numericTB.Text auf den letzten gültigen Wert gesetzt, wenn der Benutzer etwas schreibt, das Ihnen nicht gefällt. Natürlich ist dies nur eine Grundidee, und es ist nur "idiotenresistent", nicht "idiotensicher". Der Fall, in dem der Benutzer beispielsweise mit Leerzeichen herumspielt, wird nicht behandelt. Hier ist also eine vollständige Lösung, die ich für "idiotensicher" halte. Wenn ich falsch liege, sagen Sie mir bitte:

  1. Inhalt Ihrer Window1.xaml-Datei:

    <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. Inhalt Ihrer Window.xaml.cs-Datei:

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

Und das ist es. Wenn Sie viele TextBoxen haben, empfehle ich, ein CustomControl zu erstellen, das von TextBox erbt, damit Sie previousText und numericTB_TextChanged in einer separaten Datei zusammenfassen können.


Wow das ist toll! Wie könnte ich ein negatives Symbol vorne zulassen?
theNoobGuy

6

Dies ist der einzige Code, der benötigt wird:

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

Dadurch können nur Zahlen in das Textfeld eingegeben werden.

Um einen Dezimalpunkt oder ein Minuszeichen zuzulassen, können Sie den regulären Ausdruck in ändern [^0-9.-]+.


1
Sehr gute Lösung mit Ausnahme eines kleinen Nitpicks: Sie werden nicht daran gehindert, Leerzeichen einzugeben, da diese das PreviewTextInput-Ereignis nicht auslösen.
Tim Pohlmann

Backspace löst es auch nicht aus.
Flügelspieler Sendon

6

Wenn Sie nicht viel Code schreiben möchten, um eine Grundfunktion auszuführen (ich weiß nicht, warum Leute lange Methoden anwenden), können Sie dies einfach tun:

  1. Namespace hinzufügen:

    using System.Text.RegularExpressions;
  2. Legen Sie in XAML eine TextChanged-Eigenschaft fest:

    <TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
  3. Fügen Sie in WPF unter der Methode txt1_TextChanged Folgendes hinzu Regex.Replace:

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

2
Es ist viel sauberer, ein Verhalten oder eine angehängte Eigenschaft zu verwenden. Kein Code-Behind-Fummeln in jeder Ansicht / für jedes Textfeld
Sir Rufo

Kann funktionieren, ist aber hässlich, da die Karotte in die vordere Position des Textfelds springt
Momo

6

Ein anderer Ansatz ist die Verwendung eines angehängten Verhaltens. Ich habe meine benutzerdefinierte TextBoxHelper- Klasse implementiert , die für Textfelder in meinem gesamten Projekt verwendet werden kann. Weil ich mir gedacht habe, dass das Abonnieren der Ereignisse für jedes Textfeld und für jede einzelne XAML-Datei zu diesem Zweck zeitaufwändig sein kann.

Die von mir implementierte TextBoxHelper-Klasse verfügt über folgende Funktionen:

  • Filtern und Akzeptieren nur von Zahlen in Double , Int , Uint und Natural Format
  • Filterung und nur akzeptieren Selbst oder Odd Zahlen
  • Handler zum Einfügen von Ereignissen , um zu verhindern, dass ungültiger Text in unsere numerischen Textfelder eingefügt wird
  • Kann einen Standardwert festlegen , der verwendet wird, um ungültige Daten als letzte Aufnahme zu verhindern, indem das Textfeld TextChanged-Ereignis abonniert wird

Hier ist die Implementierung der TextBoxHelper-Klasse:

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

Und hier ist ein Beispiel für die einfache Verwendung:

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

Oder

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

Beachten Sie, dass sich mein TextBoxHelper im Alias ​​viewHelpers xmlns befindet.

Ich hoffe, dass diese Implementierung die Arbeit anderer erleichtert :)


1
Dies ist großartig, wenn Sie ein Textfeld in einer DataTemplate verwenden. Danke!
NucS

@ NucS Sie sind so willkommen, lieber NucS
Amir Mahdi Nassiri

3
Tolle Antwort, aber ich finde Ihre Methoden schwer zu lesen. Wahrscheinlich sollten Sie sie in kleinere zerlegen. Siehe Was ist die ideale Länge einer Methode für Sie?
Anthony

Vielen Dank für das konstruktive Feedback @Anthony
Amir Mahdi Nassiri

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

in der Vorschau Keydown-Ereignis des Textfelds.


3
Erlaubt jedoch keine Rücktaste.
Sventevit

2
Rücktaste ist 2, Tab ist 3
Daniel

6
-1 weil meiner Erfahrung nach diese Art von intelligenten Tricks dich irgendwann in den Arsch beißen, wie einige andere Kommentatoren bemerkt haben.
DonkeyMaster

Der linke Pfeil ist 23, der rechte Pfeil ist 25.
Aaron

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

2
Dies akzeptiert auch keinen Punkt ., da e.Textnur das letzte Eingabezeichen zurückgegeben wird und ein Punkt die IsDigitPrüfung nicht besteht.
Anthony

4

Wenn Sie nach einer schnellen und sehr einfachen Implementierung für diese Art von Problem suchen, bei der nur Ganzzahlen und Dezimalzahlen verwendet werden, fügen Sie in Ihrer XAML-Datei eine PreviewTextInputEigenschaft zu Ihrer hinzu TextBoxund verwenden Sie dann in Ihrer xaml.cs-Datei Folgendes:

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

Es ist überflüssig, jedes Mal die gesamte Zeichenfolge zu überprüfen, es sei denn, Sie haben, wie andere bereits erwähnt haben, etwas mit wissenschaftlicher Notation getan (wenn Sie jedoch bestimmte Zeichen wie 'e' hinzufügen, ist eine einfache Regex das Hinzufügen von Symbolen / Zeichen wirklich einfach und in anderen Antworten illustriert). Für einfache Gleitkommawerte reicht diese Lösung jedoch aus.

Geschrieben als Einzeiler mit Lambda-Ausdruck:

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

3

Wir können eine Validierung für ein vom Textfeld geändertes Ereignis durchführen. Die folgende Implementierung verhindert andere Tastendruckeingaben als numerische und einen Dezimalpunkt.

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

es funktioniert nicht bei mir :)
david2020

3

Wie wäre es damit? Funktioniert gut für mich. Hoffe ich habe keine Edge Cases verpasst ...

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

das funktioniert danke
Aljohn Yamaro

2

In Windows Forms war es einfach; Sie können ein Ereignis für KeyPress hinzufügen und alles funktioniert problemlos. In WPF ist dieses Ereignis jedoch nicht vorhanden. Aber es gibt einen viel einfacheren Weg.

Die WPF TextBox hat das TextChanged-Ereignis, das für alles allgemein ist. Es beinhaltet das Einfügen, Tippen und alles, was Ihnen in den Sinn kommt.

Sie können also so etwas tun:

XAML:

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

CODE HINTER:

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

Dies akzeptiert auch ., wenn Sie es nicht möchten, entfernen Sie es einfach aus der regexAnweisung zu sein @[^\d].

Hinweis : Dieses Ereignis kann in vielen Textfeldern verwendet werden, da es den senderText des Objekts verwendet. Sie schreiben das Ereignis nur einmal und können es für mehrere TextBoxen verwenden.


2

Jetzt weiß ich, dass diese Frage eine akzeptierte Antwort hat , aber ich persönlich finde sie etwas verwirrend und ich glaube, es sollte einfacher sein. Also werde ich versuchen zu demonstrieren, wie ich es so gut wie möglich zum Laufen gebracht habe:

In Windows Forms gibt es ein Ereignis namens, KeyPressdas für diese Art von Aufgabe perfekt geeignet ist. In WPF gibt es das jedoch nicht. Stattdessen verwenden wir das PreviewTextInputEreignis. Außerdem glaube ich, dass man für die Validierung eine verwenden kann, um die Bedingung foreachzu durchlaufen textbox.Textund zu überprüfen, ob sie übereinstimmt ;), aber ehrlich gesagt, dafür sind reguläre Ausdrücke gedacht.

Noch eine Sache, bevor wir in den heiligen Code eintauchen . Damit das Ereignis ausgelöst wird, kann man zwei Dinge tun:

  1. Verwenden Sie XAML, um dem Programm mitzuteilen, welche Funktion aufgerufen werden soll: <PreviewTextInput="textBox_PreviewTextInput/>
  2. Tun Sie dies im LoadedFalle des Formulars (in dem sich die Textbox befindet): textBox.PreviewTextInput += onlyNumeric;

Ich denke, die zweite Methode ist besser, weil Sie in solchen Situationen meistens dieselbe Bedingung ( regex) auf mehrere anwenden müssen TextBoxund sich nicht wiederholen möchten! .

Zum Schluss würden Sie Folgendes tun:

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

2

Hier ist meine Version davon. Es basiert auf einer Basisklasse ValidatingTextBox, die nur rückgängig macht, was getan wurde, wenn es nicht "gültig" ist. Es unterstützt Einfügen, Ausschneiden, Löschen, Rücktaste, +, - usw.

Für 32-Bit-Ganzzahlen gibt es eine Int32TextBox-Klasse, die nur mit einer int verglichen wird. Ich habe auch Gleitkomma-Validierungsklassen hinzugefügt.

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

Hinweis 1: Wenn Sie die WPF-Bindung verwenden, müssen Sie sicherstellen, dass Sie die Klasse verwenden, die zum gebundenen Eigenschaftstyp passt. Andernfalls kann dies zu seltsamen Ergebnissen führen.

Hinweis 2: Wenn Sie Gleitkommaklassen mit WPF-Bindung verwenden, stellen Sie sicher, dass die Bindung die aktuelle Kultur verwendet, um mit der von mir verwendeten TryParse-Methode übereinzustimmen.



1

Verwenden:

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

Eine Erklärung wäre angebracht.
Peter Mortensen

1

Ich habe mit einer ungebundenen Box für ein einfaches Projekt gearbeitet, an dem ich gearbeitet habe, daher konnte ich den Standardbindungsansatz nicht verwenden. Infolgedessen habe ich einen einfachen Hack erstellt, den andere möglicherweise sehr nützlich finden, indem sie einfach das vorhandene TextBox-Steuerelement erweitern:

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

Für einen Floating-Typ möchten Sie ihn natürlich als Float usw. analysieren. Es gelten die gleichen Grundsätze.

Dann müssen Sie in die XAML-Datei den relevanten Namespace aufnehmen:

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

Danach können Sie es als reguläre Steuerung verwenden:

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

1

Nachdem ich einige der Lösungen hier für einige Zeit verwendet hatte, entwickelte ich meine eigene, die für mein MVVM-Setup gut funktioniert. Beachten Sie, dass es nicht so dynamisch ist wie einige der anderen, da Benutzer weiterhin fehlerhafte Zeichen eingeben können, es sie jedoch daran hindert, die Taste zu drücken und somit etwas zu tun. Dies passt gut zu meinem Thema, Schaltflächen auszublenden, wenn Aktionen nicht ausgeführt werden können.

Ich habe eine, TextBoxdass ein Benutzer eine Anzahl von Dokumentseiten eingeben muss, um gedruckt zu werden:

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

... mit dieser verbindlichen Eigenschaft:

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

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

Ich habe auch einen Knopf:

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

... mit dieser Befehlsbindung:

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

        return _setNumberPagesCommand;
    }
}

Und dann gibt es die Methode von SetNumberOfPages(), aber es ist unwichtig für dieses Thema. In meinem Fall funktioniert dies gut, da ich der CodeBehind-Datei der Ansicht keinen Code hinzufügen muss und das Verhalten mithilfe der CommandEigenschaft steuern kann .



1

In der WPF-Anwendung können Sie dies behandeln, indem Sie das TextChangedEreignis behandeln:

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

1

Für Entwickler, die möchten, dass ihre Textfelder nur vorzeichenlose Zahlen akzeptieren, z. B. Socket-Ports usw.:

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

2
Beachten Sie Folgendes, wenn Sie diese Methode wirklich mit einem Socket-Port-Feld verwenden möchten. Sie müssen überprüfen, ob die Ganzzahl kleiner oder gleich ist 65535. Wenn es größer ist, ist es kein gültiger Port. Das Setzen von TextBox.MaxLengthto 5würde entweder programmgesteuert oder in XAML helfen .
Beyondo

0

Dies ist, was ich verwenden würde, um ein WPF-Textfeld zu erhalten, das Ziffern und den Dezimalpunkt akzeptiert:

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

Fügen Sie den Code in eine neue Klassendatei ein und fügen Sie ihn hinzu

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

oben in der Datei und erstellen Sie die Lösung. Das numericTextBox-Steuerelement wird dann oben in der Toolbox angezeigt.


1
Siehe die frühere VIEL einfachere Lösung mit NumberValidationTextBox und regulären Ausdrücken. Das ist lächerlich.
Scott Shaw-Smith

@ ScottShaw-Smith Vielleicht ist die akzeptierte Lösung weniger Code, aber nicht schneller. Es gibt immer einige Projekte, die viel Rechenleistung erfordern, anstatt Regex zu verwenden.
Beyondo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.