WPF验证规则阻止文本框中的小数输入?

时间:2014-01-08 19:36:56

标签: c# wpf validation textbox

我在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,当我的调试器在验证规则内部断开时,我可以看到值=&#34; 1&#34;。到现在为止还挺好。现在我继续运行并在文本框中输入一个小数点(所以我们应该有&#34; 1。&#34;现在)。同样,调试器在验证规则内部中断,并且如预期的那样,值=&#34; 1。&#34;。如果我单步执行验证规则代码,我会看到它通过了纬度值检查并返回以下内容:

new ValidationRule(true, null);

但是,一旦验证规则返回并且我进入下一行代码,我就会发现自己位于UpperLeftCornerLatitude属性setter的第一行。将鼠标置于价值之上会发现它的价值是&#34; 1&#34;而不是&#34; 1。&#34;正如我所料。所以当我继续运行我的代码时,我自然会回到文本框中,盯着&#34; 1&#34;而不是&#34; 1。&#34;。如果我删除所有断点,效果是我似乎无法在文本框中输入小数点。是否有一些显而易见的东西我在这里失踪导致我的二传手最终得到的价值是&#34; 1&#34;即使我已进入&#34; 1。&#34;在文本框中?非常感谢!

6 个答案:

答案 0 :(得分:21)

以下是解决此问题的几种方法

一个。为绑定指定LostFocus(默认文本框)

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

B中。为绑定指定Delay,允许您输入一些小数

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

℃。将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)
    {
        ...
    }
}

答案 1 :(得分:5)

.NET 4.5 UPDATE

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

FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;

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

答案 2 :(得分:2)

这真的不会很漂亮,因为WPF会在你键入时自动尝试将字符串值转换为小数;我认为这是由于默认Behavior<TextBox>。我认为快速解决此问题的最简单方法是将控件绑定到字符串属性并公开另一个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 - 稍微复杂一点

答案 3 :(得分:0)

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

您可以选择

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

B)添加小数值后添加小数点。例如,要输入“1.25”,请输入“125”,然后将光标置于“1”和“2”之间并输入“。”。此时跳过控件会产生1.25的绑定值,该值将解析为有效的十进制值。

答案 4 :(得分:0)

对于我的用例,我选择解析小数?作为一个字符串。我在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 :(得分:0)

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