Silverlight and RIA: Adding a ComboBox to a DataForm

Posted Wed, Nov 25 2009 15:44 by Deborah Kurata

Building a DataForm is quick and easy and is detailed in this prior post. Even customizing it is a breeze as was also shown in that prior post. But now the design calls for a ComboBox. Ready to walk off a cliff?

It is surprisingly difficult to modify a DataForm to display a working ComboBox that is bound to a DomainDataSource. And it is even harder because so many of the available examples use a Fields collection that disappeared in July of 2009.

This post provides the steps for adding a ComboBox control to a DataForm. It retrieves the values for the ComboBox via RIA Services, but you could bind the ComboBox to any collection of data.

The ComboBox used in this example is the one from this prior post. To use it in this example, follow these steps:

1. Add a DomainDataSource for the codes that will populate the ComboBox.

2. Add the ComboBox and bind it to the DomainDataSource.

Before adding the ComboBox to the DataForm, let's try the above steps adding the ComboBox directly to the UserControl.

In XAML:

<UserControl xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"  x:Class="SLVB.CustomerSummaryUC"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls" 
    xmlns:domain="clr-namespace:SLVB.Web">

    <Grid x:Name="LayoutRoot" Background="BlanchedAlmond">
        <riaControls:DomainDataSource x:Name="CustomerSource"
               QueryName="GetCustomers" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CustomerContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>

        <riaControls:DomainDataSource x:Name="CustomerTypeSource" 
               QueryName="GetCustomerTypes" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CodeContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>

        <StackPanel Margin="5,5,200,5">
            <ComboBox ItemsSource=
                 "{Binding Data, ElementName=CustomerTypeSource}"
                  DisplayMemberPath="CodeText"/>
            <dataFormToolkit:DataForm
                  ItemsSource=
                  "{Binding Data, ElementName=CustomerSource}">
            </dataFormToolkit:DataForm>
        </StackPanel>
    </Grid>
</UserControl>

The CustomerTypeSource is the new DomainDataSource. The ComboBox binds to this data source and displays the customer types.

The results are as follows:

image

The ComboBox appears above the DataForm and partially covers it when it is open. So now we know that the ComboBox works. The next step is to insert it instead into the DataForm.

There is no way to change the type of one field on the DataForm without then specifying every field on the DataForm. So if we want to specify that the Customer Type is a ComboBox, we can no longer use the auto field generation and must instead manually define every field.

The DataForm XAML code is then significantly longer.

In XAML:

<StackPanel Margin="5,5,200,5">
    <dataFormToolkit:DataForm
              ItemsSource="{Binding Data, ElementName=CustomerSource}">
        <dataFormToolkit:DataForm.EditTemplate>
            <DataTemplate>
                <StackPanel
                       dataFormToolkit:DataField.IsFieldGroup="True">
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding FirstName, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding LastName, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <ComboBox ItemsSource=
                      "{Binding Data, ElementName=CustomerTypeSource}"
                      DisplayMemberPath="CodeText"/>
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding EmailAddress, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                </StackPanel>
            </DataTemplate>
        </dataFormToolkit:DataForm.EditTemplate>
    </dataFormToolkit:DataForm>
</StackPanel>

A DataField element defines each field on the form. Within the DataField element is the control to display for that data field. In most case, this is a TextBox control. The Text property of the TextBox is bound to the appropriate field in the data source. The mode is TwoWay to provide review and edit.

So far, the ComboBox code is the same code defined earlier when the ComboBox was above the DataForm. But now it is in the desired location within the DataForm.

Let's give this a try and see what we have:

image

Well, the ComboBox is there … but it is empty. We simply copied the working ComboBox from above the DataForm to inside the DataForm and now it no longer populates. Hmmm.

After spending several hours Bing'ing about this … I ran across this post and thought it might be relevant. But instead of building a proxy, I thought I would just move the DomainDataSource into a UserControl resource.

So I removed the CustomerTypeSource DomainDataSource from under the Grid element and instead added it to a resources section. I then changed the x:Name to x:Key.

In XAML:

<UserControl.Resources>
    <riaControls:DomainDataSource x:Key="CustomerTypeSource"
                   QueryName="GetCustomerTypes"
                   AutoLoad="True">
        <riaControls:DomainDataSource.DomainContext>
            <domain:CodeContext/>
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
</UserControl.Resources>

This required changing the binding on the ComboBox to use this as a StaticResource. The other change was setting the SelectedItem to the CustomerTypeId.

In XAML:

<ComboBox ItemsSource=
    "{Binding Data, Source={StaticResource CustomerTypeSource}}"
     DisplayMemberPath="CodeText"
     SelectedItem="{Binding CustomerTypeId, Mode=TwoWay}"/>

And the result:

image

Whohoo!!

Now the only problem is that the SelectedItem is not correct. Regardless of the customer type for a Customer, the SelectedValue is always set to the first item on the list. How do we fix this? Yes, another cliff.

The data bound to the ComboBox has both a display member (the CodeText) and a value member (the CodeId). We want to bind the CustomerTypeId property to the Code Id.

Because the ComboBox does not have a ValueMemberPath property, there is no easy way to tell the control that it should map the CustomerTypeId to the CodeId. But there is a hard way using Converters.

The fact that the ComboBox is missing a ValueMemberPath seems like a bug that I hope will be corrected.

But I have to start baking Thanksgiving pies. So more on this in a future post.

Happy Thanksgiving!

Enjoy!

Comments

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Saturday, November 28, 2009 6:21 PM by sebnet

Great post... but I have one question.

I'm experimenting a little bit with this RIA thing and came across the following situation:

The query to retrieve the combobox items is a paremeterized query, so I added a query parameter to my DomainDataSource, but, unfortunately the query parameter is not bindable, the only way to pass a value is harcoding it... The value for this parameter is given from the selected item in my dataform...

do you know how could I overcome this problem?

bye,

Sebastián.

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Saturday, November 28, 2009 8:10 PM by Tim

Great Post Deborah!  I have been fighting this for awhile.  I have a custom combobox that I created that loads on demand but it has a few quirks.  This is so much cleaner. I am looking forward to your follow up post once you solve the next piece of the puzzle.

Also another variable to throw at it once you have it working are some validation attributes.  I have a required attribute on mine (one of the quirks) and the dataform can correctly identify that has changed and needs to save but the box does not get a red border or tooltip.

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Friday, December 04, 2009 12:35 PM by Bryan G Campbell

My thoughts while reading through this post:

"Yes, yes, I've been there, move my working combo box into a Data Form and suddenly it doesn't work."

"Ok, good, move the DDS to resources and bind as StaticResource to get ComboBox items to display, I've solved that the same way."

"Yep, the SelectedItem is not sticking, lets see how she solves that."

"Wait, that's the end? How can that be? All this talk about cliffs and she leaves me hanging off one? Now I'm going to have to subscribe to this blog to see if she comes up with an elegant solution."

This is a very clear explanation of the problem, thanks! As you say, hopefully the Silverlight team gets this resolved. A ComboBox should never be this difficult to use.  Until then, I guess we keep doing it the hard way. :-(

# Silverlight: ComboBox SelectedItem

Friday, December 04, 2009 8:08 PM by Deborah's Developer MindScape

When last we saw our Silverlight ComboBox in this prior post , it was correctly populating, but as we

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Sunday, December 06, 2009 8:19 PM by Deborah Kurata

Hi Bryan -

The continuation of this is already posted. It is here:

msmvps.com/.../silverlight-combobox-selecteditem.aspx

Thanks for visiting the blog!

# Silverlight: Wish List

Friday, December 11, 2009 10:30 AM by Deborah's Developer MindScape

Yes, it is that time of the year when we put together our holiday wish list and hope Santa has us on

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Tuesday, December 15, 2009 2:33 PM by Jonathan

Hmmm, I don't know why, but this doesn't work for me, the ComboBox remain empty, even after binding it to a UserControl.Resource, changing the Name to a Key and switching the binding like you did (literally a copy&paste).

Any clue ? I've been searching for hours :(

# re: Silverlight and RIA: Adding a ComboBox to a DataForm

Tuesday, December 15, 2009 3:12 PM by Deborah Kurata

Hi Jonathan -

Post your question here: forums.silverlight.net/forums

That will make it easier for you to post some code and for other experts to see the question as well.

Hope this helps.

Leave a Comment

(required) 
(required) 
(optional)
(required)