How to Set NotifyOnValidationError to True?

Oct 13, 2010 at 1:08 AM

Hi,

I'm using RowValidationRules with a DataGrid as follow:

MainWindow.xaml

<Window x:Class="TestDataGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestDataGrid"
        Title="Test DataGrid" Height="300" Width="500" x:Name="Me">
    <Window.Resources>
        <Style x:Key="RowStyle" TargetType="DataGridRow">
            <EventSetter Event="Validation.Error" Handler="OnValidationError" />
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="BorderThickness" Value="1"/>
                    <Setter Property="BorderBrush" Value="Red"/>
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <DataGrid AutoGenerateColumns="False"
                  ItemsSource="{Binding ElementName=Me, Path=Appointments}"
                  RowStyle="{StaticResource RowStyle}">
            <DataGrid.RowValidationRules>
                <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
            </DataGrid.RowValidationRules>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, NotifyOnValidationError=True}"/>
                <DataGridTextColumn Header="Age" Binding="{Binding Path=Age, NotifyOnValidationError=True}"/>
                <DataGridTextColumn Header="Start" Binding="{Binding Path=StartTime, NotifyOnValidationError=True}"/>
                <DataGridTextColumn Header="End" Binding="{Binding Path=EndTime, NotifyOnValidationError=True}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow
{
    public ObservableCollection<Appointment> Appointments { get; private set; }

    public MainWindow()
    {
        Appointments = new ObservableCollection<Appointment>
        {
            new Appointment { Name = "John", Age = 40, StartTime = new DateTime(2010, 1, 1), EndTime = new DateTime(2010, 1, 2) },
            new Appointment { Name = "Mary", Age = 30, StartTime = new DateTime(2010, 1, 1), EndTime = new DateTime(2010, 1, 2) },
            new Appointment { Name = "Frank", Age = 20, StartTime = new DateTime(2010, 1, 1), EndTime = new DateTime(2010, 1, 2) },
            new Appointment { Name = "Jack", Age = 10, StartTime = new DateTime(2010, 1, 1), EndTime = new DateTime(2010, 1, 2) }
        };

        InitializeComponent();
    }

    private void OnValidationError(object sender, ValidationErrorEventArgs e)
    {
        Trace.WriteLine("OnValidationError has been called: " + e.Action);
    }
}

The problem is the OnValidationError event handler that is never being called... Is there anything I'm doing wrong?

I have done some investigations to find that the NotifyOnValidationError property of the BindingGroup used by the DataGrid (ItemsControl.ItemBindingGroup) is never set to true. This is the reason why the event handler is never being called. To fix it, I'm using the following workaround (not very clean but it works). I set it to True by code in the ValidationRule itself:

public class RowDataInfoValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        BindingGroup group = (BindingGroup)value;
        StringBuilder error = null;

        foreach (var item in group.Items)
        {
            // aggregate errors
            IDataErrorInfo info = item as IDataErrorInfo;
            if (info == null)
                continue;

            if (string.IsNullOrEmpty(info.Error))
                continue;

            if (error == null)
                error = new StringBuilder();

            group.NotifyOnValidationError = true;

            error.Append((error.Length != 0 ? ", " : "") + info.Error);
        }

        return error == null ? ValidationResult.ValidResult : new ValidationResult(false, error.ToString());
    }
}

How can I set the NotifyOnValidationError property to true in XAML? Is there any order way to make this work?

I can provide the full code of my test application if required.

Thank you