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

AdvComboBox - 版本 2.1

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (45投票s)

2002年7月17日

CPOL

14分钟阅读

viewsIcon

663042

downloadIcon

11402

用户可以调整下拉窗口大小的组合框。可以是标准样式或平面样式。

Sample Image - CAdvComboBox

最新消息!

本文的源代码和相关 bug 现在可以在 Google Code 找到:http://code.google.com/p/advcombobox/

我把它放在那里,这样每个人都可以报告 bug 并帮助我开发这个控件。我自己没有时间修复所有问题,所以如果你想帮忙,请发送邮件至 mathias.tunared@gmail.com,我将把你添加到项目中。我将继续在这里的 CodeProject.com 更新此页面,以发布每个新版本。这个控件已经存在很久了,仍然被使用,所以我认为是时候进行更新了。

AdvComboBox 文章索引

   引言
   安装
   函数
   Notifications
   样式
   未来升级
   技巧与提示
   改进和 bug 修复

2.1 版本有什么新内容

我真的很厌倦不得不在 InitDialog 函数中添加所有项目,这在同一页面上有多个 CAdvComboBox 时会非常烦人。现在,此类将在字符串表中查找与 CAdvComboBox 控件在资源编辑器中的 ID 相同的条目。演示项目已升级以显示此功能。更多信息已在 安装 部分添加。如果要添加另一个字符串表条目,只需调用新函数 LoadString(UINT nID),其中 nID 指的是字符串表条目。

在此版本中,我添加了代码,以便 CAdvComboBox 可以与宏 RUNTIME_CLASS 一起使用。由 Krishna 请求。

我还添加了缺失的功能,可以通过按 Alt-Down 或 Alt-Up 来打开下拉窗口。这是 Thomas Freudenberg 请求的。

引言

我开始开发一个控件,我将在另一个带有 CListBox 中组合框的程序中使用。我想要的第一项标准是组合框应该是平坦的(没有阴影)。我找了一段时间,找不到合适的,所以只好自己创建一个。当我开始处理平面组合框时,我想到了另一个很酷的功能,会很好用。你曾多少次想要 IE 地址栏的相同功能。当你输入地址,IAutoComplete COM 对象就开始工作。太棒了!……或者不是。我想要我的组合框具有相同的功能,特别是可调整大小的下拉窗口。所以,回到绘图板。经过许多漫长的夜晚和几种不同的方法,我成功了。

ComboBox 并没有继承 MFC 的 CComboBox,主要是因为我想能够调整下拉窗口的大小。相反,我创建了一个看起来与 MFC 的 CComboBox 几乎相同的 CWnd 对象。棘手的部分是让下拉窗口的反应与 CComboBox 相同。首先,下拉窗口必须是桌面的一个子窗口,然后是鼠标和键盘捕获的棘手问题。那个问题的解决方案是有一个窗口,其中包含一个列表框和一个滚动条。CListBox 类使用的滚动条不能使用,因为存在捕获问题。

在开发过程中添加了更多功能;下拉窗口中的已选中项目和已禁用项目。

下面是类结构的图示。

Class overview
Picture 1

AdvComboBox 的下拉窗口是自动调整大小的样式,即它会计算显示列表中所有项目所需的大小。
因此,结果是一个组合框,其行为与 MFC 的 CComboBox 几乎相同。除了将资源从标准组合框更改为自定义控件外,您不应更改任何已在使用中的代码。我在 W2K 和 XP 上进行了测试。现在我转向 CodeProject 的会员和其他人来帮助我测试和查找 bug,也许还能添加一些新功能。

安装

安装索引
   创建一个新项目
   升级您的当前项目
   在 MFC DLL 中使用 CAdvComboBox

本文包含三种将 CAdvComboBox 类实现到您的项目中的方法。一种描述了新项目的操作流程,第二种描述了如何升级您当前的项目,最后一种描述了如何在 MFC DLL 中实现 CAdvComboBox

创建一个新项目

  1. 使用 App Wizard 创建一个对话框应用程序。
    下面的解释都基于您将应用程序命名为 AdvCBDemo
  2. 将以下文件插入到您的工作区

    AdvComboBox.h
    AdvComboBox.cpp
    DropWnd.h
    DropWnd.cpp
    DropListBox.h
    DropListBox.cpp
    DropScrollBar.h
    DropScrollBar.cpp
    VisualStylesXP.h
    VisualStylesXP.h

  3. 在您的 AdvCBDemoDlg.h 文件中包含 AdvComboBox.h 文件
  4. 在对话框中添加一个自定义控件
    在资源编辑器中,使用工具栏中的自定义控件工具在对话框中放置一个新的自定义控件。输入控件的属性,如下图所示。

    CAdvComboBox 自定义控件类名是 AdvComboBoxCtrl,它在 AdvComboBox.h 中定义。

    Custom control properties
    Picture 2

    设置不同样式的说明
     组合框样式  属性窗口中的值
     CBS_DROPDOWN  0x_____002
     CBS_DROPDOWNLIST  0x_____103
     CBS_AUTOHSCROLL  0x_____043
     CBS_SORT  0x_____103

    这意味着,如果您希望您的组合框具有 CBS_DROPDOWNCBS_AUTOHSCROLLCBS_SORT 样式,则样式值应以 142 结尾(如图 2 所示)。您也可以在文本模式下编辑资源文件并在此处添加样式。资源编辑器随后会将您选择的样式转换为此值。

    以下样式尚未实现

       CBS_DISABLENOSCROLL
       CBS_HASSTRINGS
       CBS_LOWERCASE
       CBS_NOINTEGRALHEIGHT
       CBS_OEMCONVERT
       CBS_OWNERDRAWFIXED
       CBS_OWNERDRAWVARIABLE
       CBS_SIMPLE
       CBS_UPPERCASE

  5. 在 AdvCBDemoDlg.h 头文件中,将以下公共成员变量添加到您的 CAdvCBDemoDlg 类中
    CAdvComboBox	m_ctlAdvCombo;
  6. 在 AdvCBDemoDlg.cpp 文件中添加以下 DXX_ 调用
    DDX_Control(pDX, IDC_ADV_COMBO, m_ctlAdvCombo);
    将其添加到 AFX_DATA_MAP 外部,以避免将来出现任何问题。
    现在,DoDataExchange(...) 函数应如下所示
    void CAdvCBDemoDlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    	//{{AFX_DATA_MAP(CAdvCBDemoDlg)
    		// NOTE: the ClassWizard will add DDX and DDV calls here
    	//}}AFX_DATA_MAP
    	DDX_Control(pDX, IDC_ADV_COMBO, m_ctlAdvCombo);
    }
    
  7. 编译并运行
    现在您应该可以测试包含 AdvComboBox 的对话框了。

    还有一项尚未实现的功能是如何从控件获取和设置数据。这将在下面描述。

  8. 从 AdvComboBox 设置和获取数据
    您可以像使用普通 CComboBox 一样使用相同的方式来获取和设置数据,除了一个小的区别。
    DDX_CBIndexDDX_CBString 应更改为 DDX_ACBIndexDDX_ACBString 才能工作。这些函数可以在 AdvComboBox.cpp 文件中找到。

    要添加获取和设置数据的功能,请在您的 AdvCBDemoDlg.h 文件中添加以下一个或两个变量

    int	m_nAdvCombo;
    CString	m_strAdvCombo;
    您还必须在 CAdvCBDemoDlg 的构造函数中初始化这些变量的值。将以下代码添加到 AFX_DATA_INIT 外部。
    m_nAdvCombo = -1;
    m_strAdvCombo = "";
    还将 DDX_ 调用添加到 AdvCBDemoDlg.cpp 文件中的 DoDataExchange(...) 函数中,位于您在步骤 6 中添加的 DDX_Control 之下。
    DDX_ACBIndex( pDX, IDC_ADV_COMBO, m_nAdvCombo );
    DDX_ACBString( pDX, IDC_ADV_COMBO, m_strAdvCombo );

    现在您应该可以像使用 MFC 的 CComboBox 一样使用 AdvComboBox 控件了。

    以下内容已添加到 2.1 版本中

  9. 使用字符串表条目填充 CAdvComboBox 列表。
    要将字符串表与此组合框一起使用,必须创建一个与控件在资源编辑器中的标识符相同的字符串表条目。在这种情况下,字符串表条目 ID 应为 IDC_ADV_COMBO。下面是您可以添加到项目中的此示例的图片(图 3)。

    在字符串表条目中,换行符 ('\n') 代表组合框列表中一个项目的结束。请参阅演示项目以获取有关如何实现的更多信息。

    Stringtable entry
    图 3

升级您的当前项目

此描述不如上面的详细。希望您有足够的知识来管理以下内容。
  1. 将以下文件插入到您的工作区

    AdvComboBox.h
    AdvComboBox.cpp
    DropWnd.h
    DropWnd.cpp
    DropListBox.h
    DropListBox.cpp
    DropScrollBar.h
    DropScrollBar.cpp
    VisualStylesXP.h
    VisualStylesXP.h

  2. 在您的头文件中包含 AdvComboBox.h 文件

  3. 更改类名
    在您的头文件中,将所有 CComboBox 变量定义重命名为 CAdvComboBox
  4. 替换资源组合框
    在资源编辑器中,将您当前的组合框替换为自定义控件,并设置属性,如下图 2 所示。当然,您可以更改自定义控件的样式。

    提示! 对您的自定义控件使用与组合框相同的 ID。这使得以后更容易,您不必更改所有事件(ON_CBN_SELCHANGE 等)的 ID,这些 ID 已正确分配给标准组合框。

  5. 更改 DDX_ 调用
    在您的 .cpp 文件中,将所有 DDX_CBIndexDDX_CBString 更改为 DDX_ACBIndexDDX_ACBString。如果您没有为您新的自定义控件设置与旧组合框相同的 ID,请更改这些调用中的 ID。
  6. 编译并测试
    您的项目现在应该可以工作了,除了 CAdvComboBox 的限制。

在 MFC DLL 中使用 CAdvComboBox

这只是一个关于如何在具有窗口(对话框)的 DLL 中使用此类以及这些窗口使用 CAdvComboBox 的简要描述。要注册该类,您需要更改一些代码。WNDCLASS 结构中的 HINSTANCE 必须是 DLL 的 HINSTANCE,而不是调用应用程序的 HINSTANCE
  1. 声明一个 CAdvComboBox 变量
    像这样创建类
    CAdvComboBox m_ctlAdvCombo( TRUE )
  2. 全局 HINSTANCE 变量
    您还必须为 DLL 项目添加一个全局 HINSTANCE 变量。
    将以下内容添加到 DLL 项目的 StdAfx.cpp 中,例如
    HINSTANCE g_hDLLInstance = NULL;
    然后,像这样在 StdAfx.h 文件中添加 extern 定义
    extern HINSTANCE g_hDLLInstance;
  3. 初始化全局 HINSTANCE
    在 DllMain 函数中设置 hDllInstance
    g_hDLLInstance = hInstance;
  4. 更改 CAdvComboBox 中的代码
    接下来是找到 AdvComboBox.cpp 文件中的 RegisterWindowClass() 函数。将函数修改如下
    BOOL CAdvComboBox::RegisterWindowClass()
    {
        WNDCLASS wndcls;
        HINSTANCE hInst;
        if( m_bInst )
        {
            hInst = g_hDLLInstance;
        }
        else
        {
            hInst = AfxGetInstanceHandle();
        }
    
        ASSERT( hInst != 0 );
    
        ...
    	
        return TRUE;
    }
    
  5. 导出 CAdvComboBox 类
    最后但同样重要的是,如果您想在 DLL 外部使用该类,则必须将其导出。请记住也要导出两个 DDX_ 函数。类定义
    class __declspec(dllexport) CAdvComboBox : public CWnd
    DDX_ 定义
    _declspec(dllexport) void AFXAPI DDX_ACBIndex( CDataExchange* pDX, 
                                                   int nIDC, int& index );
    _declspec(dllexport) void AFXAPI DDX_ACBString( CDataExchange* pDX, 
                                                    int nIDC, CString& value );

AdvComboBox 函数

CAdvComboBox 类中的函数与 MFC 的 CComboBox 中的函数相同。有关更多帮助,请查看 MSDN。
还有一些额外的函数可以使用。下面将进行描述
  • GetComboRect 
     描述 使用此函数检索组合框的位置和大小
     定义 CRect& GetComboRect()
     Returns AdvComboBox 的矩形
     
  • GetDefaultVisibleItems     2.0 版本新增 
     描述 返回下拉窗口的默认可见项目数。要设置默认数量,请使用 SetDefaultVisibleItems(int)
     定义 int GetDefaultVisibleItems()
     Returns 项目数。
     
  • GetItemChecked 
     描述 获取项目的选中状态
     定义 BOOL GetItemChecked( int nIndex )
     参数 nIndex
     列表中项目的索引
     Returns 如果项目 nIndex 已选中,则返回 TRUE,如果发生错误,则返回 CB_ERR
     
  • GetItemDisabled
     描述 获取项目的禁用状态
     定义 BOOL GetItemDisabled( int nIndex )
     参数 nIndex
     列表中项目的索引
     Returns 如果项目 nIndex 已禁用,则返回 TRUE,如果发生错误,则返回 CB_ERR
     
  • GetMinVisibleItems     1.2 版本新增
     描述 获取下拉窗口中的最小可见列表框项目数,然后下拉窗口将放置在组合框上方。
     定义 int GetMinVisibleItems()
     Returns 最小可见列表框项目的数量。
     
  • LoadString     2.1 版本新增
     描述 从字符串表中加载一个条目,并将该条目中的项目添加到下拉窗口列表中。
     定义 void LoadString( UINT nStringID )
     参数 nStringID
    要加载的字符串的 ID。
     Returns void
     
  • ModifyACBStyle
     描述 修改 CAdvComboBox 的样式。可用样式可以在这里找到。此函数仅处理 ACBS_ 样式。它的工作方式与 MFC 的 ModifyStyle 相同。
     定义 void ModifyACBStyle(UINT nRemoveStyle, UINT nAddStyle)
     参数 nRemoveStyle
     ACBS 样式要移除。
     参数 nAddStyle
     ACBS 样式要添加。
     Returns void
     
  • PointInWindow
     描述 CPoint 是否在组合框矩形内?
     定义 BOOL PointInWindow( CPoint ptScreenPoint )
     参数 ptScreenPoint
     这个点是否在组合框内。
     Returns 如果点在组合框窗口内,则返回 TRUE。
     
  • SetDefaultVisibleItems     2.0 版本新增
     描述 设置下拉窗口中默认可见的项目数。
     定义 void SetDefaultVisibleItems( int nItems = -1 );
     参数 nItems
     显示下拉窗口时要可见的项目数。如果样式应为自动大小,请将其设置为 -1。
     Returns void
     
  • SetItemChecked
     描述 将组合框中的项目设置为选中或取消选中。
     定义 void SetItemChecked( int nIndex, BOOL bChecked )
     参数 nIndex
     要设置的项目的零基索引。
     参数 bChecked
     要设置的项目的最新状态。
     Returns void
     
  • SetItemDisabled
     描述 将组合框中的项目设置为禁用或启用。
     定义 void SetItemDisabled(int nIndex, BOOL bDisabled)
     参数 nIndex
     要设置的项目的零基索引。
     参数 bDisabled
     要设置的项目的最新状态。
     Returns void
     
  • SetMinVisibleItems     1.2 版本新增
     描述 设置下拉窗口中的最小可见列表框项目数,然后下拉窗口将放置在组合框上方。默认值为五个项目。
     定义 void SetMinVisibleItems(int nMinItems)
     参数 nMinItems
     最小列表框项目的数量。
     Returns void
     
  • DDX_ACBIndex
     描述 使用此函数通过 MFC 的 UpdateData(...) 获取和设置数据
     定义 void AFXAPI DDX_ACBIndex( CDataExchange* pDX, int nIDC, int& index )
     
  • DDX_ACBString
     描述 使用此函数通过 MFC 的 UpdateData(...) 获取和设置数据
     定义 void AFXAPI DDX_ACBString( CDataExchange* pDX, int nIDC, CString& value )

下面是 CAdvComboBox 中实现的 CComboBox 函数列表。

  • AddString
  • DeleteString
  • FindString
  • FindStringExact
  • GetCount
  • GetCurSel
  • GetDroppedControlRect
  • GetDroppedState
  • GetEditSel
  • GetItemData
  • GetItemDataPtr
  • GetLBText
  • GetLBTextLen
  • GetTopIndex
  • InsertString
  • LimitText     1.2 版本新增
  • ResetContent
  • SelectString
  • SetCurSel
  • SetEditSel
  • SetItemData
  • SetItemDataPtr
  • SetTopIndex
  • ShowDropDown

以下 CComboBox 函数在 CAdvComboBox实现

  • Clear
  • CompareItem
  • 复制
  • 剪切
  • Dir
  • DrawItem
  • DeleteItem
  • GetDroppedWidth
  • GetExtendedUI
  • GetHorizontalExtent
  • GetItemHeight
  • GetLocale
  • MeasureItem
  • 粘贴
  • SetDroppedWidth
  • SetExtendedUI
  • SetHorizontalExtent
  • SetItemHeight
  • SetLocale

AdvComboBox 通知

CAdvComboBox 向父窗口发送以下通知,就像 MFC 的 CComboBox 一样。
  • CBN_CLOSEUP
  • CBN_DROPDOWN
  • CBN_EDITCHANGE
  • CBN_EDITUPDATE
  • CBN_KILLFOCUS
  • CBN_SELCHANGE
  • CBN_SELENDCANCEL
  • CBN_SELENDOK
  • CBN_SETFOCUS

以下通知未发送。

  • CBN_DBLCLK
    此消息仅在组合框样式为 CBS_SIMPLE 时发送,而 CAdvComboBox 不支持此样式。
  • CBN_ERRSPACE
    此控件尚未内置内存管理,我也不确定是否会这样做。太无聊了!

AdvComboBox 样式

CAdvComboBox 具有自己的样式。此处将进行描述。
  • ACBS_FLAT
    使组合框变平。使用此样式,组合框将在资源中的自定义控件矩形的边界处绘制。
  • ACBS_STANDARD
    使用此样式,组合框将像普通的组合框一样绘制,即 3D 外观。
  • ACBS_CHECKED
    要使下拉窗口包含一个可勾选列表,请使用此样式。
  • ACBS_AUTOAPPEND
    AdvComboBox 将自动在下拉窗口项目中搜索与 AdvComboBox 的编辑框中输入的文本最匹配的项目。如果找到,它将把剩余的文本附加到编辑框中。
  • ACBS_AUTOSUGGEST
    在编辑框中输入文本时,AdvComboBox 将在下拉窗口中显示一个与输入文本匹配的列表。

CAdvComboBox 支持以下 CComboBox 样式

  • CBS_AUTOHSCROLL
  • CBS_DROPDOWN
  • CBS_DROPDOWNLIST
  • CBS_SORT

以下 CComboBox 样式受支持

  • CBS_DISABLENOSCROLL
  • CBS_HASSTRINGS
  • CBS_LOWERCASE
  • CBS_NOINTEGRALHEIGHT
  • CBS_OEMCONVERT
  • CBS_OWNERDRAWFIXED
  • CBS_OWNERDRAWVARIABLE
  • CBS_SIMPLE
  • CBS_UPPERCASE

未来升级

  • .NET 兼容

技巧与提示

  • 使用 ClassWizard
    使用 MFC 的 CComboBox 创建您的项目,这样 ClassWizard 就可以帮助您实现通知,例如 ON_CBN_SELCHANGE。当您创建了组合框的所有事件后,切换到 CAdvComboBox。这是实现此类的最简单快捷的方法。
  • 将样式设置为平面
    OnInitDialog() 中添加对 ModifyACBStyle(...) 的调用,如下所示
    m_ctlAdvCombo.ModifyACBStyle( 0, ACBS_FLAT );
  • Windows 主题兼容
    AdvComboBox 与主题兼容,在我测试的范围内。如果您发现任何颜色或其他方面有问题,请告诉我。在告诉我之前,请再与 CComboBox 进行比较。

改进和 bug 修复

版本 2.1

  • ID 相同的字符串表条目将填充列表。
  • 已添加函数 LoadString(UINT)
  • RUNTIME_CLASS 现在可以使用了。
    在此版本中,我已将代码添加到 CAdvComboBox 可以与宏 RUNTIME_CLASS 一起使用。由 Krishna 请求。
  • 按 Alt+Up/Down 可以打开下拉窗口。
    添加了通过按 Alt-Down 或 Alt-Up 打开下拉窗口的缺失功能。这是 Thomas Freudenberg 的一个请求。

版本 2.0

  • 已添加函数 GetDefaultVisibleItems()
  • 已添加函数 SetDefaultVisibleItems(int)
  • 已添加 CAdvComboBox 样式 ACBS_AUTOAPPEND
  • 已添加 CAdvComboBox 样式 ACBS_AUTOSUGGEST
  • CAdvComboBox 现在也与 CommCtrl 6.0 manifest 兼容。
    我使用了 David Yuheng ZhaoCVisualStylesXP 类作为基础,但我添加了一些功能,例如从 DLL 获取的函数现在是静态函数,以及一个检查 CommCtrl DLL 版本以查看我们是否能够使用视觉样式的函数。如果未使用 manifest,即使在 WinXP 上运行,此函数也会告知您。
  • 添加了两个新文件:VisualStylesXP.h 和 VisualStylesXP.cpp

版本 1.21

  • 修复了使用 Create(...) 时的 bug
    尝试使用 Create(...) 函数创建 CAdvCombBox 类并且样式为 CBS_DROPDOWN 时会出现 bug。感谢您找到这个 bug。

版本 1.2

  • 实现了使用鼠标滚轮滚动下拉窗口。
  • CComboBox 函数 LimitText 现在已实现。
  • 已移除对 Windows 定义 _WIN32_WINNT 的需求。
  • 下拉窗口在控件上方
    如果到屏幕底部的距离不够,下拉窗口将显示在组合框上方。默认的最小列表框项目数为五个,但可以通过调用 CAdvComboBox 类中的 SetMinVisibleItems(int) 函数来更改此值。

版本 1.1

  • 现在可以禁用该控件。
  • 当下拉窗口打开时切换到另一个程序会关闭下拉窗口。
  • 修正了 Win2K 下的一个小绘图错误。
© . All rights reserved.