【问题标题】:WPF validation rule preventing decimal entry in textbox?WPF验证规则防止文本框中的小数输入?
【发布时间】:2014-01-08 19:36:56
【问题描述】:

我在 XAML 中定义了一个 WPF 文本框,如下所示:

<Window.Resources>        
    <Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<TextBox x:Name="upperLeftCornerLatitudeTextBox" Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <local:LatitudeValidationRule ValidationStep="RawProposedValue"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

如您所见,我的文本框绑定到名为 UpperLeftCornerLatitude 的业务对象上的小数属性,如下所示:

private decimal _upperLeftCornerLongitude;
public decimal UpperLeftCornerLatitude
{
    get { return _upperLeftCornerLongitude; }
    set
    {
        if (_upperLeftCornerLongitude == value)
        {
            return;
        }

        _upperLeftCornerLongitude = value;
        OnPropertyChanged(new PropertyChangedEventArgs("UpperLeftCornerLatitude"));
    }
}

我的用户将在此文本框中输入纬度值,为了验证该条目,我创建了如下所示的验证规则:

public class LatitudeValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        decimal latitude;

        if (decimal.TryParse(value.ToString(), out latitude))
        {
            if ((latitude < -90) || (latitude > 90))
            {
                return new ValidationResult(false, "Latitude values must be between -90.0 and 90.0.");
            }
        }
        else
        {
            return new ValidationResult(false, "Latitude values must be between -90.0 and 90.0.");
        }

        return new ValidationResult(true, null);
    }
}

我的文本框最初是空的,我在验证规则的开头设置了一个断点。我在文本框中输入 1,当我的调试器在验证规则内中断时,我可以看到 value = "1"。到目前为止,一切都很好。现在我继续运行并在文本框中输入一个小数点(所以我们现在应该有“1.”)。再次,调试器打破了验证规则,正如预期的那样,value = "1."。如果我单步执行验证规则代码,我会看到它通过了纬度值检查并返回以下内容:

new ValidationRule(true, null);

但是,一旦验证规则返回并进入下一行代码,我发现自己位于 UpperLeftCornerLatitude 属性设置器的第一行。将鼠标悬停在此处的 value 上表明它是“1”而不是“1”的值。正如我所料。所以很自然地,当我继续运行我的代码时,我最终会回到文本框中,盯着值“1”而不是“1.”。如果我删除所有断点,效果是我似乎无法在文本框中输入小数点。是否有一些明显的东西我在这里遗漏了,这导致我的设置器最终的值是“1”,即使我输入了“1”。在文本框中?非常感谢!

【问题讨论】:

  • 跟ValidationRule没关系,跟Converter有关。当您键入“1”时。它无法将其解析为小数,因此它回退到“1”

标签: c# wpf validation textbox


【解决方案1】:

这里有一些方法可以解决这个问题

A.为您的绑定指定 LostFocus(默认文本框)

<Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="LostFocus">
</Binding>

B.为绑定指定一个Delay,这将允许您在一段时间内输入小数

<Binding Path="UpperLeftCornerLatitude" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Delay="1000">
</Binding>

C.把decimal改成string自己解析

D.写一个ValueConverter覆盖默认的转换过程

class DecimalConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }
}

【讨论】:

  • 选项 B 似乎对我来说效果很好。不错!
  • 感谢您的出色回答。只是想指出,虽然选项 B 效果很好,但它有点冒险。想象一下,用户有点心烦意乱,他开始输入“1.”,同时他正在检查一些文件,然后又回到它,输入“23”。但与此同时,延迟过去了,小数点被删除了,所以应该是“1.23”现在是“123”。当涉及金钱时,这是完全不可取的。 :)
【解决方案2】:

.NET 4.5 更新

在 .NET 4.5 中,当绑定 UpdateSourceTrigger 设置为 PropertyChanged 时,Microsoft 决定对将数据输入到 TextBox 控件的方式进行重大更改。引入了一个新的 KeepTextBoxDisplaySynchronizedWithTextProperty 属性,应该重新创建以前的行为...将其设置为 false应该返回以前的行为:

FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;

不幸的是,虽然它允许我们再次输入数字分隔符,但它不像以前那样工作了。例如,分隔符仍然不会出现在 TextBox.Text 属性值中,直到它后面跟着另一个数字,如果您有自定义验证,这可能会导致问题。不过,总比打耳光好。

【讨论】:

    【解决方案3】:

    这真的不会很漂亮,因为 WPF 会在您键入时自动尝试将字符串值转换为小数;我认为这是由于默认的Behavior&lt;TextBox&gt;。我认为您快速解决此问题的最简单方法是将您的控件绑定到字符串属性并公开另一个decimal 属性:

    private string _upperLeftCornerLongitudeStr;
    public string UpperLeftCornerLatitudeStr
    {
        get { return _upperLeftCornerLongitudeStr; }
        set
        {
            if (_upperLeftCornerLongitudeStr == value)                
                return;                
    
            _upperLeftCornerLongitudeStr = value;
            OnPropertyChanged("UpperLeftCornerLatitudeStr");
        }
    }
    
    public decimal? UpperLeftCornerLatitude
    {
        get
        {
            decimal val;
            if (decimal.TryParse(_upperLeftCornerLongitudeStr, out val))
                return val;
    
            return null;
        }
        set { _upperLeftCornerLongitudeStr = value != null ? value.ToString() : null; }
    }
    

    话虽如此,您可能想研究一下不同的方法,以防止您从一开始就输入无效字符:

    DecimalUpDown in WPF Toolkit

    TextBox Input Behavior - 稍微复杂一点

    【讨论】:

      【解决方案4】:

      对于我的用例,我选择解析小数?作为一个字符串。我在 setter 上使用了此代码,因此如果输入了无效条目,则不会存储它:

      public string Price {
          get { return this._price.ToString(); }
          set
          {
              if (value != null)
              {
                  decimal p;
                  if (decimal.TryParse(value, out p))
                  {
                      this._price = (value);
                      this.NotifyPropertyChanged("Price");
                  }
      
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        在验证规则代码中,return 'False'ValidationResult 如果输入值(作为字符串)以NumberDecimalSeparator 结尾。 您将能够继续在文本框中输入...

        【讨论】:

          【解决方案6】:

          我遇到了同样的问题,我相信这是由于尝试设置“1”引起的。到十进制属性。 “1。”不会解析为有效的十进制值。

          你的选择是要么

          A) 从 TextBox 的 Text 属性绑定中删除“UpdateSourceTrigger”。这将允许输入任何文本并在 TextBox 失去焦点时执行验证;

          B) 添加小数点后添加小数点。例如,要输入“1.25”,输入“125”,然后将光标放在“1”和“2”之间,然后输入“.”。在这一点上关闭控件会产生一个 1.25 的绑定值,它会解析为一个有效的十进制值。

          【讨论】:

            猜你喜欢
            • 2014-02-04
            • 1970-01-01
            • 2012-03-30
            • 2020-09-13
            • 1970-01-01
            • 2013-05-01
            • 1970-01-01
            • 1970-01-01
            • 2016-08-17
            相关资源
            最近更新 更多