Telerik blogs

PART II [example: Conditional formatting of cells in RadGridView for WPF and Silverlight]

 

Lets say we have a list of products and we need to mark higher prices (>15.00)  with a red color. Something like :

 

prices_screenshot

 

First lets try it with RadGridView for WPF :

We have to create a small IValueConverter. It will take care to convert the value of the price to the appropriate color:

   1: public class PriceToColorConverter : IValueConverter
   2:     {
   3: public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   4:         {
   5:             SolidColorBrush brush = new SolidColorBrush();
   6: decimal price = (decimal) value;
   7:  
   8: if (price > 15)
   9:                 brush.Color = Colors.Red;
  10: else
  11:                 brush.Color = Colors.Green;
  12:  
  13: return brush;
  14:         }
  15:  
  16: public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  17:         {
  18: throw new NotImplementedException();
  19:         }
  20:     }

 

We use this converter to bind the Foreground property of the cell to the product’s Price :

   1: <Grid>
   2: <Grid.Resources>
   3: <local:PriceToColorConverter x:Key="MyConverter" />
   4: </Grid.Resources>
   5: <telerik:RadGridView x:Name="RadGridView1" AutoGenerateColumns="False" >
   6: <telerik:RadGridView.Columns>
   7: <telerik:GridViewDataColumn DataMemberBinding="{Binding ProductName}" Header="Product" />
   8: <telerik:GridViewDataColumn DataMemberBinding="{Binding Price}" Header="Price">
   9: <telerik:GridViewDataColumn.CellStyle>
  10: <Style TargetType="telerik:GridViewCell">
  11: <Setter Property="Foreground" Value="{Binding Price, Converter={StaticResource MyConverter}}" />
  12: </Style>
  13: </telerik:GridViewDataColumn.CellStyle>
  14: </telerik:GridViewDataColumn>
  15: </telerik:RadGridView.Columns>
  16: </telerik:RadGridView>
  17: </Grid>

And that is all – nice and easy! You may see this in action in 

 

 

Now lets  try it with RadGridView for Silverlight:

Lets play with the Background property this time.

You know – our RadGridView controls share a common codebase and in most cases code is reusable. Unfortunately not in this case. Silverlight does not support bindings in style setters.

Here is the workaround. We are going to reuse our nice little converter and we need to find it a place. A good place would be the template of the GridViewCell. We are going to modify it , binding the Background with the help of our converter. And here we have to paste the whole 100+  lines of XAML GridViewCell template.

   1: <UserControl x:Class="ConditionalCellFormatting.MainPage"
   2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   5: xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
   6: xmlns:gridview="clr-namespace:Telerik.Windows.Controls.GridView;assembly=Telerik.Windows.Controls.GridView"
   7: xmlns:local="clr-namespace:ConditionalCellFormatting"
   8: mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
   9: <UserControl.Resources>
  10: <local:PriceToColorConverter x:Key="MyConverter" />
  11: 
  12: <SolidColorBrush x:Key="GridLinesFill" Color="#FFB3B3B3"/>
  13: <SolidColorBrush x:Key="GridViewRowNormalBackground" Color="#FFFFFFFF"/>
  14: <LinearGradientBrush x:Key="GridViewRowSelectedBackground" EndPoint="0.5,1" StartPoint="0.5,0">
  15: <GradientStop Color="#FFFFCA5D" Offset="1"/>
  16: <GradientStop Color="#FFFFDC9C" Offset="0"/>
  17: </LinearGradientBrush>
  18: <SolidColorBrush x:Key="GridViewDisabledBackground" Color="#FFEEEEEE"/>
  19: <SolidColorBrush x:Key="GridViewDisabledBorderBrush" Color="#FFBBBBBB"/>
  20: <SolidColorBrush x:Key="GridViewDisabledForeground" Color="#FF6F6F6F"/>
  21: <ControlTemplate x:Key="GridViewCellTemplate" TargetType="gridview:GridViewCell">
  22: <Grid x:Name="SelectionBackground" Background="{Binding Price, Converter={StaticResource MyConverter}}">
  23: <VisualStateManager.VisualStateGroups>
  24: <VisualStateGroup x:Name="CommonStates">
  25: <VisualState x:Name="Normal"/>
  26: </VisualStateGroup>
  27: <VisualStateGroup x:Name="EditingStates">
  28: <VisualState x:Name="Edited">
  29: <Storyboard>
  30: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_EditorPresenter" Storyboard.TargetProperty="Visibility">
  31: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  32: </ObjectAnimationUsingKeyFrames>
  33: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentPresenter" Storyboard.TargetProperty="Visibility">
  34: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
  35: </ObjectAnimationUsingKeyFrames>
  36: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="GridViewCellBorder" Storyboard.TargetProperty="Background">
  37: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource GridViewRowNormalBackground}"/>
  38: </ObjectAnimationUsingKeyFrames>
  39: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentBorder" Storyboard.TargetProperty="Visibility">
  40: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  41: </ObjectAnimationUsingKeyFrames>
  42: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentBorder" Storyboard.TargetProperty="Stroke">
  43: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource GridViewRowSelectedBackground}"/>
  44: </ObjectAnimationUsingKeyFrames>
  45: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentBorder" Storyboard.TargetProperty="StrokeThickness">
  46: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="2"/>
  47: </ObjectAnimationUsingKeyFrames>
  48: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentBorder" Storyboard.TargetProperty="StrokeDashArray">
  49: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="0"/>
  50: </ObjectAnimationUsingKeyFrames>
  51: </Storyboard>
  52: </VisualState>
  53: <VisualState x:Name="Display">
  54: <Storyboard>
  55: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentPresenter" Storyboard.TargetProperty="Visibility">
  56: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  57: </ObjectAnimationUsingKeyFrames>
  58: </Storyboard>
  59: </VisualState>
  60: <VisualState x:Name="Current">
  61: <Storyboard>
  62: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_EditorPresenter" Storyboard.TargetProperty="Visibility">
  63: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
  64: </ObjectAnimationUsingKeyFrames>
  65: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentPresenter" Storyboard.TargetProperty="Visibility">
  66: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  67: </ObjectAnimationUsingKeyFrames>
  68: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CurrentBorder" Storyboard.TargetProperty="Visibility">
  69: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  70: </ObjectAnimationUsingKeyFrames>
  71: </Storyboard>
  72: </VisualState>
  73: </VisualStateGroup>
  74: <VisualStateGroup x:Name="ValueStates">
  75: <VisualState x:Name="Valid">
  76: <Storyboard>
  77: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="InvalidBorder" Storyboard.TargetProperty="Visibility">
  78: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
  79: </ObjectAnimationUsingKeyFrames>
  80: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ErrorIcon" Storyboard.TargetProperty="Visibility">
  81: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Collapsed"/>
  82: </ObjectAnimationUsingKeyFrames>
  83: </Storyboard>
  84: </VisualState>
  85: <VisualState x:Name="Invalid">
  86: <Storyboard>
  87: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="InvalidBorder" Storyboard.TargetProperty="Visibility">
  88: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  89: </ObjectAnimationUsingKeyFrames>
  90: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ErrorIcon" Storyboard.TargetProperty="Visibility">
  91: <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"/>
  92: </ObjectAnimationUsingKeyFrames>
  93: </Storyboard>
  94: </VisualState>
  95: </VisualStateGroup>
  96: <VisualStateGroup x:Name="DisabledStates">
  97: <VisualState x:Name="Enabled"/>
  98: <VisualState x:Name="Disabled">
  99: <Storyboard>
 100: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectionBackground" Storyboard.TargetProperty="Background">
 101: <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource GridViewDisabledBackground}"/>
 102: </ObjectAnimationUsingKeyFrames>
 103: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HorizontalGridLine" Storyboard.TargetProperty="Fill">
 104: <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource GridViewDisabledBorderBrush}"/>
 105: </ObjectAnimationUsingKeyFrames>
 106: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_VerticalGridLine" Storyboard.TargetProperty="Fill">
 107: <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource GridViewDisabledBorderBrush}"/>
 108: </ObjectAnimationUsingKeyFrames>
 109: <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_ContentPresenter" Storyboard.TargetProperty="Foreground">
 110: <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource GridViewDisabledForeground}"/>
 111: </ObjectAnimationUsingKeyFrames>
 112: </Storyboard>
 113: </VisualState>
 114: </VisualStateGroup>
 115: </VisualStateManager.VisualStateGroups>
 116: <Border x:Name="GridViewCellBorder" UseLayoutRounding="True" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
 117: <gridview:AlignmentContentPresenter x:Name="PART_ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Visibility="Visible" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Foreground="{TemplateBinding Foreground}" TextAlignment="{TemplateBinding TextAlignment}" TextDecorations="{TemplateBinding TextDecorations}" TextWrapping="{TemplateBinding TextWrapping}"/>
 118: <ContentPresenter x:Name="PART_EditorPresenter" HorizontalAlignment="Stretch" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Visibility="Collapsed"/>
 119: <Rectangle x:Name="PART_HorizontalGridLine" Fill="{StaticResource GridLinesFill}" Height="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
 120: <Rectangle x:Name="PART_VerticalGridLine" Fill="{StaticResource GridLinesFill}" HorizontalAlignment="Right" VerticalAlignment="Stretch" Width="1"/>
 121: <Rectangle x:Name="CurrentBorder" Stroke="#FF000000" StrokeDashArray="1.5" StrokeDashCap="Round" StrokeDashOffset="0" StrokeThickness="1" Height="Auto" Margin="0" Width="Auto" Visibility="Collapsed"/>
 122: <Rectangle x:Name="InvalidBorder" Stroke="#FFDB000C" StrokeThickness="2" Height="Auto" Margin="0" Width="Auto" Visibility="Collapsed"/>
 123: <Grid x:Name="ErrorIcon" Height="18" HorizontalAlignment="Right" Margin="2,0,2,0" VerticalAlignment="Center" Width="18" Visibility="Collapsed">
 124: <Ellipse Fill="#FF535353"/>
 125: <Ellipse Fill="#FFCE3527" Stroke="#FFFFFFFF" Margin="1,1,1,1"/>
 126: <Path Stretch="Fill" Margin="0.999,1,1.001,8.134" Data="M16,8 C8.127,4.9999293 6.627,10.999763 0,8 0,3.581722 3.581722,0 8,0 12.418278,0 16,3.581722 16,8 z">
 127: <Path.Fill>
 128: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
 129: <GradientStop Color="#99FFFFFF" Offset="0"/>
 130: <GradientStop Color="#33FFFFFF" Offset="1"/>
 131: </LinearGradientBrush>
 132: </Path.Fill>
 133: </Path>
 134: <Path Stretch="Fill" Stroke="#FF000000" StrokeThickness="2" Margin="5.168,5.748,4.832,4.252" Data="M0.50001547,0.5 L6.5000797,6.5000169 M6.5000155,0.5 L0.5,6.5000704"/>
 135: <Path Fill="#FFCE3527" Stretch="Fill" Stroke="#FFFFFFFF" StrokeThickness="2" Margin="5.085,5.084,4.915,4.916" Data="M0.50001547,0.5 L6.5000797,6.5000169 M6.5000155,0.5 L0.5,6.5000704"/>
 136: </Grid>
 137: </Grid>
 138: </ControlTemplate>
 139: <Style x:Key="GridViewCellStyle" TargetType="gridview:GridViewCell">
 140: <Setter Property="Template" Value="{StaticResource GridViewCellTemplate}"/>
 141: <Setter Property="Padding" Value="3,0,3,0"/>
 142: <Setter Property="BorderBrush" Value="{StaticResource GridViewRowSelectedBackground}"/>
 143: <Setter Property="VerticalContentAlignment" Value="Center"/>
 144: <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
 145: <Setter Property="Background" Value="Transparent"/>
 146: <Setter Property="UseLayoutRounding" Value="True"/>
 147: </Style>
 148: </UserControl.Resources>
 149: <Grid x:Name="LayoutRoot">
 150: <telerik:RadGridView x:Name="RadGridView1" AutoGenerateColumns="False" >
 151: <telerik:RadGridView.Columns>
 152: <telerik:GridViewDataColumn DataMemberBinding="{Binding ProductName}" Header="Product" />
 153: <telerik:GridViewDataColumn CellStyle="{StaticResource GridViewCellStyle}" DataMemberBinding="{Binding Price}" Header="Price"/>
 154: </telerik:RadGridView.Columns>
 155: </telerik:RadGridView>
 156: </Grid>
 157: </UserControl>

Here is what we have got this time:

 

prices_screenshot_Silverlight

 

For your copy/paste needs , please use

 

 

Now that is great. What I do not like here actually is that large ugly 5 screens of XAML thing. I may blame the Silverlight platform , I may blame our team for not providing a better way. I will not do that. Instead lets try find a better way via…

Using an attached behavior for conditional formatting in RadGridView for Silverlight

Here is the few-lines-of-code solution :

   1: <telerik:RadGridView x:Name="RadGridView1" AutoGenerateColumns="False" >
   2: <i:Interaction.Behaviors>
   3: <local:ConditionalFormattingBehavior/>
   4: </i:Interaction.Behaviors>
   5: <telerik:RadGridView.Columns>
   6: <telerik:GridViewDataColumn DataMemberBinding="{Binding ProductName}" Header="Product" />
   7: <telerik:GridViewDataColumn DataMemberBinding="{Binding Price}" Header="Price"/>
   8: </telerik:RadGridView.Columns>
   9: </telerik:RadGridView>

 

The code of the formatting behavior itself (for brevity - hardcoded for the Price column) :

   1: public class ConditionalFormattingBehavior : Behavior<RadGridView>
   2:     {
   3: protected override void OnAttached()
   4:         {
   5: base.OnAttached();
   6: this.AssociatedObject.RowLoaded += new EventHandler<Telerik.Windows.Controls.GridView.RowLoadedEventArgs>(AssociatedObject_RowLoaded);
   7:         }
   8:  
   9: void AssociatedObject_RowLoaded(object sender, Telerik.Windows.Controls.GridView.RowLoadedEventArgs e)
  10:         {
  11: if ((e.Row is GridViewHeaderRow) || (e.Row is GridViewFooterRow) || (e.Row is GridViewNewRow))
  12: return; //we want to apply logic to data rows only
  13:  
  14:             Binding colorBinding  = new Binding("Price"){Converter = new PriceToColorConverter()};
  15:             e.Row.Cells[1].SetBinding(GridViewCell.ForegroundProperty, colorBinding);
  16: //e.Row.Cells[1].SetBinding(GridViewCell.BackgroundProperty, colorBinding);
  17:         }
  18:     }

And of course  a

a small downloadable demo project

   using the formatting behavior.

 

We often argue with our front-end developer about which way is better. For him some hundred lines of XAML is nothing to worry about and of course it is the natural Silverlight way of getting the job done. For me it is a silent panic. Now our mission is to give you the choice. You have both in your toolbox now. Drop me a line when you make the decision :) .


Comments

Comments are disabled in preview mode.