WPF Datagrid Cell with Validation Error Style
Asked Answered
A

3

5

I am trying to change the default style of a DataGridCell (within a WPF Toolkit DataGrid) when there is a validation error. The default is a red border. How can I put my own template?

Thanks.

Adieu answered 13/3, 2010 at 19:59 Comment(0)
G
10

Try this:

<!-- Cell Style -->
    <Style x:Key="CellErrorStyle" TargetType="{x:Type TextBlock}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors)[0].ErrorContent}"/>
                <Setter Property="Background" Value="Yellow"/>
            </Trigger>
        </Style.Triggers>
    </Style>

And use it:

        <DataGrid.Columns>
            <DataGridTextColumn 
                ElementStyle="{StaticResource CellErrorStyle}">
            </DataGridTextColumn>
        </DataGrid.Columns>
Gitagitel answered 25/7, 2013 at 21:3 Comment(1)
This works only while the cell is being edited. Then the default error style is brought back. If this was the intended effect, the title shoudl be changed into something like "WPF Datagrid Cell, Editing Validation Error Style". If this was not the intended effect, then this answer is incomplete.Uniformity
L
2

There's a nice tutorial from Diederik Krols that does exactly what you're asking for the WPF Toolkit DataGrid.

Louisalouisburg answered 14/12, 2010 at 10:20 Comment(3)
This does not actually address validation of the Cell, it addresses validation of the row... still looking for a way to override the red outline behavior, ex: put a yellow background instead...Gleeman
Really, even the section that says "Cell validation" right before the section on "Row Validation"?Louisalouisburg
I meant the "style" of the cell. The article describes how to validate the value, but not change cell style, say, to a yellow background instead of the standard red border, which was the original question. It validates the cell values, but then only modifies the row style. I found this which sort of addresses modifying the actual cell: msdn.microsoft.com/en-us/library/ee622975.aspxGleeman
C
1

One kind of solution is below, but first, let me share my findings.

It seems like the validation errors never reach inside the column's ElementStyle or CellStyle. The reason I suspect this is because it reaches and can be used in the column's EditingElementStyle and the datagrid's RowStyle.

For example, you can set the style based on Validation.HasError:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="Background" Value="Red" />
            </Trigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

or you can set the Validation.ErrorTemplate as well:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Border BorderBrush="Red" BorderThickness="3">
                        <AdornedElementPlaceholder />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

and both work just fine. Same on the EditingElementStyle. Neither of these really solves the problem: changing the row style obviously doesn't show which cell the error is in, and the editing style is not visible once the text box is defocused.

Unfortunately, for some reason, the same method doesn't work on the ElementStyle or the CellStyle. I'm inclined to believe this is a bug because in this tutorial it says after showing an example of setting a Validation.HasError triggered style on the EditingElementStyle:

You can implement more extensive customization by replacing the CellStyle used by the column.

The solution

One workaround is not to use a trigger but rather bind the background (or whatever style property you want) of the cell to a new property of the data object. I'll show what I mean.

In this example, there are products, they have a category, which will be displayed in a text column in the datagrid. Here's the XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
    <DataGrid.Columns>
        <!-- other columns -->
        <DataGridTextColumn Header="Category">
            <DataGridTextColumn.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="Background"
                            Value="{Binding Mode=OneWay, Path=CategoryErrorBackgroundColor}" />
                </Style>
            </DataGridTextColumn.CellStyle>
            <DataGridTextColumn.Binding>
                <Binding Path="Category" UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule />
                    </Binding.ValidationRules>
                </Binding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
        <!-- other columns -->
    </DataGrid.Columns>
</DataGrid>

And here's the Product class:

public class Product : INotifyPropertyChanged
{
    // ...other fields and properties
    
    private string category;
    private SolidColorBrush categoryErrorBackgroundColor;

    public string Category
    {
        get
        {
            return category;
        }
        set
        {
            // validation checks
            if (value.Lenght < 5)
            {
                CategoryErrorBackgroundColor = Brushes.Red;
                // Notice that throwing is not even necessary for this solution to work
                throw new ArgumentException("Category cannot be shorter than 5 characters.");
            }
            else
            {
                CategoryErrorBackgroundColor = Brushes.Transparent;
            }
            category = value;
        }
    }
    // This is the property I'm binding to the cell's background
    // It has to have the appropriate type
    public SolidColorBrush CategoryErrorBackgroundColor
    {
        get
        {
            return categoryErrorBackgroundColor;
        }
        set
        {
            categoryErrorBackgroundColor = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Obviously, the huge downside of this solution is that it requires one (or more if you want more complex styles) style property for every property in the data object, and it needs a lot of manual settings of these properties. But it is kind of a solution nevertheless.

Confect answered 1/12, 2020 at 21:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.