Monday, December 1, 2008

Silverlight 2 Datagrids RowDetailsTemplate and databinding radiobuttons

Today something I've been testing with for future use. I've been building a small prove of concept for using the datagrid to edit data, not directly in the grid, but in a details form. It looks like this:


So you can click a row and the details form is displayed below the row. You can then edit the data and it is immediately shown in the grid.

In this article I'll assume you have some knowledge on databinding and the datagrid yourself. Also having some experience with Silverlight, XAML, Blend and Visual Studio would come in handy.

So how does this work? This uses the RowDetailsTemplate property of the datagrid.

The RowDetailsTemplate in Blend
I started building this in Blend and ended up in Visual Studio anyway. Blend does provide support for editing this template, trough the menu Object < Edit Other Templates < Edit RowDetailsTemplate. You can build up a template here, but it's visual designer is not really user friendly.

Then comes the databinding. The datagrid is easy. Just go to the Project panel and under data add a CLR object, in this case a collection and drag it onto the grid. You can then turn off AutoGenerateColumns and define your own columns trough the Columns collection.

For the RowDetailsTemplate this doesn't work that polished. You can go to a control, let's say a textbox, and databind to it's properties, in this case the text property. A nice Create Data Binding dialog comes up and I taught "ok, just bind to the collection and we're done". But clicking on the collections datasource created earlier only shows the count property. So I figured I'd add a datasource for the detailed class and bind to the property there. The dialog allows you to do this, but as soon as you close it errors start popping up telling you that a TextBox can't be converted into a DependencyObject (don't ask me why not) and the datasource you just created can't be resolved.

Checking the XAML shows that Blend did exactly what I asked, binding to the datasource I created, with the path I clicked on. But this is not what I want. I just want to bind to the actual datacontext, so at this point I fired up Visual Studio to edit this mess myself (as Blend doesn't support any IntelliSense, I prefer Visual Studio as a XAML editor). I simply changed the binding to {Binding propertyname} and it works like a charm.

So next up, I wanted to databind two RadioButtons to one field.

Databinding radiobuttons
In the process of building a small form to show how this could work in future applications, I wanted to bind a field (in this case a persons Gender field) to two radiobuttons (obviously Male and Female). I googled around a bit and found out that this is not as straight forward as one might think. Some people actually wrote a considerable amount of code to get it to work.
I pondered on this a bit and decided that I would come up with an approach using ValueConverters. You can build custom ValueConverters simply by creating a class that implements the IValueConverter interface. From there on it's actually not that hard.

I came up with using two value converters, one for the male and one for the female. Here is what the Female one looks like:

public class GenderFemaleConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool result = false;
Gender actualValue = (Gender)value;
result = actualValue == Gender.Female;
return result;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool actualValue = (bool)value;
if (actualValue)
{
return Gender.Female;
}
else
{
return null;
}
}

#endregion
}


For the male version of this, all I changed where the Gender.Female references to Gender.Male. Then all you have to do is use these in your databinding. To do so, first you'll need to create resources for them at some point. In this case I put the resources in the UserControl. The XAML looks like this:

<UserControl.Resources>
<DataClasses:PersoonCollection x:Key="PersonCollectionDS" d:IsDataSource="True"/>
<local:GenderFemaleConverter x:Key="GenderFemaleConverter" />
<local:GenderMaleConverter x:Key="GenderMaleConverter" />


Next step is to include these resources as converters in your databinding:


<RadioButton Grid.Row="1" Grid.Column="3" Content="Male" IsChecked="{Binding Gender, Converter={StaticResource GenderMaleConverter}}"></RadioButton>
<RadioButton Grid.Row="1" Grid.Column="4" Content="Female" IsChecked="{Binding Gender, Converter={StaticResource GenderFemaleConverter}}"></RadioButton>


As you can see, I bind both RadioButtons to the same field, but I use the two different converters.
One could argue that this is not the most efficient way of doing things and I would agree. A better approach may be to build some sort of RadioGroup control that has a single converter and databinding happens to a property of that. For this prove of concept however, it does what is has to do.

I hope you enjoyed this article and found it helpful. Please let me know if there are any questions and also suggestions for subjects are still very welcome, although I might not be able to go into every single thing you throw at me.

2 comments:

  1. I'd sure like to see how you've set up your Gender class. Do you think you could post that or possibly the source code? Thanks for the great insight!

    ReplyDelete
  2. Hi, vinneyk. Thanks for your comment.
    In the example, the Gender type is actualy an enum:

    public enum Gender
    {
    Male,
    Female
    }

    I don't see reasons to write a class for Gender, as it wouldn't actualy do anything.

    Greets,
    Jonathan

    ReplyDelete