第一次加载时的验证绑定

时间:2012-03-21 10:03:50

标签: wpf validation binding

我仍然在WPF中进行验证。

我有一个自定义验证规则,要求文本出现在文本框中,即它强制执行强制字段约束。

<TextBox local:Masking.Mask="^[a-zA-Z0-9]*$" x:Name="CameraIdCodeTextBox" Grid.Row="1" Grid.Column="1">
  <Binding Path="CameraIdCode" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" ValidatesOnExceptions="True">
    <Binding.ValidationRules>
      <localValidation:RequiredFieldRule />
    </Binding.ValidationRules>
  </Binding>
</TextBox>

问题是,当Window首次加载时,TextBox中没有文本(正如您所期望的那样)。但Text属性被绑定到ViewModel上的属性,因此,验证规则正在触发,表明Window存在问题 - 在用户甚至有机会违反业务规则之前。

这是一个以前解决过的问题吗?我无法成为第一个体验这一点的人。我确定这是年轻球员的陷阱。

2 个答案:

答案 0 :(得分:0)

这有几种模式。我通常在类/模型上实现ISupportInitialize接口,这将要求您在这些方法中创建BeginInit()EndInit()我只需将私有bool _isInitializing设置为true或false

在视图模型中或在何时/何时创建/填充模型/类使用begin和end init包装它:

var o = new SampleObject();
o.BeginInit()
o.StartDate = DateTime.Now; //just some sample property...
o.EndInit();

那么根据您的ValidationRule的调用方式,您可以检查_isInitializing的状态,看看是否需要验证。

最近我一直在使用PropertyChanged触发的属性验证器,所以你可以这样做:

[CustomValidator("ValidateStartDate")]
 public DateTime StartDate
 { get ...
 {
   set
     {
       if(_startDate == value) return;
       _startDate = value;
       if(_isInitializing) return;
       RaisePropertyChange(() => StartDate);
      }..

如果您不想打扰ISupportInitialize,请在构造期间传递属性中所需的所有值,而不是属性。绑定将首次查询属性上的getter并获取它们的值,之后将通过属性setter并获得验证:

 //c-tor
 public MyObject(DateTime start)
 {
    _startDate = start;
 }

答案 1 :(得分:0)

已经有一段时间了,我应该更新这个问题。我使用Ian Griffths(O'Reilly的书)在WPF书中找到的课程来解决它:

public static class Validator
{
    /// <summary>
    /// This method forces WPF to validate the child controls of the control passed in as a parameter.
    /// </summary>
    /// <param name="parent">Type: DependencyObject. The control which is the descendent root control to validate.</param>
    /// <returns>Type: bool. The validation result</returns>
    public static bool IsValid(DependencyObject parent)
    {
        // Validate all the bindings on the parent
        bool valid = true;
        LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
        while (localValues.MoveNext())
        {
            LocalValueEntry entry = localValues.Current;
            if (BindingOperations.IsDataBound(parent, entry.Property))
            {
                Binding binding = BindingOperations.GetBinding(parent, entry.Property);
                foreach (ValidationRule rule in binding.ValidationRules)
                {
                    ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
                    if (!result.IsValid)
                    {
                        BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
                        Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                        valid = false;
                    }
                }
            }
        }

        // Validate all the bindings on the children
        for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (!IsValid(child))
            {
                valid = false;
            }
        }

        return valid;
    }

}

然后,在视图中,我有以下配置:

<TextBox local:Masking.Mask="^[0-9]*$" IsEnabled="{Binding Path=LocationNumberEnabled}" Grid.Row="1" Grid.Column="3">
    <Binding Path="LocationNumber"  Mode="TwoWay" UpdateSourceTrigger="LostFocus" NotifyOnValidationError="True" ValidatesOnExceptions="True">
        <Binding.ValidationRules>
            <localValidation:PositiveNumberRule />
            <localValidation:RequiredFieldRule />
        </Binding.ValidationRules>
    </Binding>                    
</TextBox>

像魅力一样工作!每当我想手动验证时,我就调用了IsValid方法。按下按钮。