Tuesday, April 24, 2007
« Orcas Beta 1: ASP.NET not enabled by def... | Main | Check out i4o: indexing for LINQ »

I am building a WPF UI for my ProjectTracker CSLA .NET sample app. On the whole this is going pretty well, and I anticipate being done within the next couple days. I’ve found and fixed a couple bugs in CSLA – one in BusinessListBase that’s been there forever, and one in ValidationPanel that caused a null reference exception. While I fixed that last one, I optimized the code a bit, which does seem to make the control a bit faster.

But one thing I spent a ridiculous amount of time on was the simple process of getting a ComboBox control to bind to one data source to get its list of items, and to the business object property for the key value. Google turned up a number of search results, none of which really addressed this particular scenario – which seems odd to me given how common a scenario it is…

In ProjectTracker, a person (resource) can be assigned to a project. If they are, they are given a role on that project. The Role property is numeric – a key into a name/value list, and a foreign key into the Roles table in the database. In Windows Forms and Web Forms the UI handles translating this numeric value to a human-readable value through ComboBox controls, and obviously WPF can do the same thing. The trick is in figuring out the XAML to make it happen.

Here’s the ComboBox XAML:

            <ComboBox

              ItemsSource="{Binding Source={StaticResource RoleList}}"

              DisplayMemberPath="Value"

              SelectedValuePath="Key"

              SelectedValue="{Binding Path=Role}"

              Width="150" />

 

Let’s break this down.

The ItemsSource property specifies the data source for the data that will populate the display of the control. In my case it is referencing a data provider control defined like this:

  <Page.Resources>

    <csla:CslaDataProvider x:Key="RoleList"

                           ObjectType="{x:Type PTracker:RoleList}"

                           FactoryMethod="GetList"

                           IsAsynchronous="False" />

  </Page.Resources>

 

This data provider control loads a name/value list object called RoleList (that inherits from Csla.NameValueListBase). The child objects in this collection expose properties Key and Value.

You can see how the ComboBox uses the DisplayMemberPath to specify that the Value property from the name/value list should be displayed to the user, and the SelectedValuePath specifies that the Key value from the list should be used to select the current item (the Key value is not displayed to the user).

Then notice that the SelectedValue property is used to bind to the main business object. This binding statement sets a path to a property on the overall DataContext for the ComboBox, which in my case is actually set through code at the Page object level:

      this.DataContext = project;

So all controls on the entire page, by default, bind to a Project object, and this includes the ComboBox.

Remember though, that the Role property on the Project is a numeric index value. And so is the Key value from the name/value list. The ComboBox control connects these two automatically. So when the business object’s Role property changes, the ComboBox automatically changes the displayed/selected item. Conversely, when the user changes the ComboBox selected item, that automatically causes the business object’s Role property to change.


Wednesday, April 25, 2007 10:25:11 AM (Central Standard Time, UTC-06:00)
Rocky,
Is the version of ProjectTracker, that you're referring too in your blog entry, in the SVN repository?

Chris
Chris
Wednesday, April 25, 2007 4:33:47 PM (Central Standard Time, UTC-06:00)
Rocky,

In the last couple of days I started to look at LINQ and as a first example application thought I would load a set of data and bind it to a combobox. Sounds like I went down a very similar path of discovery as yourself. Seeing the XAML code that was coming up in google searches, this definately didn't seem intuitive so I must confess that I bailed out at used a WebForm application as the goal was to use LINQ.

Thanks for decyphering this one for us, might convert my LINQ sample to XAML now.

P.S. Have you used Dan Appleman's Search.Net (http://www.searchdotnet.com/)?

Blair
Wednesday, April 25, 2007 4:40:27 PM (Central Standard Time, UTC-06:00)
Yes Chris, SVN is always the most recent code. I'm presenting at the Minneapolis Codecamp this weekend though, so I'll likely refresh the test release of both CSLA .NET 3.0 and ProjectTracker on Friday.
Friday, June 22, 2007 3:51:20 PM (Central Standard Time, UTC-06:00)
i've been having the SAME big issue with projects i'm working on and i was unable to get your example to work in a mock app... i have this:

public class Employee
{
int _statusCode;
public int StatusCode
{
get { return _statusCode; }
set { _statusCode = value; }
}
string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}

and this:
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}

yet:
<ComboBox DisplayMemberPath="Description"
ItemsSource="{Binding Source={StaticResource codes}}"
SelectedValuePath="Id"
SelectedItem="{Binding StatusCode}">

</ComboBox>
Isn't working as you have suggested it should.... the items are there and the underlying data context of the whole page is an employee with StatusCode equal to an int that represents one of the status codes Id's in the list.

in the end i have a lookup service and am using IValueConverter to convert between ints and statuscodes each time, which sucks.
Friday, June 22, 2007 4:14:46 PM (Central Standard Time, UTC-06:00)
It works on my machine :) (gotta love that one huh?)

Here's the XAML from PTWpf:

http://www.lhotka.net/cslacvs/viewvc.cgi/trunk/ProjectTrackercs/PTWpf/ProjectEdit.xaml?view=markup

<ComboBox
ItemsSource="{Binding Source={StaticResource RoleList}}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=Role}"
Width="150" />

RoleList is a CslaDataProvider providing the RoleList name/value list, and the ComboBox is within a list item that is bound to a ProjectResource object with an int Role property.

Looks pretty much like your code, though your problem might be that your code doesn't support data binding (no PropertyChanged or ListChanged events coming from your objects). My objects inherit from CSLA base classes and so have full data binding support.
Comments are closed.