66 votes

Saisie de données numériques dans WPF

Comment gérez-vous la saisie de valeurs numériques dans les applications WPF ?

Sans contrôle NumericUpDown, j'ai utilisé une TextBox et géré son événement PreviewKeyDown avec le code ci-dessous, mais c'est assez laid.

Quelqu'un a-t-il trouvé un moyen plus gracieux d'obtenir des données numériques de l'utilisateur sans s'appuyer sur un contrôle tiers ?

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

58voto

Arcturus Points 14366

Pourquoi pas :

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

14voto

Eric Points 587

Voici comment je procède. Il utilise une expression régulière pour vérifier si le texte qui sera dans la boîte est numérique ou non.

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

Il existe désormais une bien meilleure façon de le faire dans WPF et Silverlight. Si votre contrôle est lié à une propriété, il vous suffit de modifier légèrement votre déclaration de liaison. Utilisez le texte suivant pour votre liaison :

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

Notez que vous pouvez également utiliser cette méthode pour les propriétés personnalisées. Il vous suffit de lever une exception si la valeur contenue dans la case n'est pas valide et le contrôle sera mis en évidence par une bordure rouge. Si vous cliquez sur la partie supérieure droite de la bordure rouge, le message d'exception s'affichera.

11voto

user3035 Points 91

J'ai utilisé une propriété jointe pour permettre à l'utilisateur d'utiliser les touches haut et bas pour modifier les valeurs dans la zone de texte. Pour l'utiliser, il suffit d'utiliser

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

Cela ne répond pas aux problèmes de validation évoqués dans cette question, mais cela répond à mon problème de ne pas avoir de commande numérique haut/bas. En l'utilisant un peu, je pense que je l'aime mieux que l'ancienne commande numérique haut/bas.

Le code n'est pas parfait, mais il gère les cas dont j'avais besoin :

  • Up flèche, Down flèche
  • Shift + Up flèche, Shift + Down flèche
  • Page Up , Page Down
  • Reliure Converter sur la propriété du texte

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;
            if (t == null)
                return;

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

11voto

Erode Points 1032

J'ai décidé de simplifier la réponse indiquée ici comme étant la réponse à 2 lignes en utilisant une expression LINQ.

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

1 votes

e.Text.All(cc => Char.IsNumber(cc) || cc == '.' || cc == '-') ?

3voto

juanagui Points 449
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);
        }
    }
}

De plus, vous pouvez personnaliser le comportement d'analyse en fournissant les propriétés de dépendance appropriées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X