Table of content
Introduction
The MultiSelectionComboBox
is like a normal ComboBox
, but instead of limit the selection to one item, the user is allowed to select multiple items.
The user interface
No. | Description |
---|---|
01 | Displays the selected item(s), or, if the control is in editable mode, a TextBox representing a concatenated string |
02 | This button opens or closes the DrowDown (No. 03) |
03 | A DropDown which displays all available items. Selected items are highlighted (blue background in the sample image) |
Common Properties
This control has all properties which are known from the normal ComboBox
and in addition the following properties.
Property | Type | Description |
---|---|---|
AcceptsReturn | bool | Gets or sets if the TextBox accepts return (editable mode only) |
DisabledPopupOverlayContent | object? | Gets or sets the content to render above the DropDown when the DropDown is disabled (more) |
DisabledPopupOverlayContentTemplate | DataTemplate? | Gets or sets the DataTemplate to render above the DropDown when the control has custom text (more) |
DropDownFooterContent | object? | Gets or sets the content of the DropDown footer |
DropDownFooterContentStringFormat | string? | Gets or sets the string format of the DropDown footer |
DropDownFooterContentTemplate | DataTemplate? | Gets or sets the DataTemplate for the DropDown footer content |
DropDownFooterContentTemplateSelector | DataTemplateSelector? | Gets or sets the DataTemplateSelector for the DropDown footer content |
DropDownHeaderContent | object? | Gets or sets the content of the DropDown header |
DropDownHeaderContentStringFormat | string? | Gets or sets the string format of the DropDown header |
DropDownHeaderContentTemplate | DataTemplate? | Gets or sets the DataTemplate for the DropDown header content |
DropDownHeaderContentTemplateSelector | DataTemplateSelector? | Gets or sets the DataTemplateSelector for the DropDown header content |
EditableTextStringComparision | StringComparison | Gets or Sets the StringComparison that is used to check if the entered text matches to the SelectedItems |
HasCustomText | bool | Indicates if the text is user defined. This property is read-only |
InterceptKeyboardSelection | bool | Gets or sets if the user can select items from the keyboard, e.g. with the ▲ ▼ Keys. This property is only applied when the SelectionMode is Single . |
InterceptMouseWheelSelection | bool | Gets or sets if the user can select items by mouse wheel. This property is only applied when the SelectionMode is Single . |
IsDropDownFooterVisible | bool | Gets or sets if the footer in the DropDown is visible |
IsDropDownHeaderVisible | bool | Gets or sets if the header in the DropDown is visible |
ObjectToStringComparer | ICompareObjectToString? | Gets or sets a function that is used to check if the entered text matches an object that should be selected (more) |
OrderSelectedItemsBy | SelectedItemsOrderType | Gets or sets how the SelectedItems should be sorted (more). |
SelectedItemContainerStyle | Style? | Gets or sets the Style that is applied to the selected items |
SelectedItemContainerStyleSelector | StyleSelector? | Gets or sets the StyleSelector that is applied to the selected items |
SelectedItems | IList? | Gets a read-only list which holds the currently selected items. |
SelectedItemsPanelTemplate | ItemsPanelTemplate? | Gets or sets the template that defines the Panel that controls the layout of the selected items. |
SelectedItemStringFormat | string? | Gets or sets how to format the selected items if they are displayed as strings. |
SelectedItemTemplate | DataTemplate? | Gets or sets the DataTemplate used to display the SelcetedItems |
SelectedItemTemplateSelector | DataTemplateSelector? | Gets or sets the DataTemplateSelector for the selected items |
SelectionMode | SelectionMode | Gets or sets the SelectionMode |
SelectItemsFromTextInputDelay | int | Gets or Sets the delay in milliseconds to wait before the selection is updated during text input. If this value is -1 the selection will not be updated during text input. Note: You also need to set an ObjectToStringComparer to get this to work. |
Separator | string? | Gets or sets the separator which will be used if the MultiSelectionComboBox is editable. |
StringToObjectParser | IParseStringToObject? | Gets or sets a parser-class that implements IParseStringToObject (more) |
Styles
MultiSelectionComboBox-Styles
MahApps ships two build in styles for this control.
The default style (MahApps.Styles.MultiSelectionComboBox
) wraps the selected items, if not all fits into a single line:
If you want a single line you can use MahApps.Styles.MultiSelectionComboBox.Horizontal
which will show a horizontal ScrollViewer
, if not all items fit into the available space.
ItemContainer-Styles
MahApps ships two build in styles for the item container visible when the DropDown
is open.
The default style (MahApps.Styles.MultiSelectionComboBoxItem
) looks like the one from the normal ComboBox
.
If you want to present CheckBoxes
to the user you can use the MahApps.Styles.MultiSelectionComboBoxItem.CheckBox
style:
SelectedItemContainer-Styles
Also for this MahApps ships two build in styles. Please note that these are only applied if IsEditable=false
.
The default one wraps each item in a nugget like container:
If you want to provide an option to remove from the selected items you can use MahApps.Styles.MulitselectionComboBoxSelectedItem.Removeable
which will add a delete button next to each item:
Selection
SelectionMode
The MultiSelectionComboBox
supports all SelectionModes
which are known from other controls with multi selection support (for example ListBox
).
SelectedItemsOrder
This enum
defines how the selected items should be presented to the user. The following values are possible:
Value | Description |
---|---|
SelectedOrder | Displays the selected items in the same order as they were selected |
ItemsSourceOrder | Displays the selected items in the same order as they are stored in the ItemsSource |
Text processing
Separator
The Separator
is used to concatenate the selected items into one single string. if the text comes from user input, the Separator
is also used to split the input string into fragments which will be used to select the requested items.
Note:
Take care which Separator to use. Ideally it should not be a valid (sub)-string of your items, as this may mess up with selection.
Custom text
If the text entered into the editable TextBox
does not match the string
representation of the SelectedItems
, the DropDown
will show an overlay above the Items
and will disable selection. This will prevent loss any user input. The property HasCustomText
will be set to true
in this case.
Build-in overlay
Customize the overlay
If you don't like the build-in overlay, you can roll your own as shown below.
<mah:MultiSelectionComboBox ItemsSource="{Binding Animals}"
IsEditable="True"
Text="CustomText" >
<mah:MultiSelectionComboBox.DisabledPopupOverlayContentTemplate>
<DataTemplate>
<!-- Note: mah:MultiSelectionComboBox.ClearContentCommand will reset the text to match the SelectedItems representation if executed -->
<Button Background="{DynamicResource MahApps.Brushes.Accent4}"
BorderThickness="0"
Command="{x:Static mah:MultiSelectionComboBox.ClearContentCommand}">
<TextBlock Text="🤷"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="50" />
</Button>
</DataTemplate>
</mah:MultiSelectionComboBox.DisabledPopupOverlayContentTemplate>
</mah:MultiSelectionComboBox>
The result will look like this:
Selecting items from text input
If the control is editable and you have set a ObjectToStringComparer
, the MultiSelectionComboBox
will try to select the items from the user input. The build-in comparer will compare the input string fragments with the objects string representation while taking the StringComparison
into account.
Basic Example
<!--
- The Separator must be defined for automatic item selection.
- The SelectItemsFromTextInputDelay is optional. If it is set to -1,
it means the items will be selected only after LostFocus.
- The EditableTextStringComparision is optional.
- We use the build in comparer (DefaultObjectToStringComparer), but you can also use your own.
-->
<mah:MultiSelectionComboBox ItemsSource="{Binding Animals}"
SelectionMode="Multiple"
IsEditable="True"
Separator=", "
SelectItemsFromTextInputDelay="200"
EditableTextStringComparision="OrdinalIgnoreCase"
ObjectToStringComparer="{mah:DefaultObjectToStringComparer}" />
Custom ObjectToStringComparer Example
You can use any class that implements the ICompareObjectToString
interface to compare your items to the entered text. For this example lets assume you have the following class User
where you want to select the items by Userame
or MailAddress
.
public class User
{
public string Username {get; set;}
public string Name {get; set;}
public string GivenName {get; set;}
public string MailAddress {get; set;}
}
We can now implement the interface as shown below:
public class MyUserToStringComparer : ICompareObjectToString
{
// This function is expected to be implemented. Returns true if the item matches, otherise false.
public bool CheckIfStringMatchesObject(
string? input, // The text fragment to test
object? objectToCompare, // The item to test for match
StringComparison stringComparison, // The string comparison to use
string? stringFormat) // The string format to use
{
// we cast the object to compare to a user.
var user = objectToCompare as User;
// if the input is null or the user is null we return false.
if (input is null || user is null)
{
return false;
}
// If either the Username or the MailAddress matches the provided
// input string we return true, otherwise we return false.
return input.Equals(user.Username, stringComparison)
|| input.Equals(user.MailAddress, stringComparison);
}
}
Adding new items from text input
If the control is editable and you have set a StringToObjectParser
, the MultiSelectionComboBox
will try to create a new item if the item was not found (see Selecting items from text input). The build-in parser will try to use reflection to create a new item from the input string, but you can also provide your own implementation.
Basic Example
<mah:MultiSelectionComboBox ItemsSource="{Binding Animals}"
SelectionMode="Multiple"
IsEditable="True"
Separator=", "
SelectItemsFromTextInputDelay="200"
EditableTextStringComparision="OrdinalIgnoreCase"
ObjectToStringComparer="{mah:DefaultObjectToStringComparer}"
StringToObjectParser="{x:Static mah:DefaultStringToObjectParser.Instance}" />
Custom StringToObjectParser Example
You can use any class that implements the IParseStringToObject
interface to parse the user input to a new object. The interface has one member called TryCreateObjectFromString
. Let's assume we have a List<string>
called Animals
. The user can add new animals, but before adding a new animal, we want the user to confirm the input.
public class MyObjectParser : IParseStringToObject
{
// This function is expected to be implemented. Returns true if the item could be created, otherise false.
public bool TryCreateObjectFromString(
string? input, // The input string to parse
out object? result, // return the object here if successful, otherwise return null
CultureInfo? culture = null, // The culture which should be used to parse. This parameter is optional
string? stringFormat = null, // The string format to apply. This parameter is optional
Type? elementType = null) // The Type to which the input should be converted to. This parameter is optional
{
// if we got an empty string, we return false and set the result to null
if (string.IsNullOrWhiteSpace(input))
{
result = null;
return false;
}
// We ask the user for confirmation
if (MessageBox.Show(
$"Do you want to add \"{input}\" to the animals list?",
"Add Animal",
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// The user accepted this item. As our List contains only strings, we can just return the input string.
result = input;
return true;
}
else
{
result = null;
return false;
}
}
}