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

带复选框的 OwnerDraw ComboBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (24投票s)

2007年5月24日

CPOL

3分钟阅读

viewsIcon

280346

downloadIcon

15638

带复选框的 OwnerDraw ComboBox

Screenshot - DropDown.jpg

引言

我需要向列表视图添加过滤功能。列表视图已经有一个ToolStrip,我可以向其中添加适当的控件。我决定使用带有CheckBox的下拉ComboBox来打开和关闭项目是最节省空间和最直观的实现方式。但是,这需要一个自绘的ComboBox。幸运的是,自 2.0 起 .NET Framework 包含一个辅助类CheckBoxRenderer,只要给定正确的参数,它就会将复选框渲染到下拉列表中。

Using the Code

第一步是子类化System.Windows.Forms.ComboBox

public partial class CheckComboBox : ComboBox
{
    public CheckComboBox()
    {
        this.DrawMode = DrawMode.OwnerDrawFixed;
        ...
    }

    ...
}

请注意,我设置了DrawMode属性来告诉ComboBox,我们打算自己渲染下拉列表项。下一步是定义一个类来包含我们的下拉列表项数据并维护状态。这是一个简单的类

public class CheckComboBoxItem
{
    public CheckComboBoxItem( string text, bool initialCheckState )
    {
        _checkState = initialCheckState;
        _text = text;
    }

    private bool _checkState = false;
    public bool CheckState
    {
        get { return _checkState; }
        set { _checkState = value; }
    }

    private string _text = "";
    public string Text
    {
        get { return _text; }
        set { _text = value; }
    }

    public override string ToString()
    {
        return "Select Options";
    }
}

然后我们将委托连接到ComboBoxDrawItem事件

this.DrawItem += new DrawItemEventHandler(CheckComboBox_DrawItem);

并按如下方式实现它

void CheckComboBox_DrawItem( object sender, DrawItemEventArgs e )
{
    if (e.Index == -1)
    {
        return;
    }

    if( !( Items[ e.Index ] is CheckComboBoxItem ) )
    {
        e.Graphics.DrawString( 
            Items[ e.Index ].ToString(), 
            this.Font, 
            Brushes.Black, 
            new Point( e.Bounds.X, e.Bounds.Y ) );
        return;
    }

    CheckComboBoxItem box = (CheckComboBoxItem)Items[ e.Index ];

    CheckBoxRenderer.RenderMatchingApplicationState = true;
    CheckBoxRenderer.DrawCheckBox( 
        e.Graphics, 
        new Point( e.Bounds.X, e.Bounds.Y ), 
        e.Bounds, 
        box.Text, 
        this.Font, 
        ( e.State & DrawItemState.Focus ) == 0, 
        box.CheckState ? CheckBoxState.CheckedNormal : 
            CheckBoxState.UncheckedNormal );
}

在此委托中,我们做的第一件事是验证我们正在渲染的项目是否已添加为CheckComboBoxItem。如果不是,我们将其渲染为简单的字符串。否则,我们从Items集合中获取相应的CheckComboBoxItem(使用DrawItemEventArgs.Index属性)。然后我们调用CheckBoxRenderer.DrawCheckBox()方法,传入我们想要渲染CheckBoxGraphics对象,以及位置、大小、文本、字体、焦点和选中状态。

结果是它会将我们的下拉列表项渲染成复选框的样子

Screenshot - DropDown.jpg

接下来,我们希望在选择其中一个项目时切换选中状态。所以我们连接另一个委托,但这次连接到SelectedIndexChanged事件

this.SelectedIndexChanged += 
    new EventHandler( CheckComboBox_SelectedIndexChanged );

并按如下方式实现它

void CheckComboBox_SelectedIndexChanged( object sender, EventArgs e )
{
    CheckComboBoxItem item = (CheckComboBoxItem)SelectedItem;
    item.CheckState = !item.CheckState;
    ...
}

这允许我们切换下拉列表中的复选框,但不允许此控件的用户知道发生了任何事情。所以我们还添加一个公共事件,以通知控件的用户下拉列表中项目的选中状态发生了更改

public event EventHandler CheckStateChanged;

并且我们在上面切换状态时触发此事件,因此完整的方法如下所示

void CheckComboBox_SelectedIndexChanged( object sender, EventArgs e )
{
    CheckComboBoxItem item = (CheckComboBoxItem)SelectedItem;
    item.CheckState = !item.CheckState;
    if (CheckStateChanged != null)
        CheckStateChanged(item, e);
}

使用控件

要使用该控件,您只需将其添加到您选择的容器,然后像这样向其添加项目

checkComboBox1.Items.Add(new CheckComboBox.CheckComboBoxItem("One", true));
checkComboBox1.Items.Add(new CheckComboBox.CheckComboBoxItem("Two", true));
checkComboBox1.Items.Add(new CheckComboBox.CheckComboBoxItem("Three", true));

要获取用户更改了选中状态的通知

this.checkComboBox1.CheckStateChanged += 
    new EventHandler(this.checkComboBox1_CheckStateChanged);

可以像这样处理

private void checkComboBox1_CheckStateChanged(object sender, EventArgs e)
{
    if (sender is CheckComboBox.CheckComboBoxItem)
    {
        CheckComboBox.CheckComboBoxItem item = 
            (CheckComboBox.CheckComboBoxItem)sender;
        ... 
    }
}

Screenshot - DropDownTest1.jpg

完成的示例应用程序。


Screenshot - DropDownTest2.jpg

下拉列表。

关注点

有一个让我烦恼的地方,我确信有比我提出的更好的解决方案。我希望控件的TextBox部分包含固定文本且不可编辑。因此,作为权宜之计,我将其初始化为字符串“Select Options”并覆盖CheckComboBoxItem 类的ToString() 方法以始终返回相同的字符串。这样,无论用户最后选择哪个项目,该字符串永远不会改变。但这并没有使其不可编辑。

如果时间允许,我想进行许多添加

  • 正确对齐文本,使其左对齐(需要找出如何计算复选框位图的宽度才能正确执行此操作)
  • 在下拉列表的顶部添加带有分隔符的“全选”和“全不选”项
  • 允许其他控件类型进入下拉列表(例如 - 单选按钮)

历史

  • 2007 年 5 月 24 日:发布原始版本
© . All rights reserved.