WPF 中实现多选 ComboBox 控件

84 阅读2分钟

前言

在 WPF 开发中,ComboBox 控件默认只支持单选模式。然而,在实际项目开发中,我们经常需要实现多选功能,例如选择多个书籍、标签或分类等。本文将介绍如何通过自定义 ComboBoxItemTemplate 并结合数据绑定机制,实现一个支持多选的 ComboBox 控件,并将选中的项显示在输入框中。

正文

1、定义基础数据模型

首先定义一个简单的 Book 类,用于表示书籍信息:

public class Book
{
    public string Name { get; set; }
}

然后,将 ComboBox.ItemsSource 绑定到该类的集合上:

<ComboBox ItemsSource="{Binding Path=Books}">
    <ComboBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:Book}">
            <StackPanel>
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

此时,ComboBox 将正常显示书籍名称列表。

2、实现多选功能

为了实现多选,我们需要为每个 Book 对象添加一个 IsChecked 属性。为此,创建一个封装类 BookEx

public class BookEx : ObservableObject
{
    public Book Book { get; private set; }

    private bool _isChecked;

    public bool IsChecked
    {
        get
        {
            return _isChecked;
        }
        set
        {
            if (_isChecked != value)
            {
                _isChecked = value;
                RaisePropertyChanged("IsChecked");
            }
        }
    }

    public BookEx(Book book)
    {
        Book = book;
    }
}

接着,将 ComboBox.ItemsSource 绑定到 BookEx 集合,并修改 ItemTemplate,加入 CheckBox 控件以实现多选:

<ComboBox ItemsSource="{Binding Path=BookExs}">
    <ComboBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:BookEx}">
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding IsChecked}" />
                <TextBlock Text="{Binding Book.Name}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

此时已可实现多选操作,但 ComboBox 输入框不会显示选中项。

3、显示选中项

为了解决“选中项无法显示”的问题,可以在 ViewModel 中新增一个 SelectedText 属性,用于拼接并显示所有被选中的书籍名称。

首先定义 BookExs 集合并监听其变化:

private ObservableCollection<BookEx> _books;
public ObservableCollection<BookEx> BookExs
{
    get
    {
        if (_books == null)
        {
            _books = new ObservableCollection<BookEx>();

            _books.CollectionChanged += (sender, e) => 
            {
                if(e.OldItems != null)
                {
                    foreach (BookEx bookEx in e.OldItems)
                    {
                        bookEx.PropertyChanged -= ItemPropertyChanged;
                    }
                }

                if(e.NewItems != null)
                {
                    foreach (BookEx bookEx in e.NewItems)
                    {
                        bookEx.PropertyChanged += ItemPropertyChanged;
                    }
                }
            };
        }

        return _books;
    }
}

private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if(e.PropertyName == "IsChecked")
    {
        BookEx bookEx = sender as BookEx;

        if(bookEx != null)
        {
            IEnumerable<BookEx> bookExs = BookExs.Where(b => b.IsChecked == true);

            StringBuilder builder = new StringBuilder();

            foreach (BookEx item in bookExs)
            {
                builder.Append(item.Book.Name + " ");
            }

            SelectedText = builder == null ? string.Empty : builder.ToString();
        }
    }
}

最后,在 XAML 中设置 ComboBox.Text 绑定和 IsEditable="True"

<ComboBox Text="{Binding SelectedText}" IsEditable="True" ItemsSource="{Binding Path=BookExs}">
    <ComboBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:BookEx}">
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding IsChecked}" />
                <TextBlock Text="{Binding Book.Name}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

现在,当选中多个书籍后,它们的名称会自动拼接显示在 ComboBox 的文本框中。

总结

通过本文介绍的方法,我们成功实现了在 WPF 中创建一个支持多选的 ComboBox 控件。

核心思路是使用 CheckBoxBookEx 类对原始数据进行封装,并通过绑定机制实现状态同步和选中项的展示。这种方法结构清晰、易于维护,适用于各种需要多选下拉控件的业务场景。

关键词

WPF、ComboBox、CheckBox、多选、数据绑定、ItemTemplate、ObservableCollection、BookEx、IsChecked、IsEditable、Text绑定、MVVM模式、UI交互设计、XAML布局、动态更新、SelectedItem、事件监听、属性通知、数据模型、界面交互

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!