Freigeben über


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).

Comments

  • Anonymous
    November 02, 2010
    Can we please have a sample using something like AdventureWorks so we can follow this exactly .. and thank you for your efforts.

  • Anonymous
    November 02, 2010
    I'll have one soon based on Northwind and I'll add a link when it's posted.

  • Anonymous
    November 02, 2010
    As useful as this sample is, it doesn't address the scenario in which the combobox list is provided by another datasource. Some months ago I finally understood why ElementName binding doesn't work unless you define the datasource inside the template (which is hideously inefficient): a template instance does not share namescope with its container because if it did, you would get name collisions using it in a list. So namescope is of necessity broken, and we must resort to declaring the combobox list datasource as a static resource, and refer to it that way. I don't think the nature of problem is widely understood, because once you understand what's wrong, the solution is obvious and easy.

  • Anonymous
    November 03, 2010
    Great point, I've updated the post with some explanation of the issue surrounding ElementName binding.

  • Anonymous
    November 04, 2010
    Peter..thank you for your post - it was a watershed moment and allowed me to move a project forward.  Now I'm struggling with getting the validation up from the web side through to the combox implementation which isn't easy.

  • Anonymous
    November 04, 2010
    Jeff just did a whole series on RIA Services validation (jeffhandley.com/.../default.aspx) that is definitely worth checking out.

  • Anonymous
    November 06, 2010
    Thanks Kyle - I'm deep into it.  I was just hoping there was a way to incorporate it into your combobox solution without "heavy lifting".

  • Anonymous
    November 07, 2010
    @Mark It would be nice to know the areas that make server-side validation trickiest. Feel free to send either Jeff or I feedback and we'll see if there are things we can improve.

  • Anonymous
    November 09, 2010
    The comment has been removed

  • Anonymous
    November 09, 2010
    @Will Use the 'Email blog author' link above and I'll email you back.

  • Anonymous
    November 22, 2010
    Hi Kyle, Did you ever manage to organise a Northwind sample as mentioned back on the 11-02-2010? I would certainly be interested.

  • Anonymous
    December 01, 2010
    Yep, it's linked off my Silverlight TV: Episode 52 post. blogs.msdn.com/.../silverlight-tv-52-ria-services-q-a.aspx

  • Anonymous
    January 31, 2011
    How to we get the default value which is selected in a combobox? Basically I am creating automation test cases for our app, and I have tried doing - ComboBox.SelectedItem  - but this gives me a value ' .......ComboBoxEntity' so I assume the value are binded to some control. So now, how do I read the default selected value of the combobox?

  • Anonymous
    January 31, 2011
    @Hiral Which binding are you using to select the item, is it SelectedValue or SelectedItem? You should be able to use the same one you're binding with.

  • Anonymous
    February 04, 2011
    Kyle, I implemented your library today and attempted to added two separate comboboxes to my dataform one representing Role entities and the other representing Subscriber entities that are tied to my User entity in the dataform. I have custom validation on my Role and Subscriber entities so that only certain Subscribers can have certain Roles. The validation works fine. My problem is this: When the validation returns an error, in my validation summary my label is shown as "SelectedItem" for both Role and Subscriber rather than "Role" and "Subscriber". I bound the labels of both datafields that the comboboxes reside in to the right PropertyPaths. What am i doing wrong?

  • Anonymous
    February 04, 2011
    @BDors I'm not positive how the ValidationSummary works, but this sounds like it might be related to your custom validation. Make sure you're returning the property member name in the ValidationResult (take a look at the validation in this post for an example blogs.msdn.com/.../using-ria-services-with-comboboxes-and-enums.aspx).

  • Anonymous
    February 14, 2011
    The comment has been removed

  • Anonymous
    February 14, 2011
    @mond Unfortunately I don't have a VB version available. There are a few decent web sites that convert C# to VB, though. You should be able to take the C# sample and run the files you're interested in through one of those converters.

  • Anonymous
    March 23, 2011
    Kyle, have you ever tried using the extension in a child window? Can you please guide us on how to implement in a child window with dataform? Thanks in advance kyle. :)

  • Anonymous
    March 23, 2011
    hello again Kyle, i would just like to ask if you have tried using the extension in a SL4 Child window with a dataform? Could you guide us on now to go about it? Thanks in advance.

  • Anonymous
    August 08, 2011
    hello again Kyle, i would just like to ask if you have tried using the combobox extension in a SL4 Child window with a dataform? Could you guide us on now to go about it?

  • Anonymous
    August 10, 2011
    @Carlos have you started in anyway in trying the comboboxes in SL4? I have successfully implemented them using ChildWindow hoever now what I want to do is select the past item or record of the 2nd combobox. Where are having problems? Thanks. Almond