Numeric Data Entry in WPF

前端 未结 17 2511
情话喂你
情话喂你 2020-12-02 05:38

How are you handling the entry of numeric values in WPF applications?

Without a NumericUpDown control, I\'ve been using a TextBox and handling its PreviewKeyDown eve

相关标签:
17条回答
  • 2020-12-02 06:08

    I've been using an attached property to allow the user to use the up and down keys to change the values in the text box. To use it, you just use

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

    This doesn't actually address the validation issues that are referred to in this question, but it addresses what I do about not having a numeric up/down control. Using it for a little bit, I think I might actually like it better than the old numeric up/down control.

    The code isn't perfect, but it handles the cases I needed it to handle:

    • Up arrow, Down arrow
    • Shift + Up arrow, Shift + Down arrow
    • Page Up, Page Down
    • Binding Converter on the text property

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 06:08

    You can also try using data validation if users commit data before you use it. Doing that I found was fairly simple and cleaner than fiddling about with keys.

    Otherwise, you could always disable Paste too!

    0 讨论(0)
  • 2020-12-02 06:10
    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;
        }
    }
    

    This is the easiest technique I've found to accomplish this. The down side is that the context menu of the TextBox still allows non-numerics via Paste. To resolve this quickly I simply added the attribute/property: ContextMenu="{x:Null}" to the TextBox thereby disabling it. Not ideal but for my scenario it will suffice.

    Obviously you could add a few more keys/chars in the test to include additional acceptable values (e.g. '.', '$' etc...)

    0 讨论(0)
  • 2020-12-02 06:11

    Add this to the main solution to make sure the the binding is updated to zero when the textbox is cleared.

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 06:11

    Can also use a converter like:

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题