以 CheckedListBox 作为下拉列表的组合框






4.87/5 (55投票s)
以 CheckedListBox 作为下拉列表的组合框

引言
此控件源于我需要创建一个CheckedListBox
控件,该控件在选定项可见时不会占用太多窗体空间。尽管已经有一些使用所有者绘制控件(即绘制ComboBox
的ListBox
部分)的示例,但我并不完全满意,因为存在一些不足之处,因此我提出了一个新版本,即CheckedComboBox
,它具有以下功能:
- 只读文本部分,显示选定的项。
- 每个选定的项都以
string
的形式出现在文本部分,每个string
由我可以定义的自定义分隔符分隔。 - 列表部分保持打开状态,直到用户完成选择。.
- 全选/取消全选所有项。
- 通过键盘显示/隐藏列表部分。
- 尽可能模仿.NET
ComboBox
。 - 从Visual Studio工具箱中使用它(作为自定义控件)。
实现
CheckedComboBox
派生自ComboBox
。为了避免列表部分在用户完成选择之前消失的问题,我决定捕获DropDown
事件并显示我自己的列表部分,该列表部分由一个Form
组成,该Form
的客户端区域完全被一个CheckListBox
填充。Form
和CheckListBox
都是我自己的自定义版本,因为我需要覆盖一些默认行为。
因此,我的自定义列表部分要么在DropDown
事件中出现,要么在用户按下向下箭头键且CheckedComboBox
具有焦点时出现。我重写了OnDropDown()
、OnKeyDown()
和OnKeyPress()
方法来
- 显示下拉列表,并
- 防止任何键盘输入,以使文本部分只读。
protected override void OnDropDown(EventArgs e) {
base.OnDropDown(e);
DoDropDown();
}
private void DoDropDown() {
if (!dropdown.Visible) {
Rectangle rect = RectangleToScreen(this.ClientRectangle);
dropdown.Location = new Point(rect.X, rect.Y + this.Size.Height);
int count = dropdown.List.Items.Count;
if (count > this.MaxDropDownItems) {
count = this.MaxDropDownItems;
} else if (count == 0) {
count = 1;
}
dropdown.Size = new Size(this.Size.Width,
(dropdown.List.ItemHeight + 1) * count);
dropdown.Show(this);
}
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.KeyCode == Keys.Down) {
OnDropDown(null);
}
// Make sure that certain keys or combinations are not blocked.
e.Handled = !e.Alt && !(e.KeyCode == Keys.Tab) &&
!((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right) ||
(e.KeyCode == Keys.Home) || (e.KeyCode == Keys.End));
base.OnKeyDown(e);
}
protected override void OnKeyPress(KeyPressEventArgs e) {
e.Handled = true;
base.OnKeyPress(e);
}
现在,列表部分(它是一个Form
)将保持打开状态而不关闭(实际上是隐藏),直到用户按下Esc(Escape键)取消任何更改,按下Enter接受所有更改,或者用鼠标点击列表外部的任何位置,这与ComboBox
的常规行为相同。
为了实现这一点,我不得不捕获Form
的Deactivate
事件(用于鼠标点击行为)和键盘事件(用于Esc、Enter行为)。此外,如果用户在列表部分具有焦点时按下Del(Delete键)或Shift + Del,所有项将分别被取消选中/选中,这提供了一个有用的(至少对我来说 :-))键盘快捷键。请注意,为了区分Deactivate
消息是来自框架(即鼠标点击)还是来自键盘(我正在捕获按键,所以可以控制),使用了一个自定义的CCBoxEventArgs
类。
protected override void OnDeactivate(EventArgs e) {
base.OnDeactivate(e);
CCBoxEventArgs ce = e as CCBoxEventArgs;
if (ce != null) {
CloseDropdown(ce.AssignValues);
} else {
CloseDropdown(true);
}
}
Using the Code
代码以演示项目形式提供,您可以运行并查看示例用法。要在代码中使用CheckedComboBox
,您只需要一个文件:CheckedComboBox.cs。尽管我没有将其制作成功能齐全的Visual Studio控件(带所有炫酷功能),但一旦编译项目,CheckedComboBox
应该出现在Visual Studio **ToolBox**面板的**Components**下,您可以将其拖放到**Designer**中的Form
上。
或者,您也可以非常轻松地通过手动编写如下代码来声明并创建一个CheckedComboBox
实例:
private CheckedComboBox ccb = new CheckedComboBox();
// If more than 5 items, add a scroll bar to the dropdown.
ccb.MaxDropDownItems = 5;
// Make the "Name" property the one to display, rather than the ToString()
// representation of the item.
ccb.DisplayMember = "Name";
// Set a separator for how the checked items will appear in the Text portion.
ccb.ValueSeparator = ", ";
要添加项,请使用Items
集合...
private string[] coloursArr = { "Red", "Green", "Black",
"White", "Orange", "Yellow",
"Blue", "Maroon", "Pink", "Purple" };
for (int i = 0; i < coloursArr.Length; i++) {
CCBoxItem item = new CCBoxItem(coloursArr[i], i);
ccb.Items.Add(item);
}
...其中CCBoxItem
是我创建的一个简单的测试类(它在演示项目中):
public class CCBoxItem {
private int val;
public int Value {
get { return val; }
set { val = value; }
}
private string name;
public string Name {
get { return name; }
set { name = value; }
}
public CCBoxItem() {
}
public CCBoxItem(string name, int val) {
this.name = name;
this.val = val;
}
public override string ToString() {
return string.Format("name: '{0}', value: {1}", name, val);
}
}
您还可以以编程方式选中一个项,或将其CheckState
设置为Checked
、Unchecked
或Indeterminate
。请注意,用户无法从用户界面将项状态设置为Indeterminate
,只能以编程方式设置(除非您想处理ItemCheck
通知并添加自己的逻辑)。
// Check first item (index == 0)
ccb.SetItemChecked(0, true);
// Set the CheckState of the 2nd item in the list to Indeterminate.
ccb.SetItemCheckState(1, CheckState.Indeterminate);
在您的应用程序中,您可能最感兴趣的事件是下拉部分关闭时(DropDownClosed
)以及项的选中状态即将更改时(ItemCheck
)。您可以像往常一样注册事件处理程序并处理事件。
请注意,DropDownClosed
事件直接从ComboBox
可用,而ItemCheck
是我从CheckListBox
中提供的。
// Add the handlers to the CheckedComboBox
this.ccb.DropDownClosed += new System.EventHandler(this.ccb_DropDownClosed);
ccb.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.ccb_ItemCheck);
// Handler implementation
private void ccb_DropDownClosed(object sender, EventArgs e) {
txtOut.AppendText("DropdownClosed\r\n");
txtOut.AppendText(string.Format("value changed: {0}\r\n", ccb.ValueChanged));
txtOut.AppendText(string.Format("value: {0}\r\n", ccb.Text));
// Display all checked items.
StringBuilder sb = new StringBuilder("Items checked: ");
// Checked items can be found via the CheckedItems property.
foreach (CCBoxItem item in ccb.CheckedItems) {
sb.Append(item.Name).Append(ccb.ValueSeparator);
}
sb.Remove(sb.Length-ccb.ValueSeparator.Length, ccb.ValueSeparator.Length);
txtOut.AppendText(sb.ToString());
txtOut.AppendText("\r\n");
}
private void ccb_ItemCheck(object sender, ItemCheckEventArgs e) {
CCBoxItem item = ccb.Items[e.Index] as CCBoxItem;
txtOut.AppendText(string.Format("Item '{0}' is about to be {1}\r\n",
item.Name, e.NewValue.ToString()));
}
在上面的代码中,您可以看到选中的项可以通过CheckedItems
属性访问。同样,CheckedIndices
将返回一个选中的索引集合。这两个集合都对应于.NET的CheckedListBox
集合,所以没有什么不熟悉的。
历史
在2.0版本中,我包含了一些小的修复/增强,如下所示:
- 公开了
SetItemChecked(int index, bool isChecked)
方法,以便您可以以编程方式将项设置为已选中。 - 与上面相关的是,还添加了另外两个方法来
get
/set
CheckState
。
CheckState GetItemCheckState(int index)
和
SetItemCheckState(int index, CheckState state)
- 为下拉列表添加了水平滚动条,当任何项的长度超过下拉列表的宽度时,该滚动条会显示出来。
- 使下拉列表“不可见”(它曾经出现,因为它是一个
Form
)。