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

Wpf 强制调用IDataErrorInfo.this[string columnName],以便更新验证工具提示,wpf,validation,tooltip,idataerrorinfo,Wpf,Validation,Tooltip,Idataerrorinfo,我有一个棘手的问题,WPF数据网格和验证错误工具提示在验证消息更改时不更新。这是.Net 4代码,因此我不能使用INotifyDataErrorInfo 我有一个绑定到数据网格的ObservableCollection。集合中的对象类型实现了IDataErrorInfo,因此我们可以支持验证并突出显示具有无效值的字段。这在大多数情况下都很有效。但是,在以下场景中,工具提示中显示的消息存在问题: 字段A有两个规则Rule 1和Rule S(共享规则) 字段B有一个规则S(共享规则) 规则S是一个共

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

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

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

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

    Field A < "Rule 1 is invalid."
    Field B < (valid - no tooltip)
    
    字段A<“规则1无效。”
    字段B<(有效-无工具提示)
    
  • 注意,字段A的验证状态没有更改(validation.HasError不会更改值),只有绑定到工具提示的消息

    我们实际上看到的是:

    Field A < "Rule 1 is invalid. Rule S is invalid"
    Field B < (valid - no tooltip)
    
    字段A<“规则1无效。规则S无效”
    字段B<(有效-无工具提示)
    
    注意:此时类实例上的基础ValidationError数据是正确的

    似乎UI不会更新字段A的工具提示文本,除非我们强制它重新查询状态并再次调用IDataErrorInfo。此[string columnName]。我发现强制执行此操作的唯一方法是手动引发字段A的属性更改事件。但是,我不想这样做,因为字段A的值实际上没有更改,只有绑定的错误消息。当这个解决方案工作时,额外的和不必要的属性更改事件会对大量数据的性能产生影响

    如何强制为字段B调用IDataErrorInfo.this[string columnName],而不必引发属性更改事件

    注意:这是我们用来显示验证消息的错误数据模板

        <!-- 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>
    

    您只需为每个setter中的两个属性提出
    属性更改
    ,即:

    • A
      B
    • A
      B
      的设置器中为
      B
      提升
      PropertyChanged

    这是我知道的唯一方法。

    我通常在我的对象上包含一个
    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;
        }
    
    }
    
    然后,只要随时调用
    IsValid
    即可触发验证,例如

    if (SomeObject.IsValid)
        CanSave = true;
    
    对于我来说,当属性发生更改时,
    PropertyChange
    通知会重新计算命令的
    CanSave()
    也很常见,例如

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


    但是,尽管如此,如果你要重新评估一个字段是否有效的唯一时间是当另一个字段发生变化时,那么最好的方法可能是在
    FieldA
    发生变化时为
    FieldB
    发出
    PropertyChange
    通知。

    这会起作用,但似乎是一个非常强力的解决方案(见我问题末尾的评论).A的值没有更改,只有与A关联的错误信息。在复杂场景中,引发属性更改事件会不必要地降低性能,因为太多挂起属性更改事件。我只希望框架再次调用IDataErrorInfo。如果不引发属性更改事件,真的没有办法做到这一点吗?@M阿克斯帕默:如果我知道另一种方法,我会把它贴出来。你可以自由等待其他答案:)
    if (e.PropertyName == "FieldA" || e.PropertyName == "FieldB")
        ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
    
    void CanSave()
    {
        return SomeObject.IsValid;
    }