A Simple Multicolumn Combo Box in WPF

NOTE: the example in this post is using .NET Framework 3.5 SP1, Visual Studio 2008 SP1, and a little bit nuances of C# 3.0 syntax.

A ComboBox...

One powerfulness of WPF is UI composition. Using UI composition, I can compose a control using other controls. It simply looks like a aggregation, but a proper term used is composition.

NOTE: composition and aggregation are different in the lifetime term. In a composition, a parent maintains its children's lifetime. It means that if the parent were destroyed, all of its children were also destroyed along with it. And this is different from aggregation. Thus, it explains why it's better named 'UI composition'.

A ComboBox is one of my favorite control for choosing an item from a list of items. Through its popup, it lists all selectable items to choose. By default, it only allows me to display one type of information of the item. It means that it can only display one item's property for all items. It can simply be achieved by assigning the ComboxBox.DisplayMemberPath to the item's property name.

The Challenge

To make things clearer, consider the following example.

The Goal

I want to display a list of persons in two ComboBoxes. In the first ComboBox, I just want to display the person's name, while in the second one, I want to display the person's name and birthdate.

Thus, I have to prepare things to achieve that goal.

The Steps

First, I have a Person class which has two properties: Name and Birthdate:

   1: public class Person
   2: {
   3:     public Person()
   4:     {
   5:     }
   6:  
   7:     public string Name { get; set; }
   8:     public DateTime Birthdate { get; set; }
   9: }

 

Then, I also have a list of persons defined as follow.

   1: ObservableCollection<Person> persons = new ObservableCollection<Person>
   2:                                            {
   3:                                                new Person {Name = "Alice", Birthdate = new DateTime(2000, 1, 1)},
   4:                                                new Person {Name = "Alice", Birthdate = new DateTime(2004, 1, 1)},
   5:                                                new Person {Name = "Bob", Birthdate = new DateTime(2000, 1, 1)},
   6:                                                new Person {Name = "Charlie", Birthdate = new DateTime(2000, 1, 1)}
   7:                                            };

Notice that there are two persons named Alice but they have different birthdates. Based on this situation, if I were using single column ComboBox, it's hard for me to choose which Alice I want since it would only display the person's name. Thus, I need further information about the person information to be displayed. And this comes where a multicolumn ComboBox is needed to show additional required information. By displaying additional information about the person in the second ComboBox, which turns out to be the person's birthdate, I can now make a clear decision about which Alice I want to choose.

 

So next, I have the following UI representations in XAML (defined in a Window). It basically creates two ComboBoxes named 'comboBox' and 'multicolumnComboBox' respectively.

   1: <StackPanel Margin="10">
   2:     <Label>A ComboBox</Label>
   3:     <ComboBox x:Name="comboBox"></ComboBox>
   4:     <Separator Margin="0, 20, 0, 10" />
   5:     <Label>A Multicolumn ComboBox</Label>
   6:     <ComboBox x:Name="multicolumnComboBox">
   7:         <ComboBox.ItemTemplate>
   8:             <DataTemplate>
   9:                 <StackPanel Orientation="Horizontal">
  10:                     <TextBlock Text="{Binding Name}"
  11:                                Width="150" />
  12:                     <TextBlock Text="{Binding Birthdate, StringFormat=d MMMM yyyy}"
  13:                                Width="100" />
  14:                 </StackPanel>
  15:             </DataTemplate>
  16:         </ComboBox.ItemTemplate>
  17:     </ComboBox>
  18: </StackPanel>

The first ComboBox declaration, named 'comboBox', looks very simple. While the second one, named 'multicolumnComboBox', now looks like hell :D. So, what the hell is that?

 

Another WPF powerfulness is UI customization. Through customization, a control can be customized to look like to whatever it should look like. UI customization can be achieved using: (1) styles, (2) templates, (3) skins, and (4) themes (Adam Nathan explains them better in his book entitled 'Windows Presentation Foundation Unleashed').

In the example above, the multicolumn ComboBox UI customization is achieved using templates, specifically DataTemplate (other than ControlTemplate). A ComboBox has a dependency property named ItemTemplate (it is actually derived from ItemsControl). It can be used to customize each item appearance in its box (if ComboBox.IsEditable set to false) and popup. As the goal mentioned before, for the second ComboBox (the multicolumn one), I want to display both person's name and birthdate.

NOTE: using ComboBox.DisplayMemberPath may not be adequate to achieve the goal in a neat way (it's probably can be accomplished also using MultiBinding and its StringFormat property, but let it be another case :)).

That way, I then create a horizontal StackPanel (which can be visualized as a table) and two TextBlocks (which each can be visualized as a table's column). The first column holds the person's name information and the second one holds the person's birthdate. In order to make them look like columns, I have to set their respective Width manually. The way StackPanel and TextBlocks declared actually shows an example of a UI composition. The StackPanel is the parent and the two TextBlocks are the parent's children.

Finally, let's bind the data (the Model in MVC) to the UI (the View in MVC) so that it looks like this:

   1: CollectionView collectionView = (CollectionView) CollectionViewSource.GetDefaultView(persons);
   2:  
   3: this.comboBox.ItemsSource = collectionView;
   4: this.comboBox.DisplayMemberPath = "Name";
   5:  
   6: this.multicolumnComboBox.ItemsSource = collectionView;
   7: TextSearch.SetTextPath(this.multicolumnComboBox, "Name");

Notice that I'm using a CollectionView as a virtual data source of the person list defined before. It might look like an overhead but there's a reason it is put there. Anyway, for this post, let's ignore it and pretend that I actually bind the ComboBox.ItemsSource property directly to the person list. Also notice that for the first ComboBox, I set the ComboBox.DisplayMemberPath property to represent the person's name's property name (defined in the Person class definition). While for the second one, I set nothing other than the ItemsSource.

NOTE: using ComboBox.DisplayMemberPath and ComboBox.ItemTemplate is a choice. If you use one, you cannot use the other one or the ComboBox will throw an exception. Thus, you cannot use both at the same time. (UPDATE: 15/4/2009) In the last case, in order to allow user to search the item when IsEditable is set true, you can use TextSearch.SetTextPath() to achieve the goal.

The Result

And voilà, the overall application looks like this:

MainWindow

The first ComboBox looks like this:

ComboBox

And the second one looks like this:

MulticolumnComboBox

Notice that the second ComboBox now looks like having a table with two columns (except without column headers, and that's why it is called 'simple' :)) inside its popup. It should be neat, I think :).

Closing Thoughts

A new UI look of a control can be achieved using UI customization and composition in WPF. It should be easy and straightforward (in most trivial cases, up until you find gotchas :D). The example in this post shows another usage perspective of a ComboBox of being a multicolumn one instead of a single column one.

NOTE: get the source code

UPDATE: (15/4/2009) fix a bug with multicolumn combo box when IsEditable is set true. Thanks to Rakesh Kumar for finding out this.

Share this post: | | | |
Published Wednesday, December 10, 2008 3:54 AM by Maximilian Haru Raditya
Filed under: ,

Comments

# A Simple Multicolumn Combo Box in WPF | .Net Programming Junction!

Pingback from  A Simple Multicolumn Combo Box in WPF | .Net Programming Junction!

Powered by Community Server (Commercial Edition), by Telligent Systems