Numerische Dateneingabe in WPF

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

    Ohne ein NumericUpDown-Steuerelement verwende ich eine TextBox und verwalte deren PreviewKeyDown-Ereignis mit dem folgenden Code, der jedoch ziemlich hässlich ist.

    Hat jemand einen eleganteren Weg gefunden, numerische Daten vom Benutzer abzurufen, ohne sich auf die Kontrolle eines Drittanbieters zu verlassen?

     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;
    }
     
    08 August 2008
    Matt Hamiltonbitbonk
16 answers
  • 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;
    }
     
    07 February 2016
    Markus SafarMHop
  • So mache ich es. Mit einem regulären Ausdruck wird überprüft, 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");
    }
     

    Dies ist jetzt in WPF und Silverlight viel besser. Wenn Ihr Steuerelement an eine Eigenschaft gebunden ist, müssen Sie lediglich Ihre Bindungsaussage ein wenig ändern. Verwenden Sie für Ihre Bindung Folgendes:

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

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

    07 February 2016
    Markus SafarMHop
  • Ich beschloss, die Antwort, die hier als Antwort markiert ist, mit einem LINQ-Ausdruck auf im Wesentlichen 2 Zeilen zu vereinfachen.

     e.Handled = !e.Text.All(Char.IsNumber);
    base.OnPreviewTextInput(e);
     
    07 February 2016
    Markus SafarMHop
  • Ich habe eine angefügte Eigenschaft verwendet, die es dem Benutzer ermöglicht, die Auf- und Ab-Tasten zum Ändern der Werte im Textfeld zu verwenden. Um es zu verwenden, verwenden Sie einfach

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

    Dies behebt nicht wirklich die Validierungsprobleme, auf die in verwiesen wird diese Frage, aber es geht um das, was ich mache, weil ich keine numerische Auf / Ab-Steuerung habe. Ich denke, dass ich es vielleicht ein bisschen lieber mag als die alte numerische Auf / Ab-Steuerung.

    Der Code ist nicht perfekt, aber er behandelt die Fälle, die ich ausführt brauchte es zu handhaben:

    • Up Pfeil, Down Pfeil
    • Shift + Up Pfeil, Shift + Down arrow
    • Page Up, Page Down
    • Bindung Converter an die 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();
            }
        }
    }
     
    07 February 2016
    Markus SafarMHop
  • Ich verwende ein benutzerdefiniertes ValidationRule, um zu überprüfen, ob 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;
        }
     

    Dann Wenn ich ein TextBox an eine numerische Eigenschaft binde, füge ich die neue benutzerdefinierte Klasse der Binding.ValidationRules -Sammlung hinzu. Im folgenden Beispiel wird die Validierungsregel bei jeder Änderung von TextBox.Text überprüft.

     <TextBox>
        <TextBox.Text>
            <Binding Path="MyNumericProperty" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <local:DoubleValidation/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
     
    07 February 2016
    Markus SafarMHop
  • Durch die Kombination der Ideen aus einigen dieser Antworten habe ich eine NumericTextBox erstellt, die

    • Dezimalzahlen behandelt
    • Führt einige grundlegende Überprüfungen durch, um sicherzustellen, dass eingegebene '-' oder '.' ist gültig
    • Behandelt eingefügte Werte

    Aktualisieren Sie sich, wenn Sie sich eine andere Logik vorstellen können eingeschlossen sein.

     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);
        }
    }
     
    07 February 2016
    Markus SafarMHop
  • Warum verwenden Sie nicht das PreviewKeyDown-Ereignis, sondern das KeyDown-Ereignis. Sie können die ungültigen Zeichen dort anhalten, aber alle Steuerzeichen werden akzeptiert. Das scheint für mich 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);
    }
     
    05 April 2011
    Jroonk
  •  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 Analyseverhalten anpassen, indem Sie die entsprechenden Abhängigkeitseigenschaften angeben.

    07 February 2016
    Markus SafarMHop
  • Sie können auch versuchen, die Datenüberprüfung zu verwenden, wenn Benutzer Daten vor der Verwendung festschreiben. Das, was ich fand, war ziemlich einfach und sauberer, als mit Schlüsseln herumzufummeln.

    Andernfalls könnten Sie das Einfügen auch immer deaktivieren!

    12 August 2008
    Nidonocu
  • Fügen Sie dies der Hauptlösung hinzu, um sicherzustellen, dass die Bindung auf null gesetzt 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();
            }
        }
    }
     
    08 June 2012
    akjoshiNadella Ravindra