in

ASP.NET Weblogs

Joe Wrobel

Conditional Formatting in the Silverlight DataGrid

I’ve been an asp.net developer for some time now and I was excited to jump on to Silverlight when it 2.0
was released a few months ago.  One thing I really struggled with was applying conditional formatting to
the individual cells in the DataGrid control.  Coming from an asp.net background, I carried a lot of
assumptions with me (big mistake).  I thought I could get a hold of the rows or cells collection and have
my way with it, but no such luck.  I stumbled down several paths which all ultimately lead to dead ends. 
After killing hours (maybe days) on trying to figure this out, I had to let it go and move on.  Now a month
later I decided to give it another shot and I finally got it.  The answer was right in front of me all along. 
I knew about the IValueConverter interface, but I didn’t fully understand its capabilities.  I thought it was
only used for converting an object into a text representation or vice versa.  Actually, you can return
anything you want from it.  So you can return a Button, Grid, or whatever.

Another aspect I couldn’t figure out was how to get access to page members from within the Convert
method.  For example, I wanted to render a button in the cell and wire up the button’s click event to a
method in the page.  Sure, you could do this to a certain extent using templates, but then I couldn’t
find a way to change the template conditionally based on a value in the bound data item.

The solution I came up with was to create a delegate along with a class which implemented the
IValueConverter interface and exposed two events.  One for converting and the other for converting
back.  I can then declare this converter in the resources collection and setup a handler in the page
as shown below.

<UserControl.Resources>
<local:UniversalConverter x:Key="ageConverter" Converting="ConvertAge" />
<local:UniversalConverter x:Key="nameConverter" Converting="ConvertName" />
</UserControl.Resources>

 

Here is the markup i used in the “First Name” column of the DataGrid.

<data:DataGridTemplateColumn Header="First Name">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Converter={StaticResource nameConverter}}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>

 

Here is the “ConvertName” method I wired up from the converter defined in the resources collection.

   1:  private object ConvertName(object value, Type targetType, 
object parameter, CultureInfo culture) {
   2:      Employee employee = value as Employee;
   3:      if (employee == null) {
   4:          return value;
   5:      }
   6:   
   7:      if (employee.FirstName.Contains('a')) {
   8:          Button btn = new Button();
   9:          btn.Content = employee.FirstName;
  10:          btn.Click += ((sender, e) => {
  11:            HtmlPage.Window.Alert(
  12:              string.Format(
"There is a button here because \"{0}\" contains an \"a\".",
  13:              employee.FirstName));
  14:          });
  15:   
  16:          return btn;
  17:      }
  18:      return new TextBlock {Text = employee.FirstName};
  19:  }

 

This is the IConverter class and delegate I created to handle the conversions.

   1:  public class UniversalConverter : IValueConverter {
   2:   
   3:      public object Convert(object value, Type targetType, 
object parameter, CultureInfo culture) {
   4:          return this.OnConverting(value, targetType, 
parameter, culture);
   5:      }
   6:   
   7:      public object ConvertBack(object value, Type targetType, 
object parameter, CultureInfo culture) {
   8:          return this.OnConvertingBack(value, targetType, 
parameter, culture);
   9:      }
  10:   
  11:      protected object OnConverting(object value, Type targetType, 
object parameter, CultureInfo culture) {
  12:          UniversalConverterHandler handler = this.Converting;
  13:          if (handler != null) {
  14:              return handler(value, targetType, parameter, culture);
  15:          }
  16:          return value;
  17:      }
  18:   
  19:      protected object OnConvertingBack(object value, Type targetType, 
object parameter, CultureInfo culture) {
  20:          UniversalConverterHandler handler = this.ConvertingBack;
  21:          if (handler != null) {
  22:              return handler(value, targetType, parameter, culture);
  23:          }
  24:          return value;
  25:      }
  26:   
  27:      public event UniversalConverterHandler Converting;
  28:   
  29:      public event UniversalConverterHandler ConvertingBack;
  30:   
  31:  }
  32:   
  33:  public delegate object UniversalConverterHandler(object value, Type targetType, 
object parameter, CultureInfo culture);

 

And finally, here is a screenshot of the DataGrid. (Ugly, I know, but it proves my point.)
image

 

Here is a link to the complete solution.

I hope you find this useful and I look forward to hearing feedback and suggestions. 
Most importantly, let me know if you know of a better way to do this.

Thanks
-Joe

Published Jan 25 2009, 11:57 PM by robolize
Filed under: ,

Comments

 

DotNetShoutout said:

Thank you for submitting this cool story - Trackback from DotNetShoutout

January 26, 2009 3:46 AM
 

Benjamin Haag said:

Timely and significantly helpful...thanks!

February 10, 2009 10:03 AM
 

Aeon said:

Very useful implementation of the cumbersome IValueConverter.

May 28, 2009 12:43 PM
 

Yapari said:

Your ConvertAge overrides the grid selection and mousemove backcolor behavior.

Do you have a way to use both conditional formatting and grid selection behavior?

July 9, 2009 10:25 AM
 

gStorm said:

This conditional formatting blog was a lifesaver as I needed to conditionally turn an ID column into a button.  I'm having trouble with it though.  

Let's say I click on a button in row 2, the button handling works fine as you've outlined.  But then when I page the grid, row 2 is invisible.  I can repopulate the grid, and still row 2 is invisible.  I have to reload the SL app for the grid to render row 2 again.  Then it pages correctly until I click another button.

I'm manually handling paging by only displaying "1 page of data" at a time.  Paging works fine until I click a button in the datagrid.  Any thoughts?

July 31, 2009 10:36 AM
 

RaWi said:

Hi,

while your example application works at my computer, I tried to implement the UniversalConverter in my own project.

In XAML I defined:        

<local:UniversalConverter x:Key="MyDateConverter" Converting="RaWiConvertDate" />

But when I try to compile, I get the Error No overload for 'RaWIConvertDate' matches delgate 'UniversalConverterHandler

The same error occurs if I try to add the ConvertName and ConvertAge methods.

If I use the "standard" event handler signature

object RaWiConvertDate(object sender, EventArgs e) it compiles fine, but I get an exception in InitializeComponent() then.

Has anybody an idea what I'am doing wrong. Its frustrading to have a running sample and don't get it to work in my projekt :-(

Thanks

Ralf

September 25, 2009 4:50 AM
 

Andrew said:

Great idea Joe.

I just wanted the stying capability and importantly had to use a descendant of datagridboundcolumn.

So I have my own class that extends boundcolumn  and then in generateElement I add:

cell.SetBinding(DataGridCell.StyleProperty, sb);

where sb is my binding that uses the universalconverter. I add my event handler at runtime based on the viewmodel associated with the column.

Thanks very much for this inspiration.

October 7, 2009 2:26 PM
 

Eric Scherrer said:

Excellent - thanks.

November 2, 2009 11:55 PM
 

Eric Scherrer said:

I'm having the same problem as Ralf above. Using SL3. Covnerted your project to SL3, changed the data reference and it works fine. But when I implement in my project I get the same error as Ralf. I've torn this thing apart and cant seem to find the problem.

November 3, 2009 4:03 PM
 

rrd_ladysalsa said:

Any luck with the

No overload for 'RaWIConvertDate' matches delgate 'UniversalConverterHandler

error? I'm having the same issue in my SL3 project, but not the converted sample project.

November 6, 2009 7:48 PM

Leave a Comment

(required)  
(optional)
(required)  
Add