15 November 2011

Binding UserControl properties in Silverlight

It seems to me that the logical thing to do in Silverlight when building some sort of composite control is to create a UserControl, expose some properties, add some controls to the user control, and bind them to those properties.

For example, a custom CoffeeMug control might want to expose MugColor and PercentFull properties. However, it's taken me quite a while to figure out how to get this to work properly.

The problem is in selecting the binding source. WCF offers the FindAncestors binding helper, which can be used to locate the user control relative to the control being bound, but Silverlight offers no counterpart.

So initially I had been using ElementName:

<UserControl x:Name="thisControl" ... >
...
<Rectangle x:Name="mugShape"
Background="{Binding MugColor, ElementName=thisControl}" />
...
</UserControl>

This works pretty well in simple circumstances, but it wasn't working for me in DataTemplate scenarios, such as in the DataGrid. I was having similar problems with using custom controls in custom ComboBox and ListBox data templates.

Well, apparently its a known shortcoming of Silverlight. When the data template is asked to make a concrete instance, it no longer carries the necessary context information to resolve element names directly. It can however be overcome by manually specifying the binding in code.


public CoffeeMug()
(
this.Loaded += new new RoutedEventHandler(Control_Loaded);
}
void Control_Loaded(object sender, RoutedEventArgs e)
{
Binding binding = new Binding("MugColor");
binding.Source = this;
// remember to set binding.Mode = TwoWay if applicable
this.mugShape.SetBinding(Shape.BackgroundProperty, binding);
}

This guy has also hacked together a behavior to make everything much nicer in an MVVM environment as well, but I don't think I'd go to that much effort personally.

No comments: