I’ve been intending to write a sample using the ComboBox for a while. The more familiar you get with Silverlight, the more you realize the ComboBox causes all sorts of problems. I have mixed feelings about the proliferation of posts I’ve seen on this topic; on one hand, it’s great to see everyone getting involved; on the other, it points to serious problems with basic scenarios.

The Challenges

I’ve seen a few good posts out there using the DomainDataSource with a ComboBox that make things look easy. Since those were already well trodden paths I thought it would be important to identify some of the harder scenarios. After a bit of investigation, three things came forward as being particularly difficult.

  • Loading ComboBox contents asynchronously
  • Implementing cascading ComboBoxes
  • Using ComboBoxes for Entity associations

 

The Sample

I’ve included a sample along with the post to get you started. It includes a small library of extensions for you to reuse and shows how you can use them. This extension namespace will pop up throughout the rest of the post.

  xmlns:ex="clr-namespace:ComboBoxExtensions;assembly=ComboBoxExtensions"

I wanted to put together a simple declarative syntax for using ComboBoxes. I figured the task wasn’t that complex so the code shouldn’t be either. It seemed like most tasks should be doable with a data source, a ComboBox, and a few simple bindings.

  <ex:ComboBoxDataSource Name="ASource"
                         DomainContext="{StaticResource SampleDomainContext
}"
                         OperationName="GetAValues" />
  .
..
  <ComboBox Name="AComboBox"
           
ItemsSource="{Binding Data, ElementName=ASource}"
           
SelectedItem="{Binding A, Mode=TwoWay}"
           
ex:ComboBox.Mode="Async" />

Here are a few things to notice when you crack open the sample.

  • AComboBox uses a simple binding to a static list
  • BComboBox binds to a list that is dynamically changing based on what is selected in A
  • CComboBox is bound to a dynamic list of Entities that changes based what is selected in B and is used to modify associations
  • I use a partial class and shared validation to fully enforce the relationships defined in the ComboBoxes

As I wrote this sample I experienced plenty of missteps and restarts. Along the way, I came to a few conclusions.

1) Do not use the DomainDataSource to populate ComboBoxes

You might think this is drastic or an over reaction, but I stick by the recommendation. Despite the simple samples you’ll see in other places, I think you’re better off avoiding the DDS when working with ComboBoxes. The DDS does not scale for more complex ComboBox scenarios. You’re better off having your own data source that you can use throughout your application. If you don’t want to write one yourself, I’ve included a simple data source in the sample.

  <ex:ComboBoxDataSource Name="ASource"
DomainContext="{StaticResource SampleDomainContext}"
OperationName="GetAValues" />

[In the spirit of full disclosure, I’m the primary developer for the DomainDataSource control going forward. Integration with ComboBoxes is one of the features I’m investigating.]

2) Share the DomainContext

If you want to associate Entities, they need to be in the same DomainContext. To accomplish this, the data source that loads entities for associations must not have its own instance of a DomainContext. Instead, it should be using the same DomainContext used to load the primary entities. I found it useful to create the DomainContext as a StaticResource in xaml and use it throughout.

  <UserControl.Resources>
    <web:SampleDomainContext x:Key="SampleDomainContext" />
  </UserControl.Resources>
...
<ex:ComboBoxDataSource Name="ASource"
DomainContext="{StaticResource SampleDomainContext}"
OperationName="GetAValues" />

3) Using the ComboBox asynchronously is hard

The ComboBox did not help me at all as I tried to asynchronously load data and constantly behaved in ways I did not anticipate. To address these limitations and quirks the extension library adds two modes to make asynchronous data loading a breeze whether you’re declaring the source or using a view model.

  • Async – The SelectedItem and ItemsSource can be updated in any order. If the SelectedItem is set first, the ComboBox may appear blank until the items are loaded.
  • AsyncEager – Just like the Async mode, the SelectedItem and ItemsSource can be updated in any order. Unlike the first mode, this one eagerly selects anything you tell it to. The next time the list is loaded it confirms the selection is valid.

  <ComboBox Name="AComboBox"
           
ItemsSource="{Binding Data, ElementName=ASource}"
           
SelectedItem="{Binding A, Mode=TwoWay}"
           
ex:ComboBox.Mode="AsyncEager" />

I found the user experience with AsyncEager to be nicer. However, there are some edge cases with SelectedValue where the behavior of Async seemed to be a better choice.

In Conclusion

I hope the next version of Silverlight (v5) addresses some of these concerns. It’d be great if the control were designed for asynchronous scenarios. Also, I’d love to hear what you think about data sources for the ComboBox. Would the ComboBoxDataSource included in the sample be sufficient if we shipped it in the toolkit? Are there DomainDataSource features you think are critical for this scenario that are not included in the ComboBoxDataSource? Would you prefer if we supported all ComboBox scenarios with just the DomainDataSource?

Hope the sample helps and thanks in advance for the feedback.

Frequently Asked Questions

There have been a number of questions asked throughout the comments on this post. To make things easy for you, I’ve aggregated the questions and provided a little more detail in my answers in this new post.

Silverlight ComboBox Frequently Asked Questions

Now on NuGet

Colin did me the favor of putting the Extension library up on NuGet (as part of RiaServiceContrib). You can find it under RiaServicesContrib.ComboBoxExtensions.