Using a value converter to bind to Visibility in the Silverlight data grid

I received a question a few weeks ago from a customer who was trying to show and hide a button inside a data grid row.  This developer had a boolean property exposed inside the data model that indicated whether an item was in stock; at that time, a “Buy” button should appear.

By using a type converter and data binding, we can make this scenario work pretty easily.  Here’s the finished appearance that we’re going for:

The solution I recommended had these components:

  • Create a value converter for bool and System.Windows.Visibility.  (IValueConverter)
  • Add the converter into the user control’s resources.
  • Use a DataGridTemplateColumn to define the optional button, and data bind the Visibility property of the button to the boolean data source property, specifying the converter.

Defining the data

I’m sticking to a really simple data definition for binding.

using System;

/// <summary>
/// My awesome data model.
/// </summary>
public class MyData
{
    /// <summary>
    /// The item name.
    /// </summary>
    public string Name
    {
        get;
        set;
    }

    /// <summary>
    /// Whether the item can be purchased right now.
    /// </summary>
    public bool IsBuyable
    {
        get;
        set;
    }
}

Creating the value converter

Next, we need to create a converter to go between bool values and Visibility.  Here’s that:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

/// <summary>
/// A type converter for visibility and boolean values.
/// </summary>
public class VisibilityConverter : IValueConverter
{
    public object Convert(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        bool visibility = (bool)value;
        return visibility ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        Visibility visibility = (Visibility)value;
        return (visibility == Visibility.Visible);
    }
}

Naming the converter in the UserControl’s resources

Next, add the converter in the UserControl’s resources.  In my simple project, I had to create this section – but hopefully you’re already using this in your real apps! 

You may also need to add an xmlns attribute in page.xaml for your project’s namespace.

    <UserControl.Resources>
        <myproject:VisibilityConverter x:Key="VisibilityConverter" />
    </UserControl.Resources>

The “x:Key” attribute defines the name that you’ll use inside your binding statements to make use of the converter.

Using DataGridTemplateColumn

The DataGridTemplateColumn lets you create advanced cells. 

If you’re looking for juicy details about the DataGrid in detail, head on over to the tutorials that Scott Morrison has created.

Make sure to set the AutoGenerateColumns property of the DataGrid to false, so that your template column is picked up.

Here’s the DataGrid element inside my page’s Grid:

<my:DataGrid x:Name="SampleGrid" AutoGenerateColumns="False">
    <my:DataGrid.Columns>
        <my:DataGridTextColumn Header="Product Name"
                               DisplayMemberBinding="{Binding Name}" />
        <my:DataGridTemplateColumn Header="Make a purchase">
            <my:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Add to cart"
                            Visibility="{Binding IsBuyable, Converter={StaticResource VisibilityConverter}}"
                        FontFamily="Trebuchet MS" FontSize="9" Margin="4,3,4,3"/>
                </DataTemplate>
            </my:DataGridTemplateColumn.CellTemplate>
        </my:DataGridTemplateColumn>
    </my:DataGrid.Columns>
</my:DataGrid>

And finally, here’s the sample data I used:

List<MyData> sampleData = new List<MyData>
{
    new MyData
    {
        Name = "Apple iPhone 3G",
        IsBuyable = true
    },
    new MyData
    {
        Name = "Microsoft Silverlight 2",
        IsBuyable = false,
    },
    new MyData
    {
        Name = "Microsoft .NET Framework 3.5"
    },
    new MyData
    {
        Name = "Microsoft Visual Studio 2008",
        IsBuyable = true
    },
    new MyData
    {
        Name = "Microsoft Windows Vista Ultimate",
        IsBuyable = true
    }
};

SampleGrid.ItemsSource = sampleData;

And here’s our finished product:

Hope this helps.

Comments

  1. August 19th, 2008 | 2:38 pm

    Very interesting, but how do you attach event handlers to the Buttons and how you identify which Button was Clicked ?

    Thanks,

    Herman

  2. August 19th, 2008 | 5:18 pm

    Herman,
    Yeah, I wasn’t actually jumping in to give a very complete story here. Let me spend some time finding a more complete solution and post about that soon.

    -Jeff

  3. Luiz Mendes
    November 4th, 2008 | 4:52 am

    How do you it if the button is create in run time?

  4. November 4th, 2008 | 5:03 pm

    Great post – just what I was looking to do. I am brand new to WPF and Silverlight, been an ASP.NET programmer for a long time. Your post was easy to follow and I was able to get my grid working in no time. Thanks!!

  5. November 24th, 2008 | 12:48 pm

    [...] that convert between two different types of values (I know, shocking).  A classic example is converting between a boolean and the Visibility enum.  We’re going to apply the same concept but this time convert between a type (our business [...]

  6. Michael Schall
    December 3rd, 2008 | 9:00 am

    You add a button handler the same way you would for a button outside the grid. And you can get the object that was bound to the row of the grid from the button as it will be the DataContext of the button.