Imagine a DataGrid with its ItemsSource
set to an ObservableCollection
. This collection provides a view model for each row in the DataGrid
. The view model in turn provides the data that is displayed in one row and a command that may change this data. Additionally, I added a rule to the RowValidationRules
property of DataGrid
. This validation rule works fine in case I enter invalid data.
However, if I change the invalid data to valid data via the command the view model provides, the row validation rule only gets triggered again if the current row in DataGrid
loses focus. Hence, the displayed data may be actually valid, but the DataGrid
still displays a red exclamation mark showing it has invalid data. This remains the case until the current row loses focus or I enter valid data again.
How do I force a second validation of the current row? I already set ValidatesOnTargetUpdated="True"
but this didn't solve the problem. I also have implemented the INotifyPropertyChanged interface but this also didn't fix the problem.
Solution
As user mm8 pointed out, INotifyDataErrorInfo
is the approach to go. I removed the row validation rule and exposed a property named HasErros
in my view model that proxies the HasErrors
property of my model that in turn implements INotifyDataErrorInfo
. Next I added a custom 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>
and created the following custom style for 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>
Note the binding of Visibility
. The HasErrors
property is the proxy property I mentioned above.
And finally, use that style in the DataGrid
as follows
<DataGrid RowHeaderStyle="{StaticResource MyDataGridRowHeaderStyle}"
...
An implementation of BoolToVisibilityConverter
can be found here.