Home > OS >  Multiselect WPF combo box
Multiselect WPF combo box

Time:02-04

I have a WPF combo box which ItemsSource is ObservableCollection<User>, where User has string Name and bool IsChecked.

Also I have

<ComboBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <CheckBox IsChecked="{Binding IsChecked}" Width="20" />
            <TextBlock Text="{Binding Name}" />
         </StackPanel>
     </DataTemplate>
</ComboBox.ItemTemplate>

, which nicely shows check boxes before each name and allows me to check/uncheck users.

What I need is to make combo box selected item to show not the selected user but all checked usernames separated by comma, ideally (if resultant string is too long) with ellipsis in the middle, i. e. "Alice, Bart...mew, John".

Possible?

CodePudding user response:

A little bit tricky to implement, but certainly can be done.

First of all you'll want to replace the drop-down bar/button with a TextBlock, so you'll need to modify the control's template. Place the edit cursor anywhere inside the parent ComboBox declaration so that the control appears in the Properties panel at the bottom right. In properties, find Miscellaneous -> Template at the bottom, then click on the little down-arrow to the right and select "Convert to new resource". This will template out the control so that you can start editing it.

Next, find the ToggleButton inside the ControlTemplate. You'll want to template that out with a TextBlock instead:

<ToggleButton x:Name="toggleButton" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}">
    <ToggleButton.Template>
        <ControlTemplate TargetType="ToggleButton">
            <TextBlock Text="{Binding Names}" Background="{TemplateBinding Background}" />
        </ControlTemplate>
    </ToggleButton.Template>
</ToggleButton>

Notice there's a binding here to "Names", which is a new property you'll need to add to the view model.

The next change is to also add a binding to your CheckBox Command property called "NamesChangedCommand" which will be called whenever the user changes the state of a CheckBox:

<CheckBox IsChecked="{Binding IsChecked}" Width="20" Command="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.NamesChangedCommand}" />

Then back in your view model, all you have to do is implement this command and have it generate the new name list. Here's how you'd do that in MVVM Toolkit:

    [RelayCommand]
    public void NamesChanged()
    {
        this.Names = String.Join(", ",
            this.Items
                .Where(item => item.IsChecked)
                .Select(item => item.Name));
    }

    [ObservableProperty]
    private string names = "";

Result:

enter image description here

CodePudding user response:

I was going to suggest the xceed checkcombobox

https://github.com/xceedsoftware/wpftoolkit/wiki/CheckComboBox

But I notice it's not free for commercial use any more. You could build something of your own based on their code though. https://github.com/xceedsoftware/wpftoolkit/blob/master/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.Toolkit/CheckComboBox/Implementation/CheckComboBox.cs

The xaml will be in that repo as well.

  • Related