Thursday, October 30, 2008

Lookup Combobox in Silverlight 2

I ran into this problem while building a proof of concept to introduce SilverLight 2. I needed a datagrid, with several columns where the values of these columns are determined by a single combobox. Putting the combobox in place was easy enough, but getting the other columns to react instantly got me googling for quite a bit (altough in the end the solution is obvious).

As an example for this post, we will use a car database (cause I love cars). A car has a brand, a type and an engine. An engine consists of a type, a fuel and a number of cillinders. I've placed a datagrid in my page.xaml and configured it a bit. It then looks like this:


The Engine column contains a combobox that appears whenever you edit a row. The Fuel and Cillinders columns are actually databound against the engine class, like this:


<?xml:namespace prefix = data /><data:datagridtemplatecolumn header="Engine">
<data:datagridtemplatecolumn.celltemplate>
<datatemplate>
<textblock margin="4" text="{Binding Engine.TypeCode}">
</datatemplate>
</data:datagridtemplatecolumn.celltemplate>
<data:datagridtemplatecolumn.celleditingtemplate>
<datatemplate>
<?xml:namespace prefix = cardemo /><cardemo:fixedcombobox selecteditem="{Binding Engine, Mode=TwoWay}" itemssource="{StaticResource Engines}">
<combobox.itemtemplate>
<datatemplate>
<stackpanel orientation="Horizontal">
<textblock width="40" margin="4" text="{Binding TypeCode}">
<textblock width="40" margin="4" text="{Binding Fuel}">
<textblock width="20" margin="4" text="{Binding NumberOfCillinders}">
</stackpanel>
</datatemplate>
</combobox.itemtemplate>
</cardemo:fixedcombobox>
</datatemplate>
</data:datagridtemplatecolumn.celleditingtemplate>
</data:datagridtemplatecolumn>
<data:datagridtextcolumn header="Fuel" isreadonly="True" binding="{Binding Engine.Fuel}">
<data:datagridtextcolumn header="Cillinders" isreadonly="True" binding="{Binding Engine.NumberOfCillinders}">
</data:datagridtextcolumn></data:datagridtextcolumn>
<data:datagridtextcolumn header="Fuel" isreadonly="True" binding="{Binding Engine.Fuel}"><data:datagridtextcolumn header="Cillinders" isreadonly="True" binding="{Binding Engine.NumberOfCillinders}"></data:datagridtextcolumn></data:datagridtextcolumn>

But like this it won't update the read-only columns for fuel and cillinders whenever I choose a different engine. To do this, we need to implement the INotifyPropertyChanged interface on the car class (in fact I would advise implementing it on all your databound classes):

public class Car : INotifyPropertyChanged
{
private Engine _engine;
private string _brand;
private string _type;

public Engine Engine
{
get
{
return _engine;
}
set
{
_engine = value;
RaisePropertyChanged("Engine");
}
}

public string Brand
{
get
{
return _brand;
}
set
{
_brand = value;
RaisePropertyChanged("Brand");
}
}

public string Type
{
get
{
return _type;
}
set
{
_type = value;
RaisePropertyChanged("Type");
}
}
#region INotifyPropertyChanged Members

private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

public event PropertyChangedEventHandler PropertyChanged;

#endregion

Now, the datagrid will subscribe to the PropertyChanged Event of your class as it binds to it. Then, whenever you choose a different item in the combobox, this changes the engine property and triggers the event. This in turn triggers the datagrid in updating the other columns.

Update: As of the December '08 release of the datagrid, a fix on the combobox is no longer needed.

No comments:

Post a Comment