如何使用 CollectionView 收听 INotifyDataErrorInfo

How to listen to INotifyDataErrorInfo using a CollectionView

我有以下场景:

XAML:

<ListView Name="lsv_edit_selectNode" Grid.Row="2" Grid.Column="0"  Grid.ColumnSpan="4" 
    Grid.RowSpan="17" IsSynchronizedWithCurrentItem="True" SelectionMode="Single" 
    ItemsSource="{Binding Path=Nodes.CollectionView, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">

其中 Nodes 是包含 ListCollectionView 的自定义 ObservableCollection

Public Class FilterableObservableCollection(Of T)
    Inherits ObservableCollection(Of T)
    Implements INotifyPropertyChanged, INotifyCollectionChanged
    Public Property CollectionView As ListCollectionView
        Get
            Return _collectionView
        End Get
        Protected Set(value As ListCollectionView)
            If value IsNot _collectionView Then
                _collectionView = value
                NotifyPropertyChanged()
            End If
        End Set
    End Property
    'etc.

T 在这种情况下是一个 Node 对象,具有许多属性,包括我感兴趣的属性(称为 NodeResults):

Public Class Node
    Inherits ObservableValidatableModelBase
        Public Property NodeResults as NodeResults
            Set
                SetAndNotify(_nodeResults, Value) ' INotifyPropertyChanged
            AddHandler _nodeResults.ErrorsChanged, AddressOf BubbleErrorsChanged ' INotifyDataErrorInfo?
             End Set
        End Property
        ' etc.

NodeResults

Public Class NodeResults
    Inherits ObservableValidatableModelBase

        ' many properties here, each validated using custom Data Annotations. This works, when I bind a text box directly to here, for example.

ObservableValidatableModelBase实现 INotifyDataErrorInfo,并将其错误存储在名为 errors:

的集合中
Private errors As New Dictionary(Of String, List(Of String))()
Public ReadOnly Property HasErrors As Boolean Implements INotifyDataErrorInfo.HasErrors
    Get
        Return errors.Any(Function(e) e.Value IsNot Nothing AndAlso e.Value.Count > 0)
    End Get
End Property

Public Function GetErrors(propertyName As String) As IEnumerable Implements INotifyDataErrorInfo.GetErrors
    Try
        If Not String.IsNullOrEmpty(propertyName) Then
            If If(errors?.Keys?.Contains(propertyName), False) _
                AndAlso If(errors(propertyName)?.Count > 0, False) Then ' if there are any errors, defaulting to false if null
                Return errors(propertyName)?.ToList() ' or Nothing if there are none.
            Else
                Return Nothing
            End If
        Else
            Return errors.SelectMany(Function(e) e.Value.ToList())
        End If
    Catch ex As Exception
        Return New List(Of String)({"Error getting errors for validation: " & ex.Message})
    End Try
End Function

Public Event ErrorsChanged As EventHandler(Of DataErrorsChangedEventArgs) Implements INotifyDataErrorInfo.ErrorsChanged

Public Sub NotifyErrorsChanged(propertyName As String)
    ErrorsChangedEvent?.Invoke(Me, New DataErrorsChangedEventArgs(propertyName))
End Sub

Public Sub BubbleErrorsChanged(sender As Object, e As DataErrorsChangedEventArgs)
    If TypeOf (sender) Is ObservableValidatableModelBase Then
        errors = DirectCast(sender, ObservableValidatableModelBase).errors
    End If
    NotifyErrorsChanged(String.Empty)
End Sub

我想要发生的是让 CollectionView 中的个人 Node 通知 ListView,以便它突出显示无效的个人条目(即有一个NodeResults 无效)在屏幕上。

我的第一直觉是 Node 不知何故需要订阅并冒泡 NodeResults' ErrorsChanged 事件,因此 ObservableValidatableModelBase [=58 上的 BubbleErrorsChanged 方法=] - 但这似乎不起作用。

另一种可能性是 - ListView 甚至有用于显示验证异常的默认模板吗?如果没有,这样的事情应该有效吗? (不是...)

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="Background" Value="{Binding Path=(Validation.Errors).CurrentItem, Converter={StaticResource ValidationExceptionToColourConverter}}"/>
     </Style>
 </ListView.ItemContainerStyle>

其中 ValidationExceptionToColourConverter 只需 returns Brushes.Red 或 Brushes.White 取决于错误是否为 Nothing

注意:将文本框直接绑定到 Nodes.NodeResults.SomeProperty 效果很好,给出了我期望的结果。

我需要以下内容:

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="Background" Value="{Binding Path=HasErrors, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BooleanToBrushConverter}}"/>
    </Style>
</ListView.ItemContainerStyle>