PreviewTextInput 如果它是最后一个字符,则会删除小数点,但如果它位于中间,则不会删除小数点

问题描述 投票:0回答:1

我正在使用 PreviewTextInput 来验证十进制数。这是我的代码:

private void AmountTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
  TextBox t = sender as TextBox;
  string newText = t.Text.Insert(t.SelectionStart,e.Text);

  Regex regex = new Regex(@"^[0-9]{1,12}(?:\.[0-9]{0,2})?$");
  e.Handled = !regex.IsMatch(newText);
}

这正确地只接受数字和一位小数点,但只有用户在数字之间插入小数点时才接受小数点。如果在字符串末尾输入小数点,则即使正则表达式返回匹配项(即 e.Handled = false),它也不会添加到文本框中。当然,在输入值时,用户会输入小数点左侧,然后输入小数点,然后输入小数点右侧。为什么小数点会被去掉?它是 PreviewTextInput 处理的产物吗?我只是错过了一些明显的东西吗?

c# wpf textbox
1个回答
0
投票

请注意,您应该覆盖

TextInput
PreviewTextInput
,而不是注册事件处理程序。一般来说,您应该始终覆盖事件处理程序,而不是注册新的处理程序。

WPF 绑定引擎执行隐式类型转换。当您将

"1."
转换为
double
时,解析器会删除小数点分隔符。如果将
TextBox.Text
属性绑定到
string
属性,则由于避免了数字转换,该问题就会消失。

正则表达式对于这个简单的任务来说太昂贵了(检查文本是否是数字)。您应该使用例如

double.TryParse

您不应该对小数点分隔符进行硬编码。相反,使用

CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
值可以让您的控件适应不同的文化(考虑到世界上 50% 的国家使用“,”作为小数点分隔符)。

另请注意,将文本粘贴到

TextBox
中不会引发
TextInput
事件,因此会绕过您的输入验证或填充。为了解决这种情况,您应该通过调用
DataObject.AddPastingHandler
方法来注册数据粘贴事件处理程序:
DataObject.AddPastingHandler(OnInputPasted)

解决转换问题的一种解决方案是检测 trailing 小数点分隔符,然后附加

0
以使其成为有效的十进制数。然后,您可以使用下一个用户输入自动覆盖生成的
0

private bool isContentAutoCompleted;

protected override void OnTextInput(TextCompositionEventArgs e)
{
  string numberDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

  bool isInputDecimalSeparator = e.Text.Equals(numberDecimalSeparator, StringComparison.OrdinalIgnoreCase);

  // Input is a decimal separator: append a "0" (if this is the only separator)
  if (isInputDecimalSeparator)
  {
    e.Handled = true;

    int existinDecimalSeparatorIndex = this.Text.LastIndexOf(numberDecimalSeparator, StringComparison.OrdinalIgnoreCase);
    bool isDecimalSeparatorPresent = existinDecimalSeparatorIndex != -1;
    if (isDecimalSeparatorPresent)
    {
      // The next character is already a decimal separator. Just advance the caret.
      // Otherwise ignore the input.
      if (existinDecimalSeparatorIndex == this.CaretIndex)
      {
        ++this.CaretIndex;
      }

      return;
    }

    // Only auto complete input if decimal separator is trailing the cotent
    this.isContentAutoCompleted = this.CaretIndex == this.Text.Length;

    string newContent = this.isContentAutoCompleted
      ? this.Text + e.Text + "0"
      : this.Text.Insert(this.CaretIndex, e.Text);

    int currentCaretIndex = this.CaretIndex;
    SetCurrentValue(TextBox.TextProperty, newContent);
    this.CaretIndex = currentCaretIndex + 1;

    return;
  }

  bool isInputNumeric = double.TryParse(e.Text, out _);
  if (!isInputNumeric)
  {
    e.Handled = true;

    return;
  }

  // Overwrite the auto appended "0" with the new input
  if (this.isContentAutoCompleted)
  {
    this.isContentAutoCompleted = false;
    e.Handled = true;

    string newContent = this.Text[..^1] + e.Text;

    // Setting the Text property will reset the caret position.
    // Therefore we need to restore it.
    int currentCaretIndex = this.CaretIndex;

    SetCurrentValue(TextBox.TextProperty, newContent);
    this.CaretIndex = currentCaretIndex + 1;

    return;
  }

  base.OnTextInput(e);
}
© www.soinside.com 2019 - 2024. All rights reserved.