为什么文本框的值重置为以前的值而不显示错误?

我有一个示例,其中将视图模型的属性与一些TextBox控件(包括验证规则)绑定在一起.在大多数情况下,这可以正常工作.但是,当我尝试包括绑定的TextBox的IsFocused属性时,如果在控件中输入了无效的数字,就会遇到麻烦.

当我在直接绑定到视图模型属性的TextBox控件中输入错误的数字时,错误将按预期显示(TextBox周围的红色边框).但是,在与包含文本框的视图模型属性和IsFocused属性的MultiBinding绑定的TextBox中,不会显示错误,并且该值将重置为先前的有效值.

例如,如果小于10的数字无效,并且我输入3,则当TextBox失去焦点时,通常在TextBox中会出现一个红色边框,表示错误.但是在包含IsFocused作为其绑定源的TextBox中,该值将更改回之前的有效值(如果在我输入3之前有39,则TextBox会更改回39).

使用下面的代码,您可以重现该问题:

TestViewModel.cs

public class TestViewModel
{
    public double? NullableValue { get; set; }
}

MainWindow.xaml

<Window x:Class="TestSO34204136TextBoxValidate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:TestSO34204136TextBoxValidate"
        Title="MainWindow" Height="350" Width="525">

  <Window.DataContext>
    <l:TestViewModel/>
  </Window.DataContext>

  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Nullable: "/>
    <TextBox VerticalAlignment="Top" Grid.Column="1">
      <TextBox.Text>
        <MultiBinding Mode="TwoWay">
          <Binding Path="NullableValue"/>
          <Binding Path="IsFocused"
                   RelativeSource="{RelativeSource Self}"
                   Mode="OneWay"/>
          <MultiBinding.ValidationRules>
            <l:ValidateIsBiggerThanTen/>
          </MultiBinding.ValidationRules>
          <MultiBinding.Converter>
            <l:TestMultiBindingConverter/>
          </MultiBinding.Converter>
        </MultiBinding>
      </TextBox.Text>
    </TextBox>
    <TextBox VerticalAlignment="Top" Grid.Column="2"/>
  </Grid>
</Window>

TestMultiBindingConverter.cs

public class TestMultiBindingConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] != null)
            return values[0].ToString();
        return DependencyProperty.UnsetValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            double doubleValue;
            var stringValue = value.ToString();
            if (Double.TryParse(stringValue, out doubleValue))
            {
                object[] values = { doubleValue };
                return values;
            }
        }

        object[] values2 = { DependencyProperty.UnsetValue };
        return values2;
    }
}

ValidateIsBiggerThanTen.cs

public class ValidateIsBiggerThanTen : ValidationRule
{
    private const string errorMessage = "The number must be bigger than 10";

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        var error = new ValidationResult(false, errorMessage);

        if (value == null)
            return new ValidationResult(true, null);

        var stringValue = value.ToString();
        double doubleValue;
        if (!Double.TryParse(stringValue, out doubleValue))
            return new ValidationResult(true, null);

        if (doubleValue <= 10)
            return error;
        return new ValidationResult(true, null);
    }
}

为什么在上面的示例中没有显示TextBox的错误?

解决方法:

您看到的行为的原因具体是您在MultiBinding中绑定了TextBox的IsFocused属性.当焦点改变时,这直接具有迫使更新绑定目标的效果.

在验证失败的情况下,会在很短的时间内触发验证规则,设置了错误,但是焦点实际上尚未更改.但这一切发生得太快,以至于用户看不到.而且由于验证失败,因此绑定源不会更新.

因此,当IsFocused属性值更改时,在对输入的值进行验证和拒绝后,接下来要发生的事情是重新评估绑定(因为源属性之一已更改!)以更新目标.并且由于实际源值从未更改,因此目标(文本框)将从您键入的内容恢复为源中存储的内容.

您应该如何解决?这取决于所需的确切行为.您有三个基本选项:

>保持对IsFocused的绑定,并添加UpdateSourceTrigger =“ PropertyChanged”.这将保持基本的当前行为,即失去焦点时将旧值复制回去,但至少会在编辑值时为用户提供立即的验证反馈.
>完全删除与IsFocused的绑定.这样,绑定的目标将不再依赖于此,并且在焦点更改时也不会重新评估.问题解决了.

上一篇:javax.validation - BindingResult


下一篇:PHP-Symfony断言类型上