Silverlight ComboBox Sample for RIA Services

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.

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.

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

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?

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.

I haven't tried your sample yet. But I just want to add a quick note to thank you for writing up this blog. Finally we have someone from MS to confirm the current ComboBox has problems with it's basic design to work with DomainDataSource.

The ComboBox has given us all kinds of headaches. All business applications depend on this control. Hope your team and Control team will address this problem quickly.

ok, i found problem, fact is that we need to mark our methods of the DomainDataService (on server side) as [Invoke], but if we do that, we must return IEnumerable or duplicate methods for returning IEnumerable and its sounds like bad news, because i do not need to dublicate methods.

so, why ComboBoxDataSource is not working with IQueryable<ENTITY_TYPE> ?

The ComboBoxDataSource requires the operation name generated on the client. For Invoke methods, this will be identical to what you wrote on the server, but Query methods will need "Query" appended to the end.

thanks for feedback, i follow your recommendation but when i mark my method as [Invoke], i have error message after compilation:

Error 1 Operation named 'GetAgreementType' does not conform to the required signature. Return types must be an entity, collection of entities, or one of the predefined serializable types. ExpressCreditService

method looks like:

[Invoke]

public IQueryable<AgreementType> GetAgreementType()

{

return this.ObjectContext.AgreementType;

}

it about dublication of existing methods, i do not need to dublicate existing methods and replacing return types, because i have so many tables like this, than i need to connect with ComboBox

Thanks for sending me your repro. The point I want to reiterate here is the DataGrid and DataForm do not support the same kind of ElementName binding that everything else does. There is a boundary between the full UI tree and the DataTemplate and ElementName binding cannot cross that boundary. In the sample, I moved the data sources inside the DataForm DataTemplate so I could use ElementName binding. Alternately, I could have made the data sources Resources and bound to them directly as StaticResources.

Thanks. Another little nit I left out of the post. The 'ex:ComboBox.Mode' property needs to be set at the very end. Everything must already be specified for it to work its magic. Frustrating at first, but hopefully you won't run into again.

Hi Kyle, Thanks for the great explanation. How could we make this work in a DatatGrid? I've been trying to make that work for days now and it is driving me nuts. Could you update your example with it working in the DataGrid?

The problem with DataGrid is that it's less clear what you should do. I'd argue you really don't want cascading ComboBoxes (like I show in the sample) in a DataGrid. Keeping that in mind, you can do it if you have to. Here are the templates for Columns A and B.

Since 'A' is a query returning static results, you could remove the CBDS from the template, move it into the page resources, and then bind to it using a {StaticReference}. Also, I found DataGrid required me to implement more explicit validation and return the member name with every ValidationResult. Here's what I changed ValidateAssociation to.

It doesn't behave exactly the same. QueryName only worked with Query operations and would infer the 'Query' part of the name. OperationName can be used with Query and Invoke operations and requires the name to be fully specified. Feel free to link the source into your project and make the updates yourself to enable drop-in replacement if that would save you some time.

> The point I want to reiterate here is the DataGrid and DataForm do not support

> the same kind of ElementName binding that everything else does. There is a

> boundary between the full UI tree and the DataTemplate and ElementName binding

> cannot cross that boundary. In the sample, I moved the data sources inside the DataForm

> DataTemplate so I could use ElementName binding.

That was what I needed. I've moved the declaration of my DDS inside the template.

I have a horrible feeling that this means I'm going to get a fresh instance for every row in the DataGrid – would this be correct?

This innocuous little paragraph of yours nicely encapsulates an issue that is the fount of many woes. In my not terribly humble opinion, something must be done about this. Frankly I think they both need a rethink and a rewrite. Yes, I do know the enormity of what I've just suggested. I have a very good idea of how hard it is to produce a good quality grid, and an appreciation of how much might be entailed.

But it's IMPORTANT. The DataGrid is a mainstay of LOB development, and use of ComboBoxes to express FK lookups is a core scenario.

Seriously, if you guys have too much on, and I can well believe you do, it would do Microsoft much credit to reach out to the dev community for help by open sourcing the DataGrid.

Not necessarily correct. If you put the data source inside the edit template, you'll get one instance for that column that will re-databind every time you try to edit. If you put it inside the cell template, then you'll get one instance per visible row.

Also keep in mind that I would only endorse putting a data source in the template if you must have dynamic content (cascading lookups, etc). If you content is static (may or may not be asynchronously loaded), then I'd recommend making your data source a control Resource and using StaticReference binding. Even in the small set of scenarios where it's required, it might be better to consider a different UI design.

I'm not sure exactly what you're asking. I assume you're saying "I used to use DataForm field auto-generation and would see my DisplayAttribute values for each field. Now that I explicitly define my template I do not see those values." I think it's a limitation of the DataForm.

My first use for a combobox is from a table but static. I would like to use it for both datagrid's and forms. Simply I have created a table with all the states/provinces from north america. Rather than trust the users to enter one, and to know the 2 character codes, I would like a drop down list to appear that they can use to select the correct code. The list should show both the StateID (2 character code), and the Name. I am not sure which way it should be sorted, opinions are appreciated.

But that aside, I would appreciate an example of how you would use this with a datagrid. I noticed your comments above that if the resource is statis you would take a slightly different approach.

The list of states is in a table States. Customers has a 0,1 relationship to states, i.e. the field can be null.

I have copied the ComboBox.Extensions project, so I am ready to use them.

My next use is not static, since I will want to want to relate a salesrep to the customer.

If you have a static list, I would recommend creating a single instance of the data source. For example, I might choose to put it in the App or MainPage resources. Then I would just link it anywhere I needed using {StaticResource} binding. The benefit of resource binding is that it's available everywhere including DataGrid and DataForm templates (while ElementName binding is not). I think I'd still recommend using an async mode in your combo boxes so you don't have to worry about how long it takes the data source to load (the alternative is waiting for the load to complete before creating UI that references it). Hopefully that explanation makes enough sense to get you started.

What if there is a list of 400 items and I want to select one with the "combo"box?

I wrote "combo" because where is the combination? it's more like a drop down list.

I can't type anywhere, showing me only the items starting with the typed letters!

Can the SlApp (mentioned before in the comments) be rewritten, showing the combobox in the datagrid, only showing when the datagrid is entering editing mode? with a datapage control. And… still being showed after going to the next page.

And when it is possible to add a new item at the bottom of the datagrid (although this topic is about comboboxes) I would be very very happy.

You're right, it is more of a drop-down list. If you want auto-complete, take a look at the AutoCompleteBox shipped in the Silverlight Toolkit.

Did you see the big block of xaml I put on the first page of the comments? It shows how to put a TextBlock in the CellTemplate and a ComboBox in the EditTemplate. That should be what you're looking for.

I hear a number of people have asked for the ability to add new items. Hopefully they'll be able to add it to the DataGrid in SL5.

Can please someone tell me how can I use or to combine comboboxes to choose Country, State, City… etc…. All my combobox are feeded by RIA services and I want to do something like your sample Kyle, passing parameters between them. I think I'm able to do so but I still couldn't find a way to cast the selected item to Entity. Can you please help me with that?

Take a close look at ComboBox "C" in my sample. It works with a list of entities and associates the selected one with the SampleEntity instance (just like a City would do with a State). If you have a sample that isn't quite working, feel free to contact me via the blog, and I'll help you take a look at it.

Finaly I have my comboboxes loading from different Entitys but now I have another problem: saving the dataform. The Commit and Cancel are even disabled. I've seen you post in forums.silverlight.net/…/392624.aspx and I have to ask you, should I create different DomainContext to my three DDS?

Can you help me to get a drop-down list being populated on the "linked" datagrid.

Now only one item is showing in stead of a list, also I'll have to click three times to open the combobox, how can I get a list only clicking once? I've posted some code with this comment, I'll hope you can see it.

I've created three tables, A person with a place(plaats) where he lives and a communication type(soort), E-mail Fax or Phone.

I'm afraid I might not understand you correctly. It sounds like You have a few dependent ComboBoxes in your DataForm. It also sounds like you're using DomainDataSources to fill them with data. I'd recommend you don't take that approach (as I mentioned in my first point above). It might be better to use a ComboBoxDataSource instead.

To save data in your DataForm, you will need to submit it using the DomainContext where the entity you're editing was loaded. If you loaded the entity through a DomainDataSource, you can use it to submit the changes.

If you want to send me you application, I can take a look. Here are some general thoughts on your problem.

I'm not sure what you have to do to click once (instead of three times) to open a ComboBox. I'm pretty sure I've seen threads on it on the SL Forum, though. Take a look around.

I think you'll only see a single item in the list when using AsyncEager selection when you already loaded the data source Data and it does not contain the item you've bound the SelectedItem to. In that case, the extension assumes the source will contain the item soon. Perhaps there's something selected that's not in the type list? Also, check to make sure the Data is populated at all. You could see this scenario if the list were empty.

I've finally putted my three combobox working fine. I've a Combobox to choose "Country", once you choose it the other combobox load the states from that country and once you choosed the state, I have another Combo that loads the citys. I've followed your sample and I'm passing parameters to three combos and all combos are from diferent entitys and it's all working perfectly.

My question is, how can I save the data? "Country", "State" and "City" are diferent entitys and I want to save the data collected to another entity called "Address". Can you please help me? Thank you.

I'm not sure how easy that would be. I think you'd just have to make the Parameter (and possibly ParameterCollection) a FrameworkElement instead of a DependencyObject. Give it a try and let me know how it works.

@Bruno

It might be easiest if I could take a look at your application and make specific recommendations. To contact me, use the 'Email Blog Author' link at the top. I'll reply back so you can send me attachments, etc.

I included the source with the library so you could make any small tweaks or changes that your application needs. In this case, I haven't changed the source at all so the version you downloaded with the library is the latest.

I have to agree with David Zuber's post on 22 Jul 2010 12:46 AM. Your combobox is a life saver, but for some reason it prevents the metadata being loaded by the data form. So I won't get any properties I define through [Display()] on the server. And if I'm not auto generating my fields, you proposed solution doesn't work.

Is there way to force a DataField to pick up the metadata somehow, something in the combobox extension that we can change, so that this data will appear.

Not yet. I looked through the DataForm for hooks into the metadata, but couldn't find anything worthwhile. I have another thought (drawn on my whiteboard), but I'm not sure whether that will work either. How hard would it be to 'brute force' your metadata? You just have to set the DataField properties to the same strings. I'll keep working on this, but I'm not sure when I'll have an acceptable solution.

I have the same scenario as Bruno Lopes posted on 17th August (Country, State scenario)

Problem is my app is able to show the right selected items on Country and State comboboxes but comboboxes do not show other items except the selected item when I tried to edit or change the selection on Country combobox.

I have it working pretty well, but I'm having trouble requerying the data for the combobox.

The data in the combobox is dynamic, and can be edited elsewhere in my application. If I do edit the data that is to be displayed in the combobox, the combobox is not changed.

I tried adding ComboBoxSource.Refresh() on the GotFocus event of the combobox. That helps, in that it will refresh the collection to show newly added or newly deleted records. But the Refresh() method doesn't show edits to the existing members of the collection. Does that make sense?

the second combobox…(the WRONG way to do this) is working fine.The selected item shows the correct member(but it doesn't change the value in the datasource when i select a different item in the combobox)…but the first combobox using your class isn't showing the selected item from the grid.selecteditem (looking at the source) is ALWAYS null.

I don't know what using a SelectedValuePath with a SelectedItem binding does. I assume it doesn't work. Also, why would you want the item selected in your DataGrid to be the item selected in your ComboBox. I imagine you'd want to be setting the DataContext instead. Something along these lines (from the sample).

I am following your example and implementing the combobox extensions in my problems. I am having some issues with the combobox not displaying any selected item when loading sometimes. I am using the comboxes in a view directly not inside a data form. I am copying the combobox code from the view. Can you please tell me where I am doing it wrong.

Make sure you set the ex:ComboBox.Mode at the very end so it can correctly handle the bindings. Also, AsyncEager mode doesn't work when DisplayMemberPath is different from SelectedValuePath. You'll need to use plain Async mode instead (see this post for an explanation of the difference blogs.msdn.com/…/silverlight-combobox-frequently-asked-questions.aspx). If those changes don't fix it, feel free to email me using the 'Email Blog Author' link above.

I was able to run your Extension in a chid window, now my problem is that i am not able to save the new record that I created and also the record of which i editted. What I am curious is what is that when I edit the a record, the changes reflects on the Grid but not on the database? I am missing something here? Also when saving a record after creation, an error is being experienced in the with regards to the filterdescription declaration.

Hope you can help me. It has been weeks and i can't go forward with my project because of this issue 🙁

Ok, here is the challenge, as it seems everyone avoids it in every sample application I've seen.

I have a database with only two tables. The main table has foreign key to a lookup table.

For example Employee table and Department table.

The employee table has just the ID of the department table.

The department table has the description of the dept – relational database 101.

Simple simple as can be.

Using RIA services I have a navigation page with a dataform to maintain details about the employee. Instead of showing the Department ID (integer) to the user. I simply want to show a combo box listing the items in the department table and for the selected item to be the employee's current dept.

Now I feel a bit stupid posting this as I consider the request to be no more complex than a "Hello World" sample.

However, after six month of browsing sample after sample, tutorial after tutorial I am yet to find one demonstration of this concept.

Why can I not find this? I suspsect the answer is that Silverlight / Ria Services cannot do it. In which case it is next to usless.

Hi Kyle, thanks for the great code. I just have one question: is it possible to reload the data from the web service via code? I have a ComboBox in a DataForm displaying a list of items, and next to this a button which opens a modal dialog to allow editing of this list. When the dialog closes, changes are submitted to the database, but not reflected in the ComboBox.

I've tried getting the ComboBoxDataSource (var ds = (ComboBoxDataSource)this.myEditForm.FindNameInContent("MyCBDataSource"); ) and calling ds.Refresh(), but this doesn't seem to do anything. With a DomainContext I'd call ctx.Load(), but there's no Load method on the ComboBoxDataSource.

If you want an editable list, then maybe using the CBDS isn't the best approach. You can always just set comboBox.ItemsSource explicitly in your code. For instance, maybe you could populate it initially with the result of ctx.Load() and then update it as the list is edited. An EntityList might work well in this scenario (take a look at this post to learn about EntityLists blogs.msdn.com/…/collection-binding-options-in-wcf-ria-services-sp1.aspx).

I am using Ria Services and i need some cascading Combo boxes. I use your combobox extension liabrary. First combo ,works perfectly, and with hard coded parameters other combo also work. But when i take parameter values from combo 1, it does'nt work.

Means, First time all combo calls their respective data methods but on selection change of combo box 1 doesnt fire the data populate methods of dependant combos. Am i missing something???

Hope you can again help me with my concern. I would just like to select the first record of the combobox after a Selection changed was done in the the main combo box. For example i have 3 tables as follows: company, department and section. If the company combobox changes only the selected records related to that company will be loaded in the departmentCbo. I have successfully implemented it however i still need to click the departmentCbo in order for me to see the records. Is it possible to select and display the 1st Record of the filtered departmentCbo based on the companyCbo.

By the way, i have successfully implemented that same selection process if the combobox is outside a dataform. I used the LoadCompleted event of the ex:ComboBoxDataSource and from the LoadCompleted code behind I then execute the departmentCbo SelectedIndex = 0; command.

Can you tell me what I'm missing…. I have an AGENCY table and DEPARTMENT table yet when displaying AGENCY i cant see DEPARTMENT.name.. I see it DEPARTMENT table listed in my AGENCY metadata.. But why can't i see DEPARTMENT.name.. Please any help would be great.. I am trying to get this solution to work because I'm having weird dataform/multi combobox issues (where it saves the first time but not the second)

Kyle – you mention at the conclusion of this article that you hope the combobox issues will be addressed in SL5. Do you think that's the case? I'm beginning development with v5 now and I wonder if I still need to use the customdatasource or if I should go back to the dds. -Thanks, Mark