Silverlight 3 Custom User Control Binding – Binding a ComboBox

So it’s late on the night before Halloween and I figured out something I wanted to share. Even with postings on forums, I did not get an answer to my problem. What problem you ask? Funny you should ask, and fortunate for you that you stumbled onto this article. Or better yet, maybe you can’t sleep either because you too have a user control you are creating in Silverlight and it wont bind correctly! Damn this thing!

The problem

I am creating a user control that contains two textblocks and a combobox. One textblock is a title, and the other a footer. The combobox sits between them. So all I wanted to do was to databind my collection to the combobox.

I have never before created a custom user control in Silverlight which is why I had to create one in the first place. I hate not having done something, don’t you? I created the xaml in blend (what a pain that was, I still just don’t get that program apparently). Then I created my code-behind for the control.

I created properties for TitleText, FooterText and ItemsSource (for the combobox). The TitleText and FooterText properties just get and set the text of the corresponding textblock controls like this:

public string FooterText
{
    get { return _lblFooter.Text; }
    set { _lblFooter.Text = value; }
}

Then I did the same for the binding of the combobox like this:

public IEnumerable ItemsSource
       {
           get { return _cboData.ItemsSource; }
           set { _cboData.ItemsSource = value; }
       }

 

After building the control, I put it in a hosting page with binding directives like so:

<sydControls:CascadingPicker x:Name=”YearPicker” Margin=”8,241,513,94″ TitleText=”Select Year” FooterText=”Hover To Change Year” ItemsSource=”{Binding Source={StaticResource YearDataSource}}” ItemTemplate=”{StaticResource YearsComboItem}” />

Looks like it should work right? WRONG!

What did I miss?

After a couple days of pulling out what little hair I had left I found something called DependencyProperty. It was the answer to my problems, except I didn’t get it at first. Apparently, you are not supposed to create properties in Silverlight controls the way I did above. The properties need to be injected (heh heh he said injected) into the Silverlight context for the page rendered. It has to do with the way things are bound and allows things like the DataContext to interact with your controls properties.

the Solution

The solution is as follows. Remove the ItemsSource property and replace it with this:

public IEnumerable ItemsSource
{
    get { return (IEnumerable)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

Wait a sec?!? If my setter calls SetValue, then where can I put my code to pass the incomming value to my combobox.ItemsSource?? The answer is in the DependencyProperty, you also need to do this:

public static readonly DependencyProperty ItemsSourceProperty =
    DependencyProperty.Register(“ItemsSource”, typeof(IEnumerable), typeof(CascadingPicker),
new PropertyMetadata( new PropertyChangedCallback(CascadingPicker.OnItemSourcecPropertyChanged)));

WOW! What crap huh? Yup, what this is, is a DependencyProperty.Register() method call. You give it the name of the property it is going to depend on, and next you tell it what datatype is used in the property (IEnumerable in this property) then you give it the control’s datatype (mine is called CascadingPicker), then you give it a callback to a method to call when it receives a propertychanged event. I am using the Entity Framework for my object, so it has that plumbing built-in. If you are rolling your own POCO then just implement the interface INotifyPropertyChanged and call NotifyPropertyChanged(“<PropertyName>”) on your setters. You see this static thing gets the actual instance of your control passed in along with the value being set, so in your callback method you could just do something like this:

private static void OnItemSourcecPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var cascadingPicker = d as CascadingPicker;

    if (cascadingPicker != null)
    {
        cascadingPicker._cboData.ItemsSource = e.NewValue as DataTemplate;
    }

}

You take the passed in control and then you can use it to access your properties and fields to assign your values.

Another way to do it, and the way I chose to implement it was to use lambda expressions instead of a callback. You could easily combine the above two calls into one like this:

public static readonly DependencyProperty ItemsSourceProperty =
          DependencyProperty.Register(“ItemsSource”, typeof(IEnumerable), typeof(CascadingPicker),
new PropertyMetadata((o, e) =>
                                      {
                                          var cascadingPicker = o as CascadingPicker;

                                          if (cascadingPicker != null)
                                          {
                                              cascadingPicker._cboData.ItemsSource = e.NewValue as IEnumerable;
                                          }
                                      }));

This is the same call as before, but I added the lambda (o,e) => {} instead of the callback method, it just does it inline and is a bit less code.

So that was it, the xaml code remained the same, it was just that I didn’t understand the way I was supposed to create properties in Silverlight controls. Now I know and now I can create re-useable data-bindable Silverlight controls.

Ok here is your test, close your eyes and tell me the syntax of a DependencyProperty. Yeah Right! Thanks for keeping it simple Microsoft…. well at least it works!

 

-Savij

2 Responses to “Silverlight 3 Custom User Control Binding – Binding a ComboBox”

  1. Charles Says:

    Wow– that was really complex, nice that you got that… What exactly is “ItemSourceProperty”? Where was that declared? Is it a built in type?

    • savij Says:

      Nope, look closer It’s a declared variable of type DependencyProperty:

      public static readonly DependencyProperty ItemsSourceProperty = …

      It acts like a backing variable (field) for the property. When the property setter gets called, the SetValue() method in the setter calls the DependencyProperty’s Callback and that runs to allow you to do whatever you want with the value when the setter is called.

Leave a Reply