C# WPF 中的数字数据输入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5511/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-07-31 16:24:26  来源:igfitidea点击:

Numeric Data Entry in WPF

提问by Matt Hamilton

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

您如何处理 WPF 应用程序中数值的输入?

Without a NumericUpDown control, I've been using a TextBox and handling its PreviewKeyDown event with the code below, but it's pretty ugly.

没有 NumericUpDown 控件,我一直在使用 TextBox 并使用下面的代码处理它的 PreviewKeyDown 事件,但它非常难看。

Has anyone found a more graceful way to get numeric data from the user without relying on a third-party control?

有没有人找到一种更优雅的方式来从用户那里获取数字数据而不依赖第三方控件?

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

采纳答案by Arcturus

How about:

怎么样:

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

回答by tags2k

Call me crazy, but why not put plus and minus buttons at either side of the TextBox control and simply prevent the TextBox from receiving cursor focus, thereby creating your own cheap NumericUpDown control?

叫我疯了,但为什么不在 TextBox 控件的任一侧放置加号和减号按钮并简单地阻止 TextBox 接收光标焦点,从而创建您自己的廉价 NumericUpDown 控件?

回答by Nidonocu

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!

否则,您也可以随时禁用粘贴!

回答by user3035

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:

代码并不完美,但它处理了我需要它处理的情况:

  • Uparrow, Downarrow
  • Shift + Uparrow, Shift + Downarrow
  • Page Up, Page Down
  • Binding Converteron the text property
  • Up箭头,Down箭头
  • Shift + Up箭头,Shift + Down箭头
  • Page Up, Page Down
  • 绑定Converter文本属性

Code behind

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

回答by Mark Allen

Can you not just use something like the following?

您不能只使用以下内容吗?

int numericValue = 0;

if (false == int.TryParse(yourInput, out numericValue))
{
    // handle non-numeric input
}

回答by Mark Allen

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.

这是我发现的最简单的技术来实现这一点。不利的一面是 TextBox 的上下文菜单仍然允许通过粘贴非数字。为了快速解决这个问题,我简单地添加了属性/属性: ContextMenu="{x:Null}" 到 TextBox 从而禁用它。不理想,但对于我的场景来说就足够了。

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

显然,您可以在测试中添加更多的键/字符以包含其他可接受的值(例如 '.'、'$' 等...)

回答by Eric

This is how I do it. It uses a regular expression to check if the text that will be in the box is numeric or not.

我就是这样做的。它使用正则表达式来检查框中的文本是否为数字。

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

There is now a much better way to do this in WPF and Silverlight. If your control is bound to a property, all you have to do is change your binding statement a bit. Use the following for your binding:

现在在 WPF 和 Silverlight 中有更好的方法来做到这一点。如果您的控件绑定到一个属性,您所要做的就是稍微更改您的绑定语句。将以下内容用于您的绑定:

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

Note that you can use this on custom properties too, all you have to do is throw an exception if the value in the box is invalid and the control will get highlighted with a red border. If you click on the upper right of the red border then the exception message will pop up.

请注意,您也可以在自定义属性上使用它,您所要做的就是在框中的值无效时抛出异常并且控件将用红色边框突出显示。如果单击红色边框的右上角,则会弹出异常消息。

回答by Ritz

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

Worked for me.

为我工作。

回答by bcd

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

回答by juanagui

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

Additionally you may customize the parsing behavior by providing appropriate dependency properties.

此外,您可以通过提供适当的依赖属性来自定义解析行为。