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

ComboBox 列表控件宿主

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (7投票s)

2009 年 1 月 9 日

CDDL

4分钟阅读

viewsIcon

63864

downloadIcon

1847

一篇演示如何将列表控件托管到标准 ComboBox 中的文章。轻松创建你自己的 CheckedList ComboBox 等。

CheckedComboBoxPic.JPG

引言

本文允许你创建自己完全自定义的列表控件,以组合框下拉样式显示,同时尽可能保留内在/标准控件的功能。下拉窗口的行为支持多选操作而不关闭下拉窗口,并且不使用任何 API 调用。基础组合框实现使用 ToolStripDropDown 组件来替换基础组合框内置的下拉窗口功能,并使用内部的 ToolStripControlHost 组件来托管第三方控件。基础实现允许开发人员通过实现各种不同的控件来扩展功能。理想情况下,仅使用以下类型的列表控件。

  • ListBox
  • CheckedListBox
  • MonthCalendar
  • 树视图
  • DataGridView

背景

本文的代码源于一个我接到的请求。我的任务是“给我们一个像 Team System 中使用的那个复选框组合框”。经过一番大量的搜索引擎搜索,我没有找到任何稳定的可用格式能完全满足我的需求。所有的示例都使用了 API 调用——而且是糟糕的调用。我有一个原则:当需要使用 API 调用时……就不要用!于是,我做了次优选择,并重新发明了轮子,这也促成了我一直想写的第一个文章。向多产的文章作者致敬——我实在无法想象你们哪来的时间。总之,下面就开始了。

Using the Code

使用代码非常简单。在 Windows Forms 应用程序中,我们有一个 Form1。控件/基础文件夹包含一个基础组合框实现和一个构建在基础组合框实现之上的 CheckedListComboBox 实现。只需将源代码解压到你附近的某个文件夹,打开它并在 VS 2008 中构建项目。选择 Items 属性来创建列表,然后点击运行按钮。仅此版本,我们的控件将不支持数据绑定,仅仅是因为标准的 Windows CheckedListBox 控件不支持数据绑定——我将在下一篇文章中解决这个功能。

基础实现

容器选择

首先,我们必须隐藏基础下拉窗口的大小,使其看起来不可见,然后准备好将提供必要功能的内部容器,特别是

  • 01 个标准的 ToolStripDropDown 组件,这是一个非常棒的小组件,它提供了许多“标准”功能。一个特别有用的小技巧是,它的行为允许鼠标单击功能来选择列表中的项目,同时保持下拉列表打开以进行后续选择操作。这是 C++ 中才能妥善实现的功能。我找到的示例代码/文章都使用了 API 调用——除了不稳定之外,这也是一种尝试访问底层 C++ 功能的行为。
  • 01 个标准的 ToolStripControlHost,它几乎是一个万能瑞士军刀,可以灵活地包装任何其他控件,并且易于使用,几乎不需要编写代码。
namespace CheckedComboTest.Controls.Base
{
    public abstract class ComboBoxEx : ComboBox
    {

        #region Declarations & Constructors

        private ToolStripDropDown tsdd = new ToolStripDropDown();
        private ToolStripControlHost tsch = null;

        private int dropDownHeight;

        new public event EventHandler DropDown;
        new public event EventHandler DropDownClosed;

        public ComboBoxEx()
            : base()
        {
            base.DropDownHeight = 1;
        }

        #endregion

实例化

在任何组件设计中,内部容器实例化的方式和时机总是很重要的。这就是为什么我喜欢使用基础的 OnHandleCreated 方法来构建内部组件,因为我的代码执行时,我知道基础控件已经被实例化,并且我的控件实例已经连接了句柄。

#region Base Overrides/Overloads

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    //  our handle has now been officially created; build internals
    BuildInternalHost();
}

#endregion

在内部,我们配置我们的下拉容器,并调用抽象的 OnCreateHost() 方法,强制继承层次结构中的任何实现,在创建宿主容器的同时提供一个用于托管的控件。

//  call extended implementations to provide a guest control
tsch = new ToolStripControlHost(OnCreateHost());

我们只需要提供自己的下拉高度功能,然后就可以着手实现自定义的 ComboBoxEx 了。

自定义实现

我们的自定义实现需要一个继承自我们 ComboBoxEx 基础组件的类,对于我们的示例,让我们使用 CheckedListBox 控件。

public class CheckedListComboBox : Base.ComboBoxEx
{

    #region Declarations & Constructors

    private CheckedListBox checkedListBox = new CheckedListBox();

    public CheckedListComboBox()
        : base()
    {
    }

    #endregion

实际上,我们所有的实现只需要通过 OnCreateHost() 函数返回一个列表控件实例即可,就像这样……

#region Base Overrides/Overloads

protected override Control OnCreateHost()
{
    return checkedListBox;
}

……然后我们就可以从工具箱中将其拖放到窗体上并运行项目。结果的下拉窗口乍一看相当正常,特别是与标准的 ComboBox 相比,直到我们实际单击以显示下拉窗口。我们的自定义下拉窗口具有下拉阴影效果和匹配的边框颜色,所有这些都是微软标准提供的。

最后,用 CheckedListBox 控件公开的那个替换标准的 Items 属性, voilà

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor,
    System.Design, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[Localizable(true)]
new public CheckedListBox.ObjectCollection Items
{
    get
    {
        return checkedListBox.Items;
    }
}

关注点

实际上,我们唯一创建的是一个中间层代码,允许多个组件相互插入。我们甚至不需要“破解”任何绘图例程,甚至不需要使用单个 API 调用。可用的控件是简单的构建块,虽然扭曲和“破解”东西很有趣,但很容易过度,尤其是在没有清晰的框架扩展思路的情况下。

在我的下一篇文章中,我将扩展我的自定义实现,以涵盖自定义实现上的数据绑定。

历史

首发!

© . All rights reserved.