Click here to Skip to main content
Click here to Skip to main content

Multiple-Selection ComboBox for Silverlight

By , 12 Mar 2013
 

Introduction

ComboBox is a widely used control. Sometimes, we want to go beyond its default capability, such as multiple-selection. In this article, I will walk through with you how to create a multiple-selection ComboBox for Silverlight. The same approach applies to WPF. I will use Expression Blend for design. It is efficient to use the Blend to create a resource skeleton; and you can use either Blend or Visual Studio to edit the code.

In this updated version, I answered the most asked question: How to display the selected items?

Step by Step

The goal is to create a multiple-selection ComboBox as shown in the left side of following figure. The items in the dropdown should display with CheckBoxes. The right side ListBox demonstrates the same XAML code can be used for a multiple selection ListBox. One stone kills two birds!

Assume you have a ComboBox in place, and want to make it supporting multiple-selection. First, right-click the ComboBox to popup the context menu. In the menu, select Edit Template -> Edit a Copy. This will open the Create Style Resource dialog box. You can rename the default Name (Key) in the dialog. And then click OK to close the dialog.

Now you get the default template of the ComboBox in your .xaml file. You can browse the details in the following window, and figure out how the ComboBox works: when you click the ComboBox, it pops up a Popup control. By default, the Popup control uses an ItemsPresenter to display the SourceItems of the ComboBox. We need to replace it with a ListBox which supports multiple-selection.

Tips

  • The Windows theme setting will effect on the Blend generated template. I suggest you avoid Basic Themes.
  • The Blend generated template could be slightly different with different versions of Silverlight. If you cannot make the ComboBox template generated with Silverlight 3 work with Silverlight 4, regenerate it Silverlight 4.

We also need to the replace the ListBoxItem with CheckBox so that the user can easily identify the selection status. It would be efficient to do this in the XAML window. Let’s create the CheckBoxListBoxItemStyle style as follows:

<Style x:Key="CheckBoxListBoxItemStyle" TargetType="ListBoxItem">
    <Setter Property="Foreground" Value="#FF000000" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Grid x:Name="RootElement">
                    <CheckBox ClickMode="Press" Content="{Binding Path=Name}"
                          IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style> 

Make sure you set ClickMode="Press"; and Mode=TwoWay.

The data I am using here is very simple: a Name property, and an IsSelected property.

 

Set the ItemContainerStyle in the ListBox to the CheckBoxListBoxItemStyle as follows:

<ListBox x:Name="lstBox" SelectionMode="Multiple"
                 ItemsSource="{TemplateBinding ItemsSource}"
                 ItemContainerStyle="{StaticResource CheckBoxListBoxItemStyle}"
                 HorizontalAlignment="Stretch" />

Now you can build and run the project. In the demo page, I included an additional ListBox, which is bound to the same data. The ListBox reuses the same style CheckBoxListBoxItemStyle. With the ListBox, you can validate that the selection is updated on both the ComboBox and the ListBox.

Now it’s time to create a Resource Dictionary file, and move the resource (templates and styles) to the Resource Dictionary.

It seems ambiguous to display the selected items in the collapsed ComboBox, which is designed to display a single item. Here we need the creativity of the User Interface design. For instance, we can reduce the width of the ComboBox, and use a separate ItemsControl to display the selected items. The ItemsControl binds to the same data as ComboBox, but displays selected items only. We can implement a converter class with IValueConverter to convert the IsSelected bool property to Visibility. In this way, the ItemsControl displays selected items only.

<ItemsControl ItemsSource="{Binding Data}" >
<ItemsControl.ItemTemplate>
              <DataTemplate>
                    <TextBlock Text="{Binding Name}"
			Visibility="{Binding IsSelected,
			Converter={StaticResource BoolToVisibilityConverter}}" />
              </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Further, we can use the Tooltip to display the selected items. The following figure shows the Tooltip with an ItemsControl embedded within.

Following is the code snippet for how to embed an ItemsControl in the Tooltip:

<ComboBox ItemsSource="{Binding Data}" Width="26" VerticalAlignment="Center"
	Style="{StaticResource MultiSelComboBoxStyle}" >
    <ToolTipService.ToolTip>
        <ToolTip>
            <ItemsControl ItemsSource="{Binding Data}" Margin="4" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"
			Visibility="{Binding IsSelected,
			Converter={StaticResource BoolToVisibilityConverter}}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ToolTip>
    </ToolTipService.ToolTip>
</ComboBox>

Conclusion

The ComboBox contains a Popup control for hosting the item selection control. You can replace the default one to customize the behavior, such as multiple selection. Technically, you can embed a UserControl (or almost any control) in the Popup control. This opens the door for creating more sophisticated User Interface. For detail. please read How to Popup Anything? 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Frank W. Wu
Technical Lead
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionAdd a "Title" to the closed ComboBoxmemberNicolas Cordouan12 Mar '13 - 0:44 
Hi all, and thanks a lot for this article,
 
To those who wonder how to display a custom text on the ComboBox, you can achieve this using the Tag property of the ComboBox, here's how :
 
In the Style definition add a TextBlock bound to the Tag property:
<Style x:Key="MultiSelComboBoxStyle" TargetType="ComboBox">
	[...]
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="ComboBox">
				<Grid>
					<Grid.Resources>[...]</Grid.Resources>
					[...]
					<Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">[...]</Border>
 
					<TextBlock x:Name="Title" Text="{TemplateBinding Tag}" Margin="20 4" />
 
					<Popup x:Name="Popup">[...]</Popup>
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>
 
Then simply bind the Tag property to whatever you want in your view :
<ComboBox ItemsSource="{Binding Data}" Style="{StaticResource MultiSelComboBoxStyle}" Tag="{Binding CustomTitle}" >
	[...]
</ComboBox>
 
And that's it !
Your ComboBox will now display the content of the CustomTitle variable.
Theory is when you know everything but nothing works.
Practice is when everything works but no one knows why.
In our laboratory, theory and practice are combined: nothing works and no one knows why.
 
Albert Einstein

QuestionUnable to populate labels/text/memberRavichandran M. Kaushika3 Dec '12 - 12:03 
dear Frank Wu and other readers,
 
good morning. i have copied the multiselstyle.xaml file to my project and in the /styles/ folder and was able to import into my app.xaml file.
 
i want to create these multi-select combo boxes in code behind. so i was able to creat a multiselect drop down and set an observable collection to the itemsource. i am able to see 11 check boxes when i cliked the button part of the combo box but i see no label text.
 
my observable collection returned 11 records (name bvalue pair) and there are exactly 11 check boxes. i am somewhat missing 1 piece of info.
 
ComboBox cbMultiSelectCombo = new ComboBox();
             cbMultiSelectCombo.Style = _styleMultiSelectComboBox;
            cbMultiSelectCombo.Name = strControlNameInParam;
            cbMultiSelectCombo.Margin = SetUniformWidthMarginForDataEntryControls(4);
            
            ToolTipService.SetToolTip(cbMultiSelectCombo, strTextBlockLabel4ControlInParam);
            
            cbMultiSelectCombo.Name = "comboBoxMulti4" + strControlNameInParam;
            cbMultiSelectCombo.Margin = SetUniformWidthMarginForDataEntryControls(4);
           
            Binding bBinding = new Binding();
            bBinding.BindsDirectlyToSource = true;
            bBinding.Mode = BindingMode.TwoWay;
            bBinding.Source = observCollOfNameValPairs;
 
cbMultiSelectCombo.ItemsSource = observCollOfNameValPairs;
          //  cbMultiSelectCombo.SelectedIndex = 0;
 

            cbMultiSelectCombo.SetValue(Grid.ColumnProperty, 1);
            cbMultiSelectCombo.SetValue(Grid.RowProperty, intTargetRow4Add);
 
Add statement for the cb multiselect combo box to a stackpanel.
 
i will be happy to get some help.
 
regards
ravi.
Ravichandran M. Kaushika
Dallas Fort Worth Area, TX.

AnswerRe: Unable to populate labels/text/memberFrank W. Wu3 Dec '12 - 14:21 
Ravi,
 
If you need to use code behind, please consider using How to Popup Anything?. It gives you much more flexibility. And for the DataGrid, you may need A Smart Behavior for DataGrid.AutoGenerateColumns.
GeneralRe: Unable to populate labels/text/memberRavichandran M. Kaushika4 Dec '12 - 5:28 
Frank,
 
good morning. thank you for a prompt response and direction.
 
regards
ravichandran
Ravichandran M. Kaushika
Dallas Fort Worth Area, TX.

GeneralMy vote of 3memberUday P.Singh11 Jun '12 - 6:45 
not clear
GeneralRe: My vote of 3membernare s14 Mar '13 - 2:55 
Sleepy | :zzz:
QuestionHow to get the selected index changed event fired?memberUday P.Singh8 Jun '12 - 23:25 
How to get the selected index changed event fired? and get which check box is checked on this event?
AnswerRe: How to get the selected index changed event fired?memberFrank W. Wu11 Jun '12 - 3:26 
Some questions including yours are out of this article’s scope. I will write a separate article to address it.
GeneralRe: How to get the selected index changed event fired?memberdbernhardt14 Jun '12 - 5:44 
I anxiously await your post.
 
I tried adding a SelectionChanged event handler to the Combo object. The routine was never called.
 
I tried adding a Click event handler to the CheckBoxListBoxStyle/ControlTemplate in MultiSelComboBoxStyle.xaml --
CheckBox ClickMode=Press .. Click=CheckBox_Click
I get the error below at runtime when I click one of the checkboxes.
Failed to assign to property 'System.Windows.Controls.Primitives.ButtonBase.Click
no matter how I define CheckBox_Click in MainPage.xaml.cs --
public void CheckBox_Click(object sender, ButtonBase e) { }
public void CheckBox_Click(object sender, RoutedEventArgs e) {}
 
I tried creating a custome event handler routine in ViewModel.cs to handle the MyData.PropertyChanged event, but didn't figure out how to wire it to the events.
SuggestionRe: How to get the selected index changed event fired?memberFrank W. Wu10 Jul '12 - 8:35 
Please take a look How to Popup Anything?, which presents a different approach, and let me know if you have any questions.
GeneralRe: How to get the selected index changed event fired?memberdbernhardt18 Jul '12 - 4:08 
The PopupApp is instructive. However, I have not figured out how to use the technique in a Silverlight application.
 
I can add a Popup object, but it does not have StaysOpen, PlacementTarget or Placement properties.
 
I cannot add references to PresentationCore or PresentationFramework to my Silverlight project because they were "not built against the Silverlight runtime".
GeneralRe: How to get the selected index changed event fired? [modified]memberFrank W. Wu18 Jul '12 - 8:12 
Silverlight is different from WPF. 1) To set the relative position of the ToggleButton and Popup, you need to wrap them in a StackPanel. 2) The easiest way to close the Popup is to add a “Close” ToggleButton in the popup. Here is the code snippet:
<StackPanel Margin="10">          
    <ToggleButton x:Name="dgDropdown" HorizontalAlignment="Left" >
        <ToggleButton.Content>
            <TextBlock>                      
                <Run Text="Selection" />                       
                <Run Text="q" FontFamily="Wingdings 3" />
            </TextBlock>
        </ToggleButton.Content>
    </ToggleButton>
    <Popup IsOpen="{Binding IsChecked, Mode=TwoWay, ElementName=dgDropdown}"  >
        <Border BorderBrush="SlateBlue" BorderThickness="1"  CornerRadius="2" >
            <Grid Background="LightGray">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <sdk:DataGrid ItemsSource="{Binding Data}" IsReadOnly="True"
                        AutoGenerateColumns="False" 
                        HorizontalGridLinesBrush="Transparent" VerticalGridLinesBrush="Transparent" Margin="2">
                    <sdk:DataGrid.Columns>
                        <sdk:DataGridTemplateColumn  >
                            <sdk:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                            HorizontalAlignment="Center" VerticalAlignment="Center" IsThreeState="False" Margin="2"/>
                                </DataTemplate>
                            </sdk:DataGridTemplateColumn.CellTemplate>
                        </sdk:DataGridTemplateColumn>
                        <sdk:DataGridTextColumn Header="Nmae" Binding="{Binding Name}" IsReadOnly="True"/>
                    </sdk:DataGrid.Columns>
                </sdk:DataGrid>
                <ToggleButton Grid.Row="1" Content="Close" Width="100" HorizontalAlignment="Right"
                                IsChecked="{Binding IsChecked, Mode=TwoWay, ElementName=dgDropdown}" 
                                Margin="10"/>
            </Grid>
        </Border>
    </Popup>
</StackPanel>


modified 19 Jul '12 - 16:03.

QuestionCodeplex projectmemberAdriaan Davel29 Mar '12 - 20:27 
I have started a codeplex project for a multiselect combobox (hopefully it will make it's way to the toolkit), it features a bindable SelectedItems etc, if you would like to help make it better please visit http://slmultiselectcombo.codeplex.com/[^]
____________________________________________________________
Be brave little warrior, be VERY brave

GeneralMy vote of 5memberLR___1 Dec '11 - 7:36 
Very useful, thanks!
QuestionCustomize your great controlmemberMr. J.4 Aug '11 - 21:10 
Hi,
We're using your drop down list control to filter a list (we have a few drop downs - few filters)
What is the best way to achieve the following, using your great control?
 
1. For each drop down list, I want to enable the following options:
a. select all (which toggles between selecting all or unselecting all)
b. display the current selection(s) on the combo box itself
b.1 if a user selects nothing, it will display nothing
b.2 if a user selects a single selection, it will display it
b.3 if a user selects two or more items, it will display "Multiple"
b.4 if a user selects all, it will display "All"
 
2. I'd like to have two buttons: OK, Cancel to close the selection list
 
Thanks a lot for your help. Smile | :)
AnswerRe: Customize your great controlmemberFrank W. Wu7 Aug '11 - 3:43 
Have you realized what you needed was not a ComboBox?
GeneralRe: Customize your great controlmemberMr. J.6 Sep '11 - 20:44 
Why not?
GeneralRe: Customize your great controlmemberFrank W. Wu7 Sep '11 - 2:41 
A ComboBox hosts a ListBox while you need to host multiple controls. I suggest you use a Popup control to host your ListBox and Buttons. Use a ToggleButton in the parent window to show/hide the Popup. Your OK button needs to be a ToggleButton, too. Both ToggleButtons bind to the same property (for show/hide) so that their statuses are in sync.

modified on Wednesday, September 7, 2011 9:51 AM

GeneralMy vote of 3memberJohnny J.2 May '11 - 3:28 
See my comment to Piyush Nandanwars message
GeneralRe: My vote of 3memberShimmy Weitzhandler6 Mar '12 - 18:17 
Agree.
I voted 4 instead of 5 for the same reason - no way to change title.
See my comment bellow.
Shimmy

GeneralHow to display text on this multiselect combobox??memberPiyush Nandanwar28 Apr '11 - 20:33 
Hi
 
This article is really really helpful for me...This is an amazing article.
But one thing I am trying to do is,I want to show the text of selected checkbox values on combobox.Like,if text apple is selected,I want to display apple on combobox and if multiple checkboxes are selected I want to show has values on it.
 
Can anybody suggest anything for how to achieve it??
GeneralRe: How to display text on this multiselect combobox??memberFrank W. Wu29 Apr '11 - 4:28 
Hi Piyush,
It is not practical to display all the selected items in a small area. In real projects, I usually make the ComboBox like a square button, and use an ItemsControl to display the selected items underneath.
GeneralRe: How to display text on this multiselect combobox??memberJohnny J.2 May '11 - 3:27 
That depends on the situation. I agree with Piyush. I would also like to see the selected values shown in a textbox.
Why can't I be applicable like John? - Me, April 2011
-----
Beidh ceol, caint agus craic againn - Seán Bán Breathnach
-----
Da mihi sis crustum Etruscum cum omnibus in eo!
-----
Just because a thing is new don’t mean that it’s better - Will Rogers, September 4, 1932

GeneralRe: How to display text on this multiselect combobox??memberFrank W. Wu2 May '11 - 6:47 
I have updated my article mainly for displaying selected items. Let me know if you have any thoughts.
QuestionRe: How to display text on this multiselect combobox??memberShimmy Weitzhandler6 Mar '12 - 18:16 
Hi!
 
I also read your article and it was very helpful indeed.
However, in my scenario, the ComboBox is used in a DataGrid cell template, and I want to show ALL the selected values when the drop-down is closed.
One idea the crossed my mind is simply having a 'fake' data template when the drop down is closed, and it will bind (perhaps thru a converter) to the ItemsSource or binding object and concatenate the selected values.
 
The question is, since there is no DataTemplate.Triggers in Silverlight, how to make two templates and select between each of them based on ComboBox.IsDropDownOpen?
 
Thanks
Shimmy

GeneralHelpful instruction, thanks you!membermg80s11 Jan '11 - 3:11 
Your article describes exactly what I'm trying to do (without success until now). Thank you for a lot of saved time!
QuestionHow to make this a control?memberrvk12 Feb '10 - 8:29 
Frank, How do I make this a control so I can reuse it for several comboboxes.
 
Thanks
AnswerRe: How to make this a control?memberFrank W. Wu16 Feb '10 - 3:35 
One of the setbacks of XAML is that the code sometimes is not reusable. The cause is the hard-coding of the property name, event handler, etc. and it’s hard to pass in a variable. To make the concept (not code) reusable, you need to rewrite the style and template in C# so that you can pass in the variable.
GeneralRe: How to make this a control?memberrvk17 Feb '10 - 6:19 
Frank
 
One more thing: it seems ambiguous to display the selected items in the collapsed ComboBox, which is designed to display a single item.
 
The combobox is not displaying anything when it is collapsed. Any reason why?
 
Thanks
GeneralRe: How to make this a control?memberFrank W. Wu18 Feb '10 - 4:52 
You are right, the selected items is not a single item anymore. In the real app, I use an icon instead of showing text.
GeneralRe: How to make this a control?memberrvk18 Feb '10 - 5:39 
Frank,
 
Sorry to bother you. How do I get the SelectedItems from the ListBox in the ComboBox style in C# code? I am trying to find help on VisualTreeHelper and Find methods but not getting anywhere. Any help is appreciated.
 
Thanks
GeneralRe: How to make this a control?memberFrank W. Wu18 Feb '10 - 11:14 
To get the data, you should retrieve it from the model, not UI. Getting data from UI is the hard way because SelectedItems doesn’t support data binding. And separate UI from data is a widely accepted good practice.
QuestionRe: How to make this a control?memberDave290930 May '11 - 16:46 
Anyone have suggestions on how we might change the style to make it work more like a custom control? In particular, I'd really like to be able to use things like the DisplayMemberPath value instead of the hard-coded property in the template. I've tried a handful of things, and can't seem to find anything that works. Any thoughts?
 
Thanks,
 
Dave
AnswerRe: How to make this a control?memberFrank W. Wu31 May '11 - 7:03 
Dave,
 
You can replace the ListBox with your own UserControl. In that way, you can “Combo” anything in the ComboBox, and handle your stuff entirely in your UserControl. Use the ComboBox as a container; and no other coupling.
 
Hope this helps.
 
Frank
GeneralRe: How to make this a control?memberDave290931 May '11 - 15:30 
Thanks Frank,
 
I think I've got that part. What I can't figure out is how to get a hold of properties of the 'parent' comboBox from within the template. I'd like to be able to turn over the combobox style to other devs, but allow them to use something like DisplayPathMember= for cases where their dataItems don't match up with the hard-coded Name/IsSelected/etc. that I seem to be stuck with. I've tried a couple of things with relative/template bindings and not found anything that works - though it's probably not worth writing a full-blown custom control.
 
-Dave
GeneralAdditional functionality for yours Multiple-Selection ComboBoxmemberMember 38063051 Feb '10 - 15:46 
Frank,
I like your article. Unfortunately for me, yours ComboBox has only part of functionality which I need.
 
First, I need to pass list of key and value pairs.
Second, I would like to see keys for selected values displayed as comma delimiter string in controls TextBox.
 
I was able to do first part.
I'm not sure how to approach second issue.
 
Any ideas and/or code examples very appreciated.
 
Thanks
GeneralRe: Additional functionality for yours Multiple-Selection ComboBox [modified]memberFrank W. Wu3 Feb '10 - 6:48 
There are many approaches to do that. An easier way is to always display the strings, but change the foreground color or toggle the Visibility according to the selection. You can use a ValueConverter to do this. HTH.
 
modified on Wednesday, February 3, 2010 4:36 PM

QuestionHow to access SelectionChanged event ?memberMember 388387417 Nov '09 - 19:44 
This post was really helpful.
 
The SelectionChanged event of the combo box doesnt get called when any check box is selected.
Then how can i extract this SelectionChanged event for the check box??
AnswerRe: How to access SelectionChanged event ?memberMember 388387417 Nov '09 - 22:18 
I was able to find out the way.
 
Add Click event for the check box in the CheckBoxListBoxItemStyle style.
 

 

 

private void CheckBox_Click(object sender, RoutedEventArgs e)
{
 
MessageBox((System.Windows.Controls.ContentControl)(e.OriginalSource)).Content.ToString());
}
GeneralRe: How to access SelectionChanged event ?memberFrank W. Wu18 Nov '09 - 15:49 
The CheckBox overlaps the ListBoxItem in the z-order. So it is difficult to click the ListBoxItem, which will trigger the original SelectionChanged event. On the other hand, it is a better practice to use data binding to archive loosely-coupling rather than using event handler.
GeneralRe: How to access SelectionChanged event ?memberjsnsoft1 Jun '11 - 0:11 
am new to this, plz explain me how?
GeneralRe: How to access SelectionChanged event ?memberjsnsoft1 Jun '11 - 0:10 
HEy can u plz elaborate me how did u do this.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 13 Mar 2013
Article Copyright 2009 by Frank W. Wu
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid