Wednesday, May 18, 2011

WPF Toolkit DataGrid – Conditional Formatting

Often you may want to highlight certain data entries inside a datagrid according to given conditions. Imagine displaying business sales. Here you may want to highlight all rows that don’t reach a certain amount in the corresponding value column in order to get a quick overview. That is where conditional formatting comes into play. 

This post explains a possibility to achieve conditional formatting inside a WPF Toolkit Datagrid using styles and triggers. It will focus on formatting rows or just single cells. This is achieved by adding style information to the datagrids row and cell style at runtime. The entire style will be generated in code. If you just need some XAML to get you started doing conditional formatting, scroll down and use the XAML snippet.  Let’s first create some building bricks for our solution.
The Condition
It’s obvious that we need to define the condition in any way to get conditional formatting. According to your data you may have lots of conditions you may need to filter at. In order to demonstrate the concept I will use numeric values. Again there is a lot that you can compare using numbers. The following enumeration defines the most common operations:

public enum NumericOperator
{
    //A equals B
    Equals = 1, 
    //A is greater than B
    GreaterThan = 2,
    //A is smaller than B
    SmallerThan = 4, 
    //A is greater or equals B
    GreaterThanEquals = 8,
    //A is smaller or equals B
    SmallerThanEquals = 16,
}
A type of NumericOperator can be used to define the comparison operator. Now we need the values to be compared. The first value comes from the underlying objects that are displayed in the grid. More specifically it is the value of a property from the bound object. For simplicity we define where to find that value by just passing the name. This is acceptable because I will use the name inside a binding element so no extra work is required. The second value must be provided to the formatter by just passing the value itself. This is a static value set by the user. To sum it up, the following parameters need to be passed to the formatter in order to define a condition:
-          A value of the NumericOperator enumeration
-          The name of a property of the underlying object
-          The compare value
The Conditional Formatter
The formatter receives the condition parameters defined above and a reference to the grid:

new DataGridConditionalRowFormatter(this.dataGrid, "Price",
NumericOperator.GreaterThan, "10")
    {
        ConditionalBackgroundColor = Colors.OrangeRed,
        ConditionalTextColor = Colors.White,
        NormalBackgroundColor = Colors.LightGreen
    };

At runtime it generates a style object and applies it to the appropriate style property of the grid (e.g. RowStyle or CellStyle). The XAML code it produces looks similar to the following snippet:

<Style TargetType="DataGridCell" >
  <Style.Triggers>
    <DataTrigger Binding="{Binding Price,
    Converter=NumberToBoolConverter}" Value="True">
      <Setter Property="Background" Value="OrangeRed"/>
    </DataTrigger>
    <DataTrigger Binding="{Binding Price,
    Converter=NumberToBoolConverter}" Value="False">
      <Setter Property="Background" Value="White"/>
    </DataTrigger>
  </Style.Triggers>
</Style>

It uses DataTriggers to set the background color of a row or cell if the result of the binding evaluates to true and back to a standard color if it evaluates to false. Therefore converters have to be used in order to evaluate the condition and return a boolean value. If you use a boolean property as binding source than actually no converter is needed.

I started a small codeplex project with my solution where you can download a library containing a conditional row and cell formatter that are able to do numeric, string and boolean evaluation (see pictures below). As far as my spare time allows it I will extend the library with additional features. Any feedback is very welcome.

Conditional row formatting: rows with a price greater ten are highlighted
Conditional cell formatting: names that contain "1" are highlighted

No comments:

Post a Comment