in

ASP.NET Weblogs

Joe Wrobel

January 2009 - Posts

  • 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

More Posts