WPF TextBox输入十进制值

Sam*_*Sam 16 wpf decimal data-entry

是否有任何体面的方法来获得绑定到十进制值的WPF控件?

当我将TextBox或DataGridTextColumn绑定到小数时,数据条目会耗费大量时间.

<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=PropertyChanged, 
    ValidatesOnDataErrors=True}"/>
Run Code Online (Sandbox Code Playgroud)

当我尝试在此TextBox中输入"0,5"时,我将得到"5".根本不可能输入"0,5"(除了输入1,5并用"0"替换"1").

当我使用StringFormat时,数据输入仍然很少:

<TextBox Text="{Binding MyDecimal, StringFormat=F1, UpdateSourceTrigger=PropertyChanged,
    ValidatesOnDataErrors=True}"/>
Run Code Online (Sandbox Code Playgroud)

现在,当我尝试输入"0,5"时,我最终会得到"0,5,0",这仍然是错误的,但至少我可以删除尾随的"0"而没有太多问题.

尽管如此,使用WPF输入小数会吸引大量时间,因为此输入字段非常容易出现数据输入错误,这对于值来说尤其令人痛苦!

那么我应该用什么来为wpf中的十进制数据输入?或者微软不支持十进制数据?

bli*_*eis 20

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

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
  }
Run Code Online (Sandbox Code Playgroud)

XAML用法如下所示:

<TextBox Text="{Binding Sum}">
    <i:Interaction.Behaviors>
        <Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>
    </i:Interaction.Behaviors>
</TextBox>
Run Code Online (Sandbox Code Playgroud)

  • 为什么downvote没有评论.这毫无意义 (4认同)
  • 要获得`Behavior <TextBox>`,请添加一个引用> extensions> System.Windows.Interactivity (2认同)
  • 作为 .net 5 或更高版本中的注释,它现在位于名为 Microsoft.Xaml.Behaviors.Wpf 的 nuget 包中。 (2认同)

CMa*_*den 8

    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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 解释为什么这段代码会解决OP的问题而不是那个接受的答案...... (2认同)

slu*_*ter 6

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

至于验证输入,有许多方法可以应用验证,这里有一个详细的MSDN.我在博客上的两个帖子中详述了另一种自定义绑定验证方法(您可以将验证应用于ValueDecimalUpDown控件上的属性绑定).


tes*_*ern 6

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

<TextBox Text="{Binding MyDecimal, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, StringFormat=n1}" />
Run Code Online (Sandbox Code Playgroud)

您可以使用该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";
 }
Run Code Online (Sandbox Code Playgroud)

  • @Sam,啊,在那种情况下,这可能无济于事。不过我会保留它,因为在您不需要 PropertyChanged 的​​情况下,这是一个简单的解决方案,IMO。 (2认同)

00j*_*0jt 6

从 .NET 4.5 开始,有一个更简单的修复,在绑定中添加“延迟”

 <TextBox  Text="{Binding MyDouble, UpdateSourceTrigger=PropertyChanged, Delay=1000}" />
Run Code Online (Sandbox Code Playgroud)

现在,用户在绑定系统尝试替换句点(将“1.”更改为“1”)之前有 1 秒(1000 毫秒)的时间。这应该让他们有时间在“.”之后输入其他字符。这样它就不会被删除。