强制调用IDataErrorInfo.this [string columnName]以更新验证工具提示

时间:2013-05-13 15:58:15

标签: wpf validation tooltip idataerrorinfo

我有一个棘手的问题,WPF数据网格和验证错误工具提示在验证消息更改时不会更新。这是.Net 4代码,所以我不能使用 INotifyDataErrorInfo

我有一个绑定到数据网格的 ObservableCollection 。集合中的对象类型实现 IDataErrorInfo ,以便我们可以支持验证并突出显示具有无效值的字段。在大多数情况下,这样做很好。但是,在以下方案中,工具提示中显示的消息存在问题:

  1. 字段A有两个规则规则1和规则S(共享规则)
  2. 字段B有一条规则S(共享规则)
  3. 规则S是一个共享规则,而不是引用字段A和字段B
  4. 如果规则1和规则S都无效,我们会为每个字段显示以下验证工具提示,这是我们想要的行为:

    Field A < "Rule 1 is invalid. Rule S is invalid"
    Field B < "Rule S is invalid"
    
  5. 如果我们现在编辑字段B以使规则S有效。我们希望工具提示消息更新如下:

    Field A < "Rule 1 is invalid."
    Field B < (valid - no tooltip)
    
  6. 注意,字段A的验证状态没有改变(Validation.HasError不改变值),只有绑定到工具提示的消息。

    我们实际看到的是:

    Field A < "Rule 1 is invalid. Rule S is invalid"
    Field B < (valid - no tooltip)
    

    此时,类实​​例上的基础ValidationError数据是正确的。

    似乎UI不会更新字段A的工具提示文本,除非我们强制它重新查询状态并再次调用IDataErrorInfo.this [string columnName]。我发现迫使这种情况发生的唯一方法是手动提升字段A的属性更改事件。但是,我不想这样做,因为字段A的值实际上没有改变,只有绑定错误消息。虽然这个解决方案可以工作,但额外的和不必要的属性更改事件会带来大量数据的性能。

    我可以做些什么来强制IDataErrorInfo.this [string columnName]为字段B调用而不必提升属性更改事件?

    这里是我们用来显示验证消息的错误数据模板。

        <!-- ERROR HANDLING Data Template -->
        <Style x:Key="controlBaseStyle"
               TargetType="{x:Type Control}">
    
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" 
                                BorderThickness="2"
                                Visibility="{Binding ElementName=placeholder, Path=AdornedElement.IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
                            <AdornedElementPlaceholder x:Name="placeholder"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
    
            <Setter Property="ToolTipService.ShowOnDisabled" Value="true"/>
    
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem, Converter={StaticResource ErrorContentConverter}}"/>
                </Trigger>
    
    
                <!--We don't want to see the validation if the control is disabled.  This doesn't affect it if the control is read only. -->
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Validation.ErrorTemplate">
                        <Setter.Value>
                            <ControlTemplate>
                                <AdornedElementPlaceholder x:Name="placeholder"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
    
        </Style>
    

2 个答案:

答案 0 :(得分:0)

您只需要为每个setter中的两个属性引发PropertyChanged,即:

  • PropertyChangedA
  • 的设置器中为A提升B
  • PropertyChangedB
  • 的设置器中为A提升B

这是我所知道的唯一方式。

答案 1 :(得分:0)

我通常在我的对象上包含IsValid属性,该属性将通过验证规则,如果对象有效,则返回true / false。

通常我的代码看起来像这样。根据您的实施方式IDataErrorInfo

,您的解决方案可能会有所不同
public class MyObject : ValidatingObject
{
    public MyObject()
    {
        // Add Properties to Validate here
        this.ValidatedProperties.Add("FieldA");
        this.ValidatedProperties.Add("FieldB");
    }

    // Implement validation rules here
    public override string GetValidationError(string propertyName)
    {
        if (ValidatedProperties.IndexOf(propertyName) < 0)
        {
            return null;
        }

        string s = null;

        switch (propertyName)
        {
            case "FieldA":
            case "FieldB":
                if (FieldA <= FieldB)
                    s = "FieldA must be greater than FieldB";
                break;
        }

        return s;
    }

}

实现ValidatingObject的{​​{1}}基类通常包含以下内容:

IDataErrorInfo

然后,您只需在需要时调用#region IDataErrorInfo & Validation Members /// <summary> /// List of Property Names that should be validated /// </summary> protected List<string> ValidatedProperties = new List<string>(); public abstract string GetValidationError(string propertyName); string IDataErrorInfo.Error { get { return null; } } string IDataErrorInfo.this[string propertyName] { get { return this.GetValidationError(propertyName); } } public bool IsValid { get { return (GetValidationError() == null); } } public string GetValidationError() { string error = null; if (ValidatedProperties != null) { foreach (string s in ValidatedProperties) { error = GetValidationError(s); if (error != null) { return error; } } } return error; } #endregion // IDataErrorInfo & Validation Members 即可触发验证,例如

IsValid

当属性发生变化时,我有一个if (SomeObject.IsValid) CanSave = true; 通知可以重新评估命令的PropertyChange,这一点并不少见,例如

CanSave()

if (e.PropertyName == "FieldA" || e.PropertyName == "FieldB")
    ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();

但是,尽管如此,如果你唯一一次重新评估一个字段是否有效是另一个字段发生变化的话,那么最好只选择Daniel said并提出{{ void CanSave() { return SomeObject.IsValid; } 更改PropertyChange时的{1}}通知。