用于输入十进制值的 WPF 文本框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16914224/
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-09-08 23:49:25  来源:igfitidea点击:

WPF TextBox to enter decimal values

wpfdecimaldata-entry

提问by Sam

Is there any decent way to get a WPF control which is bound to a decimalvalue?

有没有什么体面的方法来获得绑定到一个decimal值的 WPF 控件?

When I just bind the TextBoxor DataGridTextColumnto a decimal, data entry is a problem.

当我只是将TextBox或绑定DataGridTextColumn到 a 时decimal,数据输入是一个问题。

<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=PropertyChanged, 
    ValidatesOnDataErrors=True}"/>

When I try to enter "0,5" in this TextBoxI'll get "5" as a result. It is nearly impossible to enter "0,5" at all (apart from entering 1,5 and replacing the "1" with a "0").

当我尝试在其中输入“0,5”时,TextBox结果会得到“5”。几乎不可能输入“0,5”(除了输入 1,5 并将“1”替换为“0”)。

When I use StringFormat, data entry is only slightly improved:

当我使用 时StringFormat,数据输入仅略有改进:

<TextBox Text="{Binding MyDecimal, StringFormat=F1, UpdateSourceTrigger=PropertyChanged,
    ValidatesOnDataErrors=True}"/>

Now, when I try to enter "0,5" I'll end up with "0,5,0", which still is wrong but at least I can remove the trailing ",0" without much difficulty.

现在,当我尝试输入“0,5”时,我最终会得到“0,5,0”,这仍然是错误的,但至少我可以毫不费力地删除尾随的“,0”。

Still, entering decimaltypes using WPF is very awkward, because these TextBoxes are veryprone to data entry errors, which is a real pain especially for values!

尽管如此,decimal使用 WPF输入类型非常笨拙,因为这些TextBoxes非常容易出现数据输入错误,这对于值来说尤其痛苦!

So what am I supposed to use for decimal data entry in WPF? Or does Microsoft not support decimal data??

那么我应该使用什么来输入 WPF 中的十进制数据?还是微软不支持十进制数据??

回答by blindmeis

I currently use this behavior for digital and decimal input:

我目前将此行为用于数字和十进制输入:

public class TextBoxInputBehavior : Behavior<TextBox>
{
    const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |
                                               NumberStyles.AllowThousands |
                                               NumberStyles.AllowLeadingSign;
    public TextBoxInputBehavior()
    {
        this.InputMode = TextBoxInputMode.None;
        this.JustPositivDecimalInput = false;
    }

    public TextBoxInputMode InputMode { get; set; }


    public static readonly DependencyProperty JustPositivDecimalInputProperty =
     DependencyProperty.Register("JustPositivDecimalInput", typeof(bool),
     typeof(TextBoxInputBehavior), new FrameworkPropertyMetadata(false));

    public bool JustPositivDecimalInput
    {
        get { return (bool)GetValue(JustPositivDecimalInputProperty); }
        set { SetValue(JustPositivDecimalInputProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;

        DataObject.AddPastingHandler(AssociatedObject, Pasting);

    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;

        DataObject.RemovePastingHandler(AssociatedObject, Pasting);
    }

    private void Pasting(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(typeof(string)))
        {
            var pastedText = (string)e.DataObject.GetData(typeof(string));

            if (!this.IsValidInput(this.GetText(pastedText)))
            {
                System.Media.SystemSounds.Beep.Play();
                e.CancelCommand();
            }
        }
        else
        {
            System.Media.SystemSounds.Beep.Play();
            e.CancelCommand();
        }
     }

     private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
     {
        if (e.Key == Key.Space)
        {
            if (!this.IsValidInput(this.GetText(" ")))
            {
                System.Media.SystemSounds.Beep.Play();
                e.Handled = true;
            }
        }
     }

     private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
     {
        if (!this.IsValidInput(this.GetText(e.Text)))
        {
            System.Media.SystemSounds.Beep.Play();
            e.Handled = true;
        }
     }

     private string GetText(string input)
     {
        var txt = this.AssociatedObject;

        int selectionStart = txt.SelectionStart;
        if (txt.Text.Length < selectionStart) 
            selectionStart = txt.Text.Length;

        int selectionLength = txt.SelectionLength;
        if (txt.Text.Length < selectionStart + selectionLength) 
            selectionLength = txt.Text.Length - selectionStart;

        var realtext = txt.Text.Remove(selectionStart, selectionLength);

        int caretIndex = txt.CaretIndex;
        if (realtext.Length < caretIndex) 
            caretIndex = realtext.Length;

        var newtext = realtext.Insert(caretIndex, input);

        return newtext;
     }

     private bool IsValidInput(string input)
     {
        switch (InputMode)
        {
            case TextBoxInputMode.None:
                return true;
            case TextBoxInputMode.DigitInput:
                return CheckIsDigit(input);

            case TextBoxInputMode.DecimalInput:
                decimal d;
                //wen mehr als ein Komma
                if (input.ToCharArray().Where(x => x == ',').Count() > 1)
                    return false;


                if (input.Contains("-"))
                {
                     if (this.JustPositivDecimalInput) 
                        return false;


                     if (input.IndexOf("-",StringComparison.Ordinal) > 0) 
                          return false;

                      if(input.ToCharArray().Count(x=>x=='-') > 1)
                          return false;

                        //minus einmal am anfang zul?ssig
                       if (input.Length == 1) 
                           return true;
                    }

                    var result = decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);
                    return result;



            default: throw new ArgumentException("Unknown TextBoxInputMode");

        }
        return true;
     }

     private bool CheckIsDigit(string wert)
     {
        return wert.ToCharArray().All(Char.IsDigit);
     }
}

 public enum TextBoxInputMode
 {
  None,
  DecimalInput,
  DigitInput
  }

The XAML usage looks like this:

XAML 用法如下所示:

<TextBox Text="{Binding Sum}">
    <i:Interaction.Behaviors>
        <Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>
    </i:Interaction.Behaviors>
</TextBox>

回答by CMarsden

    private void DecimalTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        bool approvedDecimalPoint = false;

        if (e.Text == ".")
        {
            if (!((TextBox)sender).Text.Contains("."))
                approvedDecimalPoint = true;
        }

        if (!(char.IsDigit(e.Text, e.Text.Length - 1) || approvedDecimalPoint))
            e.Handled = true;
    }

回答by slugster

The WPF Extended toolkithas a DecimalUpDowncontrol that may suit your needs. It's free to use, and it's better to use this than to try and roll your own.

WPF扩展工具包具有DecimalUpDown可满足您的需求控制。它是免费使用的,最好使用它而不是尝试自己动手。

As for validating the input on it, there are a number of ways of applying validation, here is onedetailed in MSDN. I detail another approachfor custom bindable validation in two posts on my blog (you would apply the validation to the Valueproperty binding on the DecimalUpDown control).

至于验证上面的输入,有多种应用验证的方法,这里是MSDN 中详细介绍的一种。我在我的博客上的两篇文章中详细介绍了自定义可绑定验证的另一种方法(您可以将验证应用于ValueDecimalUpDown 控件上的属性绑定)。

回答by testpattern

I also came across this issue; with UpdateSourceTrigger=PropertyChangedit seems that the binding tries to update the text as you are typing it. To fix this issue we changed our input fields so that UpdateSourceTrigger=LostFocus, e.g.:

我也遇到过这个问题;与UpdateSourceTrigger=PropertyChanged似乎结合尝试为您键入它更新文本。为了解决这个问题,我们改变了我们的输入字段UpdateSourceTrigger=LostFocus,例如:

<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, StringFormat=n1}" />

You can define your own validation errors by using the IDataErrorInfointerface. You just need to add the following to your backing model:

您可以使用该IDataErrorInfo接口定义自己的验证错误。您只需要将以下内容添加到您的支持模型中:

 public class MyModel : IDataErrorInfo
 {
    /* my properties */

    public string Error { get { return null; } }
    public string this[string name]
    {
       get
       {
          switch (name)
          {
             case "MyDecimal":
                return NumberHelper.IsValidValue(MyDecimal) ? message : null;
             default: return null;
          }
       }
    }
    private string message = "Invalid value";
 }

回答by pinki

I implemented my own TextBox. It updates the source, when there is a number in the text, otherwise not. On lost Focus, I read the source property. All you have to do is replace the TextBox with this class and bind the "Number" Property which is of type double.

我实现了我自己的 TextBox。当文本中有数字时,它会更新源,否则不会。在失去焦点时,我阅读了源属性。您所要做的就是用此类替换 TextBox 并绑定类型为 double 的“Number”属性。

public class DoubleTextBox: TextBox
{
    public DoubleTextBox()
    {
        TextChanged += DoubleTextBox_TextChanged;
        LostFocus += DoubleTextBox_LostFocus;
    }

    void DoubleTextBox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        Text = Number.ToString("N2");
    }

    void DoubleTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        double zahl;
        if (string.IsNullOrWhiteSpace(Text))
        {
            Number = 0;
        }
        else if (double.TryParse(Text, out zahl))
        {
            Number = Double.Parse(zahl.ToString("N2"));
        }
        else
        {
            ValidationError validationError =
                new ValidationError(new ExceptionValidationRule(), GetBindingExpression(NumberProperty));

            validationError.ErrorContent = "Keine gültige Zahl";

            Validation.MarkInvalid(
                GetBindingExpression(NumberProperty),
                validationError);

        }
    }

    public double Number
    {
        get { return (double)this.GetValue(NumberProperty); }
        set { this.SetValue(NumberProperty, value); }
    }

    public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
        "Number", typeof(double), typeof(DoubleTextBox), 
        new FrameworkPropertyMetadata
            (
                0d,
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
            )
    );
}

回答by Hari prasad kasavajjala

if you want the textbox to only allow decimal then write previewinputtext event for that textbox. then in that event write this code

如果您希望文本框只允许十进制,则为该文本框编写 previewinputtext 事件。然后在该事件中编写此代码

decimal result;
e.Handled=!decimal.TryParse((sender as TextBox).Text + e.Text, out result)

回答by cheebacat

Im new, so I cant comment his answer, but I fixed the negative number issues in blindmeis's code.

我是新来的,所以我不能评论他的回答,但我修复了blindmeis代码中的负数问题。

Just modify the

只需修改

if (input.Contains("-"))

section of IsValidInput() to...

IsValidInput()来...

                if (input.Contains("-"))
                {
                    if (this.JustPositivDecimalInput)
                        return false;

                    //minus einmal am anfang zul?ssig
                    //minus once at the beginning
                    if (input.IndexOf("-", StringComparison.Ordinal) == 0 && input.ToCharArray().Count(x => x == '-') == 1)
                    {
                        if(input.Length == 1)
                        {
                            //INPUT IS "-"
                            return true;
                        }
                        else if (input.Length == 2)
                        {
                            //VALIDATE NEGATIVE DECIMALS...INPUT IS "-."
                            if (input.IndexOf(".", StringComparison.Ordinal) == 1)
                            {
                                return true;
                            }
                        }
                        else 
                        {
                            return decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);
                        }
                    }
                }

回答by ian korkie

This will allow only decimals to be entered into the textbox and nothing else.

这将只允许在文本框中输入小数,而不允许输入其他任何内容。

The viewmodel looks like this:

视图模型如下所示:

    private string _decimalVal = "0";
    public string decimalVal
    {
        get { return _decimalVal.ToString(); }
        set
        {
            if (string.IsNullOrEmpty(value) || value == "-")
                SetProperty(ref _decimalVal, value);
            else if (Decimal.TryParse(value, out decimal newVal))
            {
                if (newVal == 0)
                    value = "0";

                SetProperty(ref _decimalVal, value = (value.Contains(".")) ? Convert.ToDecimal(value).ToString("0.00") : value);
            }
        }
    }

The XAML usage looks like this:

XAML 用法如下所示:

<TextBox Text="{Binding decimalVal,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

回答by JC Frigon

I know that this post is old but it comes in first on Google Search for this problem. As I had error with system.windows.interactivity package (old version of this package) I continued my search.

我知道这篇文章很旧,但它首先出现在 Google 搜索中以解决此问题。由于 system.windows.interactivity 包(此包的旧版本)出错,我继续搜索。

This post on MSDNfixed my problem and it's a one line solutionjust before initializecomponent on the main window like this:

MSDN上的这篇文章解决了我的问题,它是一个单行解决方案,就在主窗口上的 initializecomponent 之前,如下所示:

    Public Sub New()

    ' This call is required by the designer.
    FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = False
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.

End Sub

Hope this will help other google searchers.

希望这会帮助其他谷歌搜索者。

回答by Kavinda Gehan

This regex works

这个正则表达式有效

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
  {
   Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
   e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart,e.Text));
  }