基础数据更改时验证行

Validate row when underlying data changes

想象一个 DataGrid,其 ItemsSource 设置为 ObservableCollection。此集合为 DataGrid 中的每一行提供一个视图模型。视图模型依次提供显示在一行中的数据和可能更改此数据的命令。此外,我在 DataGridRowValidationRules 属性 中添加了一条规则。如果我输入无效数据,此验证规则可以正常工作。

但是,如果我通过视图模型提供的命令将无效数据更改为有效数据,则仅当 DataGrid 中的当前行失去焦点时才会再次触发行验证规则。因此,显示的数据可能实际上是有效的,但 DataGrid 仍然显示红色感叹号,表明它有无效数据。在当前行失去焦点或我再次输入有效数据之前,情况一直如此。

如何强制对当前行进行第二次验证?我已经设置了 ValidatesOnTargetUpdated="True" 但这并没有解决问题。我还实现了 INotifyPropertyChanged 接口,但这也没有解决问题。

解决方案

正如用户 指出的那样,INotifyDataErrorInfo 是要走的路。我删除了行验证规则并在我的视图模型中公开了一个名为 HasErros 的 属性,它代理我的模型的 HasErrors 属性,而后者又实现了 INotifyDataErrorInfo。接下来我添加了一个自定义 RowValidationErrorTemplate

<DataGrid.RowValidationErrorTemplate>
    <ControlTemplate>
        <Grid>
            <Ellipse Width="12" Height="12" Fill="Red"/>
            <Label Content="!" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                   Foreground="White" FontSize="11"/>
        </Grid>
    </ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

并为 DataGridRowHeader

创建了以下自定义样式
<Style x:Key="MyDataGridRowHeaderStyle" TargetType="{x:Type DataGridRowHeader}">
    <!-- ... -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
                <Border>
                    <Grid>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        <Control SnapsToDevicePixels="True"
                                 Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
                                 Visibility="{Binding Path=HasErrors, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"/>
                    </Grid>
                </Border>
                <!-- ... -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

注意 Visibility 的绑定。 HasErrors属性就是我上面说的代理属性

最后,在 DataGrid 中使用该样式,如下所示

<DataGrid RowHeaderStyle="{StaticResource MyDataGridRowHeaderStyle}"
...

可以找到 BoolToVisibilityConverter 的实现 here

您可以处理 CellEditEnding,找到行并调用 BindingGroupUpdateSources:

    private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
        DataGrid dg = sender as DataGrid;
        foreach (var r in dg.Items)
        {
            DataGridRow row = dg.ItemContainerGenerator.ContainerFromItem(r) as DataGridRow;
            if (row != null)
                row.BindingGroup.UpdateSources();
        }
    }

此外,请注意也为您的绑定设置 UpdateSourceTrigger=PropertyChanged

编辑

请注意,您还可以使用 EventTrigger:

 <DataGrid x:Name="dataGrid1"   ItemsSource="{Binding Models}" DataContext="{Binding}"> 
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="CellEditEnding" >
            <i:InvokeCommandAction Command="{Binding PCommand}" 
                                   CommandParameter="{Binding  RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
            </i:InvokeCommandAction>
        </i:EventTrigger>
    </i:Interaction.Triggers> 
</DataGrid>

其中 PCommand 在 ViewModel 中并且:

    private void DoPCommand(object parameter)
    {
        DataGrid dg = parameter as DataGrid;
        if (dg != null)
            foreach (var r in dg.Items)
            {
                DataGridRow row = dg.ItemContainerGenerator.ContainerFromItem(r) as DataGridRow;
                if (row != null)
                    row.BindingGroup.UpdateSources();
            }
    }

您可以在视图模型 [=26] 中实现 INotifyDataErrorInfo 接口,而不是将 ValidationRule 添加到 DataGridRowValidationRules 属性 =] 并在您想要刷新 row/item.

的状态时引发其 ErrorChanged 事件

这是实现数据验证的MVVM方式。使用 ValidationRule 不是。

WPF 4.5:使用 INotifyDataErrorInfo 接口验证数据: https://social.technet.microsoft.com/wiki/contents/articles/19490.wpf-4-5-validating-data-in-using-the-inotifydataerrorinfo-interface.aspx