基础数据更改时验证行
Validate row when underlying data changes
想象一个 DataGrid,其 ItemsSource
设置为 ObservableCollection
。此集合为 DataGrid
中的每一行提供一个视图模型。视图模型依次提供显示在一行中的数据和可能更改此数据的命令。此外,我在 DataGrid
的 RowValidationRules
属性 中添加了一条规则。如果我输入无效数据,此验证规则可以正常工作。
但是,如果我通过视图模型提供的命令将无效数据更改为有效数据,则仅当 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
,找到行并调用 BindingGroup
的 UpdateSources
:
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
添加到 DataGrid
的 RowValidationRules
属性 =] 并在您想要刷新 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
想象一个 DataGrid,其 ItemsSource
设置为 ObservableCollection
。此集合为 DataGrid
中的每一行提供一个视图模型。视图模型依次提供显示在一行中的数据和可能更改此数据的命令。此外,我在 DataGrid
的 RowValidationRules
属性 中添加了一条规则。如果我输入无效数据,此验证规则可以正常工作。
但是,如果我通过视图模型提供的命令将无效数据更改为有效数据,则仅当 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
,找到行并调用 BindingGroup
的 UpdateSources
:
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
添加到 DataGrid
的 RowValidationRules
属性 =] 并在您想要刷新 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