65.9K
CodeProject 正在变化。 阅读更多。
Home

CheckBox ComboBox 扩展 ComboBox 类及其项

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (123投票s)

2007年10月29日

CPOL

5分钟阅读

viewsIcon

1266132

downloadIcon

60036

一篇关于扩展了 ComboBox.Items 的 CheckBox ComboBox 控件的文章。

Screenshot - CheckBoxComboBox.jpg

引言

我需要一种方法来最小化筛选器控件所需的空间,同时最大化提供给用户的筛选器功能。实现这一目标的一种方法是用一个 CheckBoxComboBox 控件替换一组组合框(Grouped Box)的 CheckBox 控件。网上有几个 CheckBoxComboBox 控件,但我找到并测试过的所有控件都缺少一些东西。

一些控件通过仅在 Popup 中绘制复选框来模拟复选框,这意味着复选框的行为不像复选框那样,并且在用户进行更多选择之前就会导致 Popup 关闭。其他控件没有对普通的 ComboBox 进行特殊化处理,因此您会丢失 ComboBox 控件及其 Items 的现有功能,例如,您无法再将控件绑定到自定义 DataSource。

这个 CheckBoxComboBox 结合了标准的 .NET ComboBox 和真实的 CheckBox,它通过创建一个 ComboBox.Items 的包装器来实现这一点(它也没有使用 CheckBoxListBox)。这里还有一点值得一提的是,它还使用了 Lukasz Swiatkowski 提供的精妙的 PopUp 解决方案,您可以在 这里 找到。这解决了一些自定义 PopUp 问题,例如焦点从所有者窗体移除、大小调整能力、定位等。确实值得一看。

背景

CheckBoxComboBox 提供了一个 CheckBoxItems 属性,其中包含列表中显示的 CheckBox,以及一个指向它链接到的 ComboBox Item 对象的链接。此外,它还有一个 CheckBoxCheckedChanged 事件,该事件将 CheckBox 元素的 CheckedChanged 事件冒泡回 ComboBox。

Using the Code

使用代码很简单,您可以像平常一样填充 ComboBox,无论是手动填充 Items 还是链接到 DataSource。

但是,请务必考虑以下几点,特别是如果您要绑定到该控件。您可能目前有一个对象列表,您希望在 CheckBoxComboBox 弹出列表中列出这些对象供用户选择。该选择需要一个 bool 属性,当绑定时,该属性将被设置回绑定对象。该对象真的关心它是否在某个地方被选中吗?如果您不仅想选择对象,还想添加额外的显示信息,例如有多少可用对象,会发生什么?所有这些额外信息都会不必要地使您的对象混乱。因此,我包含了一个额外的类,您可以使用它来解决这个问题。它基本上接受您的对象列表,并添加一个 Selection 和 Count 属性供您操作,而无需将这些属性添加到您的现有对象中,而这些属性实际上并不相关。然后,您可以轻松地扩展这些属性或更改其行为,而不会影响您当前的类。(它是独立且干净的。)下面的代码演示了它与自定义 List<T> 和 DataTable 的用法,但唯一的要求是 IEnumerable 类型。

#region POPULATE THE "MANUAL" COMBO BOX

cmbManual.Items.Add("Item 1");
cmbManual.Items.Add("Item 2");
cmbManual.Items.Add("Item 3");
cmbManual.Items.Add("Item 4");
cmbManual.Items.Add("Item 5");
cmbManual.Items.Add("Item 6");
cmbManual.Items.Add("Item 7");
cmbManual.Items.Add("Item 8");

#endregion

上面的代码示例非常直接,八个字符串对象像往常一样添加到 ComboBox 中。

#region POPULATED USING A CUSTOM "IList" DATASOURCE

_StatusList = new StatusList();

_StatusList.Add(new Status(1, "New"));
_StatusList.Add(new Status(2, "Loaded"));
_StatusList.Add(new Status(3, "Inserted"));
_StatusList.Add(new Status(4, "Updated"));
_StatusList.Add(new Status(5, "Deleted"));

cmbIListDataSource.DataSource = 
    new ListSelectionWrapper<status />(_StatusList);
cmbIListDataSource.ValueMember = "Selected";
cmbIListDataSource.DisplayMemberSingleItem = "Name";
cmbIListDataSource.DisplayMember = "NameConcatenated";

#endregion

在此摘录中,一个 List<Status> 对象被绑定到列表。请注意,它不是直接绑定的,我使用了 ListSelectionWrapper<T> 来为我处理选择,因为我不想修改我现有的“可重用” Status 类。

#region POPULATED USING A DATATABLE

DataTable DT = new DataTable("TEST TABLE FOR DEMO PURPOSES");
DT.Columns.AddRange(
new DataColumn[]
    {
        new DataColumn("Id", typeof(int)),
        new DataColumn("SomePropertyOrColumnName", typeof(string)),
        new DataColumn("Description", typeof(string)),
    });
DT.Rows.Add(1, "AAAA", "AAAAA");
DT.Rows.Add(2, "BBBB", "BBBBB");
DT.Rows.Add(3, "CCCC", "CCCCC");
DT.Rows.Add(3, "DDDD", "DDDDD");

cmbDataTableDataSource.DataSource =
    new ListSelectionWrapper<DataRow>(
    DT.Rows,
    // "SomePropertyOrColumnName" will populate the Name 
    // on ObjectSelectionWrapper.
    "SomePropertyOrColumnName" 
    );
cmbDataTableDataSource.DisplayMemberSingleItem = "Name";
cmbDataTableDataSource.DisplayMember = "NameConcatenated";
cmbDataTableDataSource.ValueMember = "Selected";

#endregion

在第三个示例中,我也使用了 ListSelectionWrapper<T>,因为我的表没有 Selection 列。但请注意,我没有包装 DataTable,而是包装了 Rows,它们是 IEnumerable。在初始化包装器时,我指定了“SomePropertyOrColumnName”,因为包装器在对象上使用 ToString(),而 DataRow 上的 ToString() 通常会得到“System.Data.DataRow”,而不是您想要显示的实际文本。您可以为此 Property 指定器提供一个 PropertyDescriptor 或普通属性,以防您不想让它们使用 ToString()。这可能很有用。

如果您想知道哪些项目被选中,您有两个选择:

  1. 使用 ComboBox 上的 CheckBoxItems 属性,它是一个列表,包装了 ComboBox.Items 列表中的每个项。CheckBoxComboBoxItem 类是一个标准的 CheckBox,因此您要查找的 bool 值包含在 Checked 中。
  2. 或者,如果您存储了 ListSelectionWrapper<T> 的引用,您可以使用它来访问绑定列表的 Selected 属性。
if (ComboBox.CheckBoxItems[5].Checked)
    DoSomething();

OR

if (StatusSelections.FindObjectWithItem(UpdatedStatus).Selected)
    DoSomething();

如果 CheckBoxComboBox 对您来说没有意义或看起来没有必要,请考虑以下过滤器作为实际示例。

此处显示的 ComboBox 替换了其下方的 Group Box。

Screenshot - CheckBoxComboBox_2_small.jpg

这个控件没有空间了。

Screenshot - CheckBoxComboBox_3.jpg

关注点

我最初是一名忠实的 Delphi 开发人员,所以使用这个控件让我对 C# 和 .NET 学到了很多。ComboBox 一直是,并且仍然是一个难以定制的控件,而且定制它所花费的时间可能会比您预期的要多。

以下来源列表可能会帮助您尝试,并且还包含指向其他 CheckBox ComboBox 控件的链接。

  1. 简单的弹出控件
  2. 具有高级下拉功能的自定义组合框
  3. 带复选框的 OwnerDraw ComboBox

历史

2007-11-06

  • 将弹出框的边框和持续时间更改为零,以修复黑色闪烁。我找不到在保留淡入淡出效果的同时解决此问题的办法。
  • 解决了 Checkbox 的选中状态在 CheckBoxCheckedChanged 事件被引发之前尚未分配回绑定属性的问题。如果 ComboBox 使用 DataSource,我现在会自己分配这个值。
  • 添加了一个类来包装现有列表,这样我就不需要在一个不需要的地方添加不必要的 Selected 属性。这个 List 包装器确实支持 IBindingList 源,所以如果您在列表中实现了该接口,这个包装器将自动与您的列表保持同步。但它根本不是必需的。
  • Selection 包装器也有助于处理计数。
  • 更改了 CheckBoxItems,以便在访问属性时进行同步,从而可以在显示弹出窗口之前初始化 Checked 值。

2007-11-22

  • 在 ComboBox 上添加了一个子属性 CheckBoxProperties,它允许您更改 Checkbox 的外观,例如 Flat、Text Alignment 等。
© . All rights reserved.