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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (55投票s)

2008年11月20日

CPOL

4分钟阅读

viewsIcon

698784

downloadIcon

34064

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

引言

此控件源于我需要创建一个CheckedListBox控件,该控件在选定项可见时不会占用太多窗体空间。尽管已经有一些使用所有者绘制控件(即绘制ComboBoxListBox部分)的示例,但我并不完全满意,因为存在一些不足之处,因此我提出了一个新版本,即CheckedComboBox,它具有以下功能:

  • 只读文本部分,显示选定的项。
  • 每个选定的项都以string的形式出现在文本部分,每个string由我可以定义的自定义分隔符分隔。
  • 列表部分保持打开状态,直到用户完成选择。.
  • 全选/取消全选所有项。
  • 通过键盘显示/隐藏列表部分。
  • 尽可能模仿.NETComboBox
  • 从Visual Studio工具箱中使用它(作为自定义控件)。

实现

CheckedComboBox派生自ComboBox。为了避免列表部分在用户完成选择之前消失的问题,我决定捕获DropDown事件并显示我自己的列表部分,该列表部分由一个Form组成,该Form的客户端区域完全被一个CheckListBox填充。FormCheckListBox都是我自己的自定义版本,因为我需要覆盖一些默认行为。

因此,我的自定义列表部分要么在DropDown事件中出现,要么在用户按下向下箭头键且CheckedComboBox具有焦点时出现。我重写了OnDropDown()OnKeyDown()OnKeyPress()方法来

  1. 显示下拉列表,并
  2. 防止任何键盘输入,以使文本部分只读。
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的常规行为相同。

为了实现这一点,我不得不捕获FormDeactivate事件(用于鼠标点击行为)和键盘事件(用于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设置为CheckedUncheckedIndeterminate。请注意,用户无法从用户界面将项状态设置为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/setCheckState
    CheckState GetItemCheckState(int index)

    SetItemCheckState(int index, CheckState state)
  • 为下拉列表添加了水平滚动条,当任何项的长度超过下拉列表的宽度时,该滚动条会显示出来。
  • 使下拉列表“不可见”(它曾经出现,因为它是一个Form)。
一个带有CheckedListBox作为下拉框的ComboBox - CodeProject - 代码之家
© . All rights reserved.