Silverlight ComboBox Frequently Asked Questions

I’ve wanted to do a follow up to my original ComboBox post for a while now. I answered a number of the questions throughout the comments, but I thought I’d gather all the answers up and add some nice code samples. Hope this helps.

How do I use a ComboBox in a DataForm?

For most of the scenarios where you’ll be using a ComboBox in a DataForm a static data source will be sufficient. In our case, let’s assume we’re pulling a list of options from the server. We’ll add a method to our DomainService to get these items.

   public IEnumerable<string> GetCountryStrings()
  {
    return new[] { "UK", "USA" };
  }

Our next step is to create a data source to call the method. We can add the following snippet to our Silverlight page.

   <navigation:Page.Resources>
    <web:EmployeeDomainContext x:Key="employeeDomainContext"/>
    <ex:ComboBoxDataSource x:Key="countryDataSource"
      DomainContext="{StaticResource employeeDomainContext}"
      OperationName="GetCountryStrings" />
  </navigation:Page.Resources>

It’s worth noting that we’ve made both the DomainContext and ComboBoxDataSource resources. This will allow us to share the DomainContext with the data source we use to load our data. Specifically, when we add a DomainDataSource that loads the entities, we’ll make sure that is uses the DomainContext declared in the resources.

   <dds:DomainDataSource AutoLoad="True"
    Name="employeeDomainDataSource"
    DomainContext="{StaticResource employeeDomainContext}"
    QueryName="GetEmployeesQuery" />

The final step in using a ComboBox within a DataForm is to hook everything together within a DataField.

   <toolkit:DataForm AutoGenerateFields="False"
    ItemsSource="{Binding ElementName=employeeDomainDataSource, Path=Data}"
    Name="employeeDataForm">
    <StackPanel>
      <toolkit:DataField Label="Title">
        <TextBox Text="{Binding Path=Title, Mode=TwoWay}" />
      </toolkit:DataField>
      <toolkit:DataField Label="Country">
        <ComboBox 
          ItemsSource=
            "{Binding Data, Source={StaticResource countryDataSource}}"
          SelectedItem="{Binding Country, Mode=TwoWay}"
          ex:ComboBox.Mode="AsyncEager" />                            
      </toolkit:DataField>
    </StackPanel>                    
  </toolkit:DataForm>

In this case, we’ve bound the ItemsSource to our data source, two-way bound the SelectedItem to a property on the entity, and set the ComboBox mode to AsyncEager.

It’s also worth noting we’ve set the DataField label to “Country”. If we were using the Display or Required attributes, we could set PropertyPath instead to ensure the metadata was applied to the DataField correctly.

Another point to highlight is I’m not using ElementName binding. It’s rarely explained, but ElementName binding has some limitations when used in templates (there are good reasons for this, but I won’t go into them here). It’s best to consider templates as a barrier for ElementName binding. Controls inside the template can bind to other controls inside the template. Controls outside the template can bind to other controls outside the template. However, controls inside the template cannot use ElementName binding to bind to controls outside the template (and vice-versa). This is another reason we chose to make the data source a resource above.

How do I use a ComboBox in a DataGrid?

Using a ComboBox in a DataGrid follows nearly the same pattern as above. First, define an operation on your DomainService to return the items . Second, define a data source to load the items; sharing a DomainContext as necessary. Finally, hook everything together within a DataGridTemplateColumn.

   <sdk:DataGrid AutoGenerateColumns="False"
    ItemsSource="{Binding ElementName=employeeDomainDataSource, Path=Data}"
    Name="employeeDataGrid">
    <sdk:DataGrid.Columns>
      <sdk:DataGridTextColumn x:Name="titleColumn"
        Binding="{Binding Path=Title}" Header="Title" />
      <sdk:DataGridTemplateColumn x:Name="countryColumn" Header="Country">
        <sdk:DataGridTemplateColumn.CellEditingTemplate>
          <DataTemplate>
            <ComboBox
              ItemsSource=
                "{Binding Data, Source={StaticResource countryDataSource}}"
              SelectedItem="{Binding Country, Mode=TwoWay}"
              ex:ComboBox.Mode="AsyncEager" />
          </DataTemplate>
        </sdk:DataGridTemplateColumn.CellEditingTemplate>
        <sdk:DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding Path=Country}"/>
          </DataTemplate>
        </sdk:DataGridTemplateColumn.CellTemplate>
      </sdk:DataGridTemplateColumn>
    </sdk:DataGrid.Columns>
  </sdk:DataGrid>

As above, we’ve bound the ItemsSource to our data source, two-way bound the SelectedItem to a property on the entity, and set the ComboBox mode to AsyncEager.

Additionally, you might notice we’ve created two templates. We need to set up the CellEditingTemplate to use a ComboBox, but the CellTemplate used for reading the value can have anything in it. In this case it was easiest to display the value using a TextBlock.

What are the modes Async and AsyncEager and am I using them correctly?

There have been a number of questions along the lines of “I only see the selected item in my pick list. Why aren’t the items loading correctly?” In nearly every case, the inquirer is using the AsyncEager mode and it is masking the underlying failure (which tends to be the SelectedItem isn’t available in the ItemsSource) . I figured I’d take a second to explain the modes.

Async mode makes sure the ComboBox’s SelectedItem will be reselected any time the ItemsSource changes. By default the control will stop binding any time the SelectedItem is not in the Items. This can be inconvenient in scenarios where you’re loading the ItemsSource asynchronously as the burden of correctly ordering the load completion and then establishing the binding always falls on you. With the Async mode, the ItemsSource can be loaded before or after the binding is created and the ComboBox will work fine regardless of the order. For this mode, the ComboBox will remain blank with an empty pick list until the ItemsSource is loaded.

AsyncEager mode goes one step farther then Async mode to improve the UI experience by addressing the selection ‘flicker’ that can occur when the ItemsSource is loaded asynchronously. If the SelectedItem is not in the ItemsSource, the ItemsSource will be replaced with a single-item list containing only the SelectedItem. The assumption is that in the near future, the ItemsSource will be updated with a list that does contain the SelectedItem. In this mode, the ComboBox will have a single-item pick list containing only the SelectedItem until the ItemsSource is loaded.

In both modes, it is important to note that equality is often based on referential comparison. Entities loaded in different DomainContext instances are not referentially equal. If you load the ItemsSource in one context and the SelectedItem in another, the entity will not be selected correctly in the ComboBox. For this reason (among others) it is important to share the DomainContext when loading the ItemsSource (see my last post for an example).