Frage

Wie gehen Sie mit der Eingabe numerischer Werte in WPF-Anwendungen um?

Ohne ein NumericUpDown-Steuerelement habe ich eine TextBox verwendet und ihr PreviewKeyDown-Ereignis mit dem folgenden Code verarbeitet, aber es ist ziemlich hässlich.

Hat jemand eine elegantere Möglichkeit gefunden, numerische Daten vom Benutzer zu erhalten, ohne auf die Kontrolle eines Drittanbieters angewiesen zu sein?

private void NumericEditPreviewKeyDown(object sender, KeyEventArgs e)
{
    bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Decimal;
    bool isNumeric = (e.Key >= Key.D0 && e.Key <= Key.D9) || e.Key == Key.OemPeriod;

    if ((isNumeric || isNumPadNumeric) && Keyboard.Modifiers != ModifierKeys.None)
    {
        e.Handled = true;
        return;
    }

    bool isControl = ((Keyboard.Modifiers != ModifierKeys.None && Keyboard.Modifiers != ModifierKeys.Shift)
        || e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Insert
        || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up
        || e.Key == Key.Tab
        || e.Key == Key.PageDown || e.Key == Key.PageUp
        || e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Escape
        || e.Key == Key.Home || e.Key == Key.End);

    e.Handled = !isControl && !isNumeric && !isNumPadNumeric;
}
War es hilfreich?

Lösung

Wie wäre es mit:

protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
    e.Handled = !AreAllValidNumericChars(e.Text);
    base.OnPreviewTextInput(e);
}

private bool AreAllValidNumericChars(string str)
{
    foreach(char c in str)
    {
        if(!Char.IsNumber(c)) return false;
    }

    return true;
}

Andere Tipps

So mache ich es.Es verwendet einen regulären Ausdruck, um zu prüfen, ob der Text im Feld numerisch ist oder nicht.

Regex NumEx = new Regex(@"^-?\d*\.?\d*$");

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    if (sender is TextBox)
    {
        string text = (sender as TextBox).Text + e.Text;
        e.Handled = !NumEx.IsMatch(text);
    }
    else
        throw new NotImplementedException("TextBox_PreviewTextInput Can only Handle TextBoxes");
}

In WPF und Silverlight gibt es jetzt eine viel bessere Möglichkeit, dies zu tun.Wenn Ihr Steuerelement an eine Eigenschaft gebunden ist, müssen Sie lediglich Ihre Bindungsanweisung ein wenig ändern.Verwenden Sie Folgendes für Ihre Bindung:

<TextBox Text="{Binding Number, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>

Beachten Sie, dass Sie dies auch für benutzerdefinierte Eigenschaften verwenden können. Sie müssen lediglich eine Ausnahme auslösen, wenn der Wert im Feld ungültig ist, und das Steuerelement wird mit einem roten Rahmen hervorgehoben.Wenn Sie oben rechts auf den roten Rand klicken, wird die Ausnahmemeldung angezeigt.

Ich habe eine angehängte Eigenschaft verwendet, um dem Benutzer zu ermöglichen, die Werte im Textfeld mit den Auf- und Ab-Tasten zu ändern.Um es zu verwenden, verwenden Sie einfach

<TextBox local:TextBoxNumbers.SingleDelta="1">100</TextBox>

Damit werden die Validierungsprobleme, auf die in dieser Frage Bezug genommen wird, nicht wirklich angesprochen, aber es geht darauf ein, was ich tue, wenn ich keine numerische Auf-/Ab-Steuerung habe.Wenn ich es eine Weile benutze, denke ich, dass es mir tatsächlich besser gefallen könnte als die alte numerische Auf-/Ab-Steuerung.

Der Code ist nicht perfekt, aber er behandelt die Fälle, für die ich ihn brauchte:

  • Up Pfeil, Down Pfeil
  • Shift + Up Pfeil, Shift + Down Pfeil
  • Page Up, Page Down
  • Bindung Converter auf der Texteigenschaft

Code behind

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace Helpers
{
    public class TextBoxNumbers
    {    
        public static Decimal GetSingleDelta(DependencyObject obj)
        {
            return (Decimal)obj.GetValue(SingleDeltaProperty);
        }

        public static void SetSingleDelta(DependencyObject obj, Decimal value)
        {
            obj.SetValue(SingleDeltaProperty, value);
        }

        // Using a DependencyProperty as the backing store for SingleValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SingleDeltaProperty =
            DependencyProperty.RegisterAttached("SingleDelta", typeof(Decimal), typeof(TextBoxNumbers), new UIPropertyMetadata(0.0m, new PropertyChangedCallback(f)));

        public static void f(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            TextBox t = o as TextBox;

            if (t == null)
                return;

            t.PreviewKeyDown += new System.Windows.Input.KeyEventHandler(t_PreviewKeyDown);
        }

        private static Decimal GetSingleValue(DependencyObject obj)
        {
            return GetSingleDelta(obj);
        }

        private static Decimal GetDoubleValue(DependencyObject obj)
        {
            return GetSingleValue(obj) * 10;
        }

        private static Decimal GetTripleValue(DependencyObject obj)
        {
            return GetSingleValue(obj) * 100;
        }

        static void t_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            TextBox t = sender as TextBox;
            Decimal i;

            if (t == null)
                return;

            if (!Decimal.TryParse(t.Text, out i))
                return;

            switch (e.Key)
            {
                case System.Windows.Input.Key.Up:
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                        i += GetDoubleValue(t);
                    else
                        i += GetSingleValue(t);
                    break;

                case System.Windows.Input.Key.Down:
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                        i -= GetDoubleValue(t);
                    else
                        i -= GetSingleValue(t);
                    break;

                case System.Windows.Input.Key.PageUp:
                    i += GetTripleValue(t);
                    break;

                case System.Windows.Input.Key.PageDown:
                    i -= GetTripleValue(t);
                    break;

                default:
                    return;
            }

            if (BindingOperations.IsDataBound(t, TextBox.TextProperty))
            {
                try
                {
                    Binding binding = BindingOperations.GetBinding(t, TextBox.TextProperty);
                    t.Text = (string)binding.Converter.Convert(i, null, binding.ConverterParameter, binding.ConverterCulture);
                }
                catch
                {
                    t.Text = i.ToString();
                }
            }
            else
                t.Text = i.ToString();
        }
    }
}

Ich habe beschlossen, die hier als Antwort markierte Antwort mithilfe eines LINQ-Ausdrucks auf im Grunde zwei Zeilen zu vereinfachen.

e.Handled = !e.Text.All(Char.IsNumber);
base.OnPreviewTextInput(e);

Ich verwende eine benutzerdefinierte ValidationRule um zu prüfen, ob der Text numerisch ist.

public class DoubleValidation : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        if (value is string)
        {
            double number;
            if (!Double.TryParse((value as string), out number))
                return new ValidationResult(false, "Please enter a valid number");
        }

        return ValidationResult.ValidResult;
    }

Wenn ich dann einen binde TextBox Zu einer numerischen Eigenschaft füge ich die neue benutzerdefinierte Klasse hinzu Binding.ValidationRules Sammlung.Im folgenden Beispiel wird die Validierungsregel jedes Mal überprüft TextBox.Text Änderungen.

<TextBox>
    <TextBox.Text>
        <Binding Path="MyNumericProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:DoubleValidation/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Warum versuchen Sie es nicht einfach mit dem KeyDown-Ereignis anstelle des PreviewKeyDown-Ereignisses?Sie können dort die ungültigen Zeichen stoppen, aber alle Steuerzeichen werden akzeptiert.Das scheint bei mir zu funktionieren:

private void NumericKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9);
    bool isNumeric =((e.Key >= Key.D0 && e.Key <= Key.D9) && (e.KeyboardDevice.Modifiers == ModifierKeys.None));
    bool isDecimal = ((e.Key == Key.OemPeriod || e.Key == Key.Decimal) && (((TextBox)sender).Text.IndexOf('.') < 0));
    e.Handled = !(isNumPadNumeric || isNumeric || isDecimal);
}
public class NumericTextBox : TextBox
{
    public NumericTextBox()
        : base()
    {
        DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(CheckPasteFormat));
    }

    private Boolean CheckFormat(string text)
    {
        short val;
        return Int16.TryParse(text, out val);
    }

    private void CheckPasteFormat(object sender, DataObjectPastingEventArgs e)
    {
        var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);

        if (isText)
        {
            var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
            if (CheckFormat(text))
            {
                return;
            }
        }

        e.CancelCommand();
    }

    protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
    {
        if (!CheckFormat(e.Text))
        {
            e.Handled = true;
        }
        else
        {
            base.OnPreviewTextInput(e);
        }
    }
}

Darüber hinaus können Sie das Parsing-Verhalten anpassen, indem Sie entsprechende Abhängigkeitseigenschaften bereitstellen.

Ich habe die Ideen einiger dieser Antworten kombiniert und eine NumericTextBox erstellt

  • Behandelt Dezimalzahlen
  • Wird eine grundlegende Validierung durchgeführt, um ein eingegebenes '-' oder 'zu gewährleisten.' ist gültig
  • Behandelt eingefügte Werte

Bitte zögern Sie nicht, zu aktualisieren, wenn Ihnen eine andere Logik einfällt, die einbezogen werden sollte.

public class NumericTextBox : TextBox
{
    public NumericTextBox()
    {
        DataObject.AddPastingHandler(this, OnPaste);
    }

    private void OnPaste(object sender, DataObjectPastingEventArgs dataObjectPastingEventArgs)
    {
        var isText = dataObjectPastingEventArgs.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);

        if (isText)
        {
            var text = dataObjectPastingEventArgs.SourceDataObject.GetData(DataFormats.Text) as string;
            if (IsTextValid(text))
            {
                return;
            }
        }

        dataObjectPastingEventArgs.CancelCommand();
    }

    private bool IsTextValid(string enteredText)
    {
        if (!enteredText.All(c => Char.IsNumber(c) || c == '.' || c == '-'))
        {
            return false;
        }

        //We only validation against unselected text since the selected text will be replaced by the entered text
        var unselectedText = this.Text.Remove(SelectionStart, SelectionLength);

        if (enteredText == "." && unselectedText.Contains("."))
        {
            return false;
        }

        if (enteredText == "-" && unselectedText.Length > 0)
        {
            return false;
        }

        return true;
    }

    protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
    {
        e.Handled = !IsTextValid(e.Text);
        base.OnPreviewTextInput(e);
    }
}

Sie können auch versuchen, die Datenvalidierung zu verwenden, wenn Benutzer Daten festschreiben, bevor Sie sie verwenden.Das fand ich ziemlich einfach und sauberer, als mit den Schlüsseln herumzuspielen.

Andernfalls können Sie Einfügen jederzeit auch deaktivieren!

Meine Version von Arcturus Antwort: Sie können die verwendete Konvertierungsmethode ändern, um mit int/uint/decimal/byte (für Farben) oder jedem anderen numerischen Format zu arbeiten, das Sie verwenden möchten. Funktioniert auch mit Kopieren/Einfügen

protected override void OnPreviewTextInput( System.Windows.Input.TextCompositionEventArgs e )
{
    try
    {
        if ( String.IsNullOrEmpty( SelectedText ) )
        {
            Convert.ToDecimal( this.Text.Insert( this.CaretIndex, e.Text ) );
        }
        else
        {
            Convert.ToDecimal( this.Text.Remove( this.SelectionStart, this.SelectionLength ).Insert( this.SelectionStart, e.Text ) );
        }
    }
    catch
    {
        // mark as handled if cannot convert string to decimal
        e.Handled = true;
    }

    base.OnPreviewTextInput( e );
}

Hinweis:Ungetesteter Code.

Fügen Sie dies zur Hauptlösung hinzu, um sicherzustellen, dass die Bindung auf Null aktualisiert wird, wenn das Textfeld gelöscht wird.

protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e)
{
    base.OnPreviewKeyUp(e);

    if (BindingOperations.IsDataBound(this, TextBox.TextProperty))
    {
        if (this.Text.Length == 0)
        {
            this.SetValue(TextBox.TextProperty, "0");
            this.SelectAll();
        }
    }
}

Nennen Sie mich verrückt, aber warum platzieren Sie nicht Plus- und Minus-Schaltflächen auf beiden Seiten des TextBox-Steuerelements und verhindern einfach, dass das TextBox-Steuerelement den Cursor-Fokus erhält, und erstellen so Ihr eigenes billiges NumericUpDown-Steuerelement?

private void txtNumericValue_PreviewKeyDown(object sender, KeyEventArgs e)
{
    KeyConverter converter = new KeyConverter();

    string key = converter.ConvertToString(e.Key);

    if (key != null && key.Length == 1)
    {
        e.Handled = Char.IsDigit(key[0]) == false;
    }
}

Dies ist die einfachste Technik, die ich gefunden habe, um dies zu erreichen.Der Nachteil ist, dass das Kontextmenü der TextBox über Einfügen immer noch nicht-numerische Eingaben zulässt.Um das Problem schnell zu lösen, habe ich einfach das Attribut/die Eigenschaft hinzugefügt:ContextMenu="{x:Null}" zur TextBox hinzufügen und sie dadurch deaktivieren.Nicht ideal, aber für mein Szenario wird es ausreichen.

Natürlich könnten Sie dem Test noch ein paar weitere Schlüssel/Zeichen hinzufügen, um zusätzliche akzeptable Werte einzubeziehen (z. B.'.', '$' usw.)

Private Sub Value1TextBox_PreviewTextInput(ByVal sender As Object, ByVal e As TextCompositionEventArgs) Handles Value1TextBox.PreviewTextInput
    Try
        If Not IsNumeric(e.Text) Then
            e.Handled = True
        End If
    Catch ex As Exception
    End Try
End Sub

Hat bei mir funktioniert.

Können Sie nicht einfach so etwas wie das Folgende verwenden?

int numericValue = 0;

if (false == int.TryParse(yourInput, out numericValue))
{
    // handle non-numeric input
}
void PreviewTextInputHandler(object sender, TextCompositionEventArgs e)
{
    string sVal = e.Text;
    int val = 0;

    if (sVal != null && sVal.Length > 0)
    {
        if (int.TryParse(sVal, out val))
        {
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }
}

Kann auch einen Konverter verwenden wie:

public class IntegerFormatConverter : IValueConverter
{
    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int result;
        int.TryParse(value.ToString(), out result);
        return result;
    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int result;
        int.TryParse(value.ToString(), out result);
        return result;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top