扩展(继承)的列表框和组合框控件版本






4.98/5 (18投票s)
列表框和组合框控件的扩展版本,允许为单个项目提供图像、字体和颜色——列表框支持多行/对齐制表符——组合框支持下拉列表鼠标事件/水平滚动条和自定义下拉按钮。
最后更新:2024年8月30日 晚上11:15 (EST)
(注意:如果您在2021年8月24日 上午1:00 EST 之前下载过此文件,请阅读下面的“警告!”部分。此外,如果图形设计器“拒绝”显示包含一个或两个这些控件的窗体,请阅读下面的“在宿主项目中使用此库”部分。)
引言
ExtListBox
和 ExtComboBox
分别继承自 ListBox
和 ComboBox
控件——并具有以下附加功能:
- 允许在单个列表项的文本之前(或代替文本)显示图像,以及设置单个项的文本和背景的字体和颜色。
- 允许为给定的列表项提供多行文本(只需在项的文本中嵌入回车/换行符序列)。
- 允许使用一个命令智能地对齐(以制表符分隔)列表项的列(仅限
ExtListBox
)。 - 允许自定义选定(高亮)项使用的文本和背景颜色。
- 允许捕获
ExtComboBox
下拉列表的鼠标事件并提供水平滚动条,以及自定义其下拉按钮的位图。 - 允许跟踪
ExtListBox
插入符号(caret)所在的项目,无论它是否被选中(高亮)。 - 允许基于显示字符串对列表项执行二进制搜索;在排序列表上可以使用
FindStringBinarySearch
代替FindString
和FindStringExact
。 - 允许设置列表,以便在列表更改时自动调整水平滚动范围和制表符对齐。
- 如果需要,允许在设计时设置列表初始内容中每个项的文本和所有图形属性。
最近更改
2024年8月30日 晚上11:15 (EST)
BindingContext
和 DataBindings
属性不再被隐藏(Shadowed),因为我发现它们不需要特殊处理。因此,您现在可以使用这些属性或 DataSource
属性将 ExtListBox
和 ExtComboBox
控件绑定到数据源。
2023年2月2日 (土拨鼠日!)
COMBOBOXINFO
结构(在 ExtComboBox.ExtComboBoxNativeWindow
嵌套类中)现在是一个类。同时,为控件的 ObjectCollection
对象提供了各种构造函数(类似于其中 AddRange
方法的重载)。
2023年2月1日 下午2:30 (EST)
ExtComboBox
类不再依赖 Win32 API SetWindowSubclass、RemoveWindowSubclass 和 DefWindowProc 来对下拉列表进行子类化;相反,它使用一个嵌套的 Private
类——ExtComboBoxNativeWindow
,该类继承自 NativeWindow
——以完全托管的方式处理子类化。下拉列表的4个鼠标事件仍然按原样工作;将编辑和下拉窗口句柄以及将消息信息转换为 MouseEventArgs
信息的所有关键代码都已转移到嵌套类中,以及检测控件的创建/销毁的主句柄。
此外,我修复了演示程序中的一个错误,以便鼠标状态报告过程对所有4个 DropDownMouse
xxxxx 事件(正如应该的那样)都触发,而不仅仅是 DropDownMouseMove
。当我移除然后重新添加控件到窗体上,但忘记在重构的 Handles
子句中包含所有4个事件时,就会出现这个错误!
9月24日 下午1:50 (EST)
我修正了一个拼写错误,该错误导致 ExtComboBox
属性 DropDownButtonRectangle
被错误地命名为 DropDownButtonRectange
(!!)。如果您已在代码中使用此拼写错误的属性,则在下载已更正的 DLL 后,请在所有使用它的地方添加“l”。
2002年9月13日
我终于添加了一个属性页,允许程序员在设计时填充任一控件的列表!!!我还修改了 ExtListBox.IntegerCollection
的 AlignTabs
方法,使其能够使用每列的最小必要制表符宽度(包括列间距),或者当制表符不够宽时简单地扩展制表符(之前的行为)。我还修改了 ExtListBox.HorizontalExtent
和 ExtComboBox.DropDownHorizontalExtent
,当设置为-1时(不再是瞬时值),水平滚动条的水平范围会在列表修改时自动调整,并添加了 ExtListBox.SpacingBetweenColumns
属性,以允许在列表修改时自动调整制表符对齐。此外,ItemHeight
即使在运行时设置为 0-255 之外的值,也会抛出异常,并且 ExtComboBox.IndexFromDropDownPoint
有一个额外的可选参数(在两个重载上)来指定是否自动滚动下拉列表到指定的坐标。最后,我修正了项目渲染和制表符对齐过程中的一些错误,并为各种集合类添加了一系列构造函数。
2022年4月12日 下午2:49 EST (非常重要!)
我修复了 ExtListBox
的 ItemHeight
属性中的一个错误,该错误导致控件在设置新值后无法反映该值(尽管仍在后台设置了项目高度)。对于两个控件,请参阅属性的新描述以及何时设置它的注意。
2021年12月28日
我修正了受保护的 OnCaretIndexChanged
方法声明中的一个错误,使其现在可以被重写。
2021年12月25日 (圣诞快乐!!!) 下午5:30 EST
隐藏的 ItemInfo 属性 DisplayString
和 ItemInfo 类的公共方法 ToString
都返回列表项的最终显示文本字符串,除非 ItemInfo 实例不是从实际列表中检索的(即,由宿主程序显式实例化),在这种情况下,它只是 Value.ToString
。
2021年12月22日 下午4:50 EST
我在两个控件中都添加了 FindStringBinarySearch
方法,允许使用二进制搜索来搜索已排序列表中的完全匹配或部分匹配的显示字符串;这对于非常大的列表很有用。我还处理了一些潜在的罕见错误情况。特别是,ItemInfo
的“隐藏” DisplayString
属性总是尽可能地解析对象的最终显示文本,并且解析 DisplayMember
和 ValueMember
属性信息的 ExtSupportModule
过程会解析声明存在歧义或不存在的属性的情况。
如果 DisplayMember
或 ValueMember
指定的属性对于列表对象不存在,则控件的行为就像没有指定属性一样——分别返回整个对象的 ToString
值或整个对象本身。如果属性隐藏了基类版本,则它依赖于派生类版本。如果在考虑了任何隐藏后,仍有两个或多个属性重载,则控件的行为就像没有指定属性一样(返回默认信息)。(在任何情况下,由于请求的属性有歧义或不存在,都不会向宿主程序抛出异常。)
2021年10月17日 (非常重要!)
我修复了一个导致在没有图像且 ImageSize
未设置为 Size.Empty 时控件崩溃的错误。
2021年8月24日 上午1:00 EST (非常重要!)
我修复了一个潜在的文件和文件夹故障,该故障可能——可能——导致您依赖一个不支持最新功能的过时 DLL。(请参阅“警告!”部分以修复您在2021年8月24日 上午1:00 EST 之前下载的情况!)此外,两个控件中的 SelectedItemInfo
属性不再是只读的。(这是我的错误!)将其设置为 ItemInfo
实例将,如果 ItemValue.Value
对象存在于列表中,则选择该条目并将图像、字体和颜色属性设置为您提供的实例。
2021年8月20日
我修复了演示程序中的一个错误,该错误导致在鼠标悬停在不带水平滚动条的组合框的最底部项目上时,工具提示不会显示。(请参阅文章末尾的 ShowExtComboBoxTooltip
代码示例进行修改。)
2021年6月23日
演示程序的小更新——它现在显示了如何在 ExtComboBox
的下拉列表中显示项目工具提示!!演示中的 ShowExtComboBoxTooltip
过程处理所有事情。请注意,如果坐标(相对于编辑部分或下拉框)被省略,则使用最新的坐标。如果包含了 AreForcingRedraw 且其值为 True,则即使鼠标和选择未移动,工具提示也会重绘;否则,仅当鼠标位置或选定索引更改时,工具提示才会重绘。演示还为 ExtListBox
提供了工具提示,指示当前插入符号索引。请注意,列表框使用 ToolTip
的 SetToolTip
方法(标准过程),而组合框必须依赖 Show
方法(显示默认的 5 秒!)。
2021年6月3日
在 ExtComboBox
的辅助句柄子类化中加入了异常处理,以确保即使在“DropDown”事件过程中未处理异常时,也会发生默认的消息处理;此外,使焦点矩形绘制逻辑更有效。
2021年5月22日
我修正了一个错误,该错误导致控件在 DropDownStyle
为 ComboBoxStyle.Simple 时,读取 DefaultDropDownButton
时崩溃,并在设置 CustomDropDownButton
时可能显示错误。(请参阅下面成员的更新描述。)
2021年5月20日 下午9:49 EST
我更新了两个控件的 SelectedValue
属性的代码,以便 SelectedValueChanged
事件在正确的情况下触发。此外,ExtListBox
控件现在包含一个 CaretIndex
属性和 CaretIndexChanged
事件,用于跟踪列表框插入符号所在的项目;这在 SelectionMode
为 MultiSimple 或 MultiExtended 时很有用。最后,我修正了 ExtSupportModule
模块的 DrawImageAndText
方法中的逻辑,以便当前插入符号下的项目具有正确的焦点矩形。
2021年5月5日
我使用了 Win32/SetWindowSubclass
(现在是嵌套的 NativeWindow
派生类——**见上文**)和 WndProc
子类化,以允许跟踪 ExtComboBox
下拉列表的鼠标事件(DropDownMouseMove
、DropDownMouseDown
、DropDownMouseUp
和 DropDownMouseWheel
)——以及利用它们提供的信息的属性和方法——为列表启用水平滚动条(DropDownHorizontalScrollbar
/DropDownHorizontalExtent
),并允许用自定义位图替换默认的下拉按钮。对下拉列表进行子类化是一件棘手的事情,需要“非托管”Win32
API,因为列表使用与整体控件不同的、不直接暴露的句柄。我还暴露了下拉和编辑部分句柄,以防您希望使用 Win32
和子类化进行其他扩展。这些新属性、方法和事件中的许多可能不经常使用,但它们可能很有用——特别是能够跟踪下拉列表上的鼠标。请注意,所有专门针对下拉列表的自定义成员在其名称中都包含“DropDown”。
2020年12月19日
我修复了一些关于没有类型规范(声明末尾没有“As
type”子句,导致某些属性被错误地定义为 Object
类型)的属性和函数,以及一些关于隐式类型转换的错误。因此,类库将能够正确地使用Option Strict进行编译(现在是这样);如果您一直在使用这些控件在一个编译了 Option Strict 的宿主应用程序中,并遇到有关类型转换的编译时错误,这应该可以解决这些问题!!!
2020年12月17日(文档修复)
不是功能更改或错误修复;但是——我错误地表示 DataSource
在设计时不可用。实际上是的。
2020年12月14日
一个新属性 AlignText
,允许文本部分项的起始位置在最宽图像的右侧之后开始,即使图像大小不是固定的(即,当 ImageSize
= Size.Empty 时)。
2020年12月10日
以前,控件不设计为“数据感知”;现在,支持 DataSource
、DisplayMember
和 ValueMember
属性,以及一个新方法 SetDataSource
,该方法允许使用图形信息与数据源进行绑定。但是,这些属性在扩展控件与基类控件中的实现方式有所不同。MyBase
的 DataSource
信息与派生控件的不同,并且 DisplayMember
和 ValueMember
属性与 MyBase
完全不使用。与基类控件一样,当 DataSource
不是 Nothing 时,宿主程序无法在列表中插入、删除或修改数据;但是——与基类控件不同——它可以修改列表项的图形信息,甚至可以设置 Sorted
和/或 DisplayMember
属性来更改显示项的顺序并由 Items
集合返回,而 DataSource
不是 Nothing。但是,通过引用 DataSource
属性返回的项的顺序不受影响——因此程序不应仅依赖整数索引来匹配列表项与数据源项。然而,使用 BindingContext
和 DataBinding
属性进行数据绑定仍然不受支持;这两个属性都被重写/隐藏为只读属性,返回Nothing。
最后,现在可以设置列表项的背景颜色,以及图像、字体和文本颜色;并且可以更改用于高亮项的文本/背景颜色。此外,DrawItem
和 MeasureItem
事件不再被“忽略”(抑制),从“宿主程序”的角度来看;然而,任何特定于这些控件的“默认”绘制或测量总是在宿主程序可以使用给定的事件进行附加显示格式化之前完成。
Using the Code
关于在填充列表时指定图形信息
ExtListBox
、ExtComboBox
及其 ObjectCollection
类的许多成员支持重载,允许宿主程序为正在添加到/分配给列表的项指定图像、字体、文本颜色和背景颜色信息。每个参数都是可选的,默认为父控件的 DefaultImage
、Font
、ForeColor
或 BackColor
属性。每个参数,如果已指定,可以是标量实例 Image
、Font
、Color
或 Color
——在这种情况下,给定类型的相同图形信息适用于所有指定的列表项——或者是这些类型的数组——在这种情况下,不同的图形信息将分配给每个指定的列表项。后一种情况,数组会自动调整大小以匹配要添加/分配的项目数量,而与数组元素对应的项目(这些元素缺失或为 Nothing)将获得上述默认值。(数组元素必须与按索引枚举的列表项顺序相同。)
请注意,数组只能与用于添加/分配范围的成员一起指定,例如 Items.AddRange
或 SetDataSource
——不能与用于添加单个项的成员一起指定,例如 Items.Add
或 Items.Insert
——因为为单个项分配多个图像/字体/颜色没有意义。最后,有重载用于使用 ItemInfo
类型实例或数组/集合,以便在调用成员之前预先指定图形信息——从而避免使用额外的参数或默认值。
使用基类/祖先类类型变量/参数时的成员使用
由于控件及其相关类重写和/或隐藏了许多基类成员——其功能与 MyBase
版本非常不同——因此在将这些类的实例分配给键入为基类或祖先类的变量/参数时,应非常小心。最好的做法是先将变量/参数(使用 CType
或 DirectCast
)转换为派生版本,然后再调用可能被重写或隐藏的成员,以免调用基类版本,从而产生不良后果。例如
Public Function GetDisplayMember(lb As ListBox) As String ' get DisplayMember value for list box If TypeOf lb Is ExtListBox Then ' extended control Return _ DirectCast(lb, ExtListBox).DisplayMember Else ' regular control Return lb.DisplayMember End If End Function
在宿主项目中使用此库(请阅读!)
我现在添加了一个属性页/编辑器,允许程序员在设计时设置和修改列表的初始内容——这是一个以前无法实现的功能,在两个控件的基类中都存在。但是,由于我不知道如何编写“生成代码的代码”,因此该功能与基类中的实现方式不同,它依赖于一个“代理属性”(InitialListItems
),该属性出现在属性窗口中,显示为“Items”(它修改的是这个属性),并且只能在运行时设置为空列表(即,在窗体的 InitializeComponents
方法中)。由于此属性的类类型性质(不知道为什么这是一个问题),您在 Visual Studio 中尝试从一个项目/解决方案切换到另一个使用这些控件的(预先存在的)项目/解决方案时可能会遇到问题。
当您要定位的项目/解决方案是全新的(从头创建)、预先存在的且是 VS 当前会话中加载的第一个,或者预先存在的但尚未在任何窗体上使用这些控件中的任何一个时,您应该不会遇到问题。但是,如果您从一个项目/解决方案切换到一个已经在其中一个或多个窗体上使用这些控件的预先存在的项目/解决方案,那么您不能只是关闭第一个项目/解决方案然后打开包含这些控件的那个;相反,您必须完全退出 VS,然后重新启动它并加载目标项目/解决方案。否则,任何使用任一控件的窗体都将无法在图形设计器中成功显示!如果您计划将一个使用这些控件的预先存在的项目添加到(尚未包含这些控件的)一个预先存在的解决方案中,那么请将项目添加到解决方案,保存解决方案,退出 VS(完全退出),然后重新启动它并加载已修改的解决方案。
对于由此带来的任何不便,我们深表歉意。
自定义这些控件(请阅读!)
如果您希望自定义此类库,除非您在使用图形设计器对属性页窗体(frmListCollection
)或其任何控件进行任何更改,否则不应有任何问题。在这种情况下,您必须在进行任何更改后执行以下操作:打开窗体的资源文件(ListCollection.resx),并在“Other”类别下,删除“elbList.InitialListItems
”的行(在出现的警告时响应“Yes”);然后打开其设计器代码文件(ListCollection.Designer.vb),并删除或注释掉以“elbList.InitialListItems =
”开头的行。否则,库项目将无法生成!这都与该窗体使用了在同一库中定义的两个控件之一的事实有关。
ExtListBox 和 ExtComboBox 控件的自定义成员
ExtListBox
和 ExtComboBox
分别继承自 ListBox
和 ComboBox
。下面是独有于这些控件,或者功能与其基类不同的属性、方法和事件。
* = 成员在基类控件中不可用
† = 属性在设计时不可用
属性
CaretIndex
*†(仅限ExtListBox
)—— 获取或设置列表框插入符号所在的项目,无论项目是否被选中(高亮)。当允许多选时很有用。读取此属性时,返回当前选定项目的索引,如果SelectionMode
为 SelectionMode.One 且SelectedIndex
不为 -1;——否则,使用SendMessage
Win32 API 来获取当前(可能未选定)的插入符号索引(如果没有项目获得焦点,则返回 0)。写入此属性时,还会选择指定索引的项目(如果不是 -1),或取消选择所有项目(如果为 -1),如果SelectionMode
为 SelectionMode.One(且列表不为空);——否则,使用SendMessage
API 将插入符号设置到指定索引而不影响任何选择。当控件没有项目,或者在SelectionMode
不是 SelectionMode.One 时缺少句柄时,获取时返回 -1,设置时不执行任何操作。Items
——指定列表项,包括图形信息。请注意,此类型为ExtListBox.ObjectCollection
或ExtComboBox.ObjectCollection
,而不是它们的基类型。要在设计时设置列表内容,请参阅InitialListItems
属性以及下面关于相应属性页的主题。SelectedItems
† 和SelectedIndices
†(仅限ExtListBox
)——分别指定选定的项目(包括图形信息)及其索引。请注意,这些类型为ExtListBox.SelectedOnjectCollection
和ExtListBox.SelectedIndexCollection
,而不是它们的基类型。DrawMode
——根据ExtListAndCombo.DrawMode
枚举值指定要具有固定或可变垂直项目高度的位置。(控件始终配置为“所有者绘制”以允许图形操作,因为控件执行其自身的自定义成像。如果您尝试将DrawMode
设置为DrawMode.Normal
,则会抛出异常。)DisplayMode
*——根据ExtListAndCombo.DisplayMode
枚举值指定是仅显示文本、仅显示图像还是两者都显示(图像在左,文本在右)。AlignText
*——指定是否将项的文本部分的水平对齐起始位置放在列表中最宽图像的右端之后,即使图像大小不是固定的(ImageSize
= Size.Empty)。True 是默认值;如果设置为 False,则项文本的水平起始位置可能随图像宽度而变化,因为每项的文本然后从其特定关联图像的右端开始。ImageSize
* 和ImagePadding
*——分别确定显示图像的Size
和Padding
;默认为方形图像,四周有 1 像素的填充,使用初始ItemHeight
属性值(减去填充)进行大小调整。如果ImageSize
设置为 Size.Empty (0,0),则图像不会缩放到匹配ImageSize
:在这种情况下,如果DrawMode
属性为ExtListAndCombo.DrawMode.UseFixedHeight
,则它们将被缩放到使其高度与当前ItemHeight
属性值(减去填充;保持纵横比)匹配;如果DrawMode
属性为ExtListAndCombo.DrawMode.UseVariableHeight
,则它们根本不会缩放。(后者在图像大小不同时很有用;使用AlignText
来确定项目文本的左端是否所有项目都对齐。)每当在设计时更改ItemHeight
值时(通过在属性窗口中设置它或调整控件大小),这两个属性都会被重置为四周填充 1 像素,并使用方形ItemHeight
减去填充的大小调整。(在运行时更改ItemHeight
时不会发生此行为。)ItemHeight
——获取第一个项目的高度;当Drawmode
为ExtListAndCombo.DrawMode.UseFixedHeight
时设置项目的高度,或当ExtListAndCombo.DrawMode.UseVariableHeight
时设置下一个添加的项目的高度。在设计时设置时,会设置ImageSize
和ImagePadding
,如上所述。注意:设置此值时,应在更改项目的图形属性之前进行,因为出于未知原因,设置它会重置所有项目的字体为主要的Font
属性!最好在列表为空时设置它,至少对于ExtListBox
控件而言,因为在填充列表时这样做可能会导致控件的Height
收缩(这也是基类控件的问题!)。DefaultImage
*——指定在没有为列表项指定图像时显示的Image
;默认为无图像。SelectedItemInfo
*†——获取或设置选定列表项的完整信息(包括图像/字体/颜色),作为ItemInfo
实例。设置此属性将,如果存在匹配实例Value
属性的条目,则选择该条目并将图像、字体和颜色属性根据新实例进行设置。DisplayMember
——指定列表项数据对象(ItemInfo.Value
)的哪个(子)成员用于在列表中显示;如果设置为 null(默认)或不存在的成员,则显示该对象ToString
方法的值。与基类版本不同,此属性即使在DataSource
不为 Nothing 时也可以设置,并且如果Sorted
属性值为 True,则会影响显示和Items
集合中的项目排序顺序(但不会影响DataSource
属性本身)。ValueMember
——指定列表项数据对象的哪个(子)成员由SelectedValue
属性返回;如果设置为 null(默认)或不存在的成员,则SelectedValue
返回该对象ToString
方法的值。与基类版本不同,此属性即使在DataSource
不是 Nothing 时也可以设置。Sorted
——确定列表在添加、删除或修改项目时是否自动排序;当设置为 True 时,列表会立即排序。排序顺序取决于DisplayMember
属性的值以及是否使用了Format
事件(用于为项目指定自定义字符串)。与基类版本不同,此属性即使在DataSource
未设置为 Nothing 时也可以设置;但是,重新排序显示/Items
列表不会影响DataSource
引用中的项目顺序。HighlightTextColor
* 和HighlightBackColor
*——分别指定分配给高亮项目的文本和背景的颜色。(当标准“高亮”颜色用于未高亮项目时很有用。)DataSource
——使用默认图形信息检索、分配或插入列表中的项目。HorizontalExtent
(仅限ExtListBox
)——通过将其设置为-1,允许自动确定列表中最宽项目的宽度并据此设置范围——在这种情况下,任何添加、删除或修改列表项都会导致重新计算该值并将其重置为反映最宽的新项目。ExtListBox.IntegerCollection
的AlignTabs
方法现在将此属性设置为-1,以智能地设置范围。(旧方法未能考虑到我所做的对图像大小调整选项的更改。)现在设计时也可用。DropDownHorizontalScrollbar
* 和DropDownHorizontalExtent
*(仅限ExtComboBox
)——功能上与ExtListBox
的HorizontalScrollbar
/HorizontalExtent
相同,不同之处在于它们用于为ExtComboBox
的下拉列表实现水平滚动条功能。与列表框控件一样,第二个属性仅在第一个为True
(允许水平滚动条)时才有意义,将其设置为-1 会使控件将其设置为最宽项目的范围,并在对列表进行任何更改时重新评估该范围。DropDownHorizotnalExtent
现在也可用在设计时。CustomDropDownButton
*(仅限ExtComboBox
)——为ExtComboBox
的下拉按钮指定一个Bitmap
(Nothing 表示正在使用默认图像);允许使用自定义位图设置图像。请注意,在读写此属性时,图像不会按按钮大小(DropDownButtonSize
)存储,而是在渲染时缩放到适合(这可以防止控件反复调整大小时图像精度下降,例如在设计时);此外,与默认图像不同,自定义按钮的背景颜色不会根据列表是否下拉而改变。(因此,**在使用自定义按钮后恢复到标准按钮,请将此属性设置为Nothing而不是DefaultDropDownButton
**;否则,您只会得到一个自定义图像的副本,并且其外观不会随按钮的状态而改变!)自定义下拉按钮,就像默认按钮一样,仅在设计为“下拉”的列表时渲染;也就是说,只要DropDownStyle
不是 ComboBoxStyle.Simple。DefaultDropDownButton
*†(仅限ExtComboBox
)——获取组合框控件当前“默认下拉”按钮的Bitmap
——确切的位图取决于下拉样式和按钮的状态。此属性仅在控件具有句柄时返回有意义的值,读取时使用自定义按钮可能会导致短暂闪烁,因为默认按钮会暂时恢复以便读取到位图中。此属性主要用于允许您将默认图像用作创建自定义图像的绘图模板。当DropDownStyle
为 ComboBoxStyle.Simple 时返回Nothing,因为该样式的组合框不会“下拉”。DropDownHandle
*† 和EditHandle
*†(仅限ExtComboBox
)——分别返回组合框的下拉列表和编辑部分的句柄;与用于整体控件的Handle
属性不同。这些句柄只有在整体句柄存在时才有有意义的值,并且专门提供给希望为控件的这些部分创建自己的特殊功能的情况。BindingContext
和DataBinding
——隐藏的只读属性,返回Nothing。请使用DataSource
属性或SetDataSource
方法使控件具有数据感知能力!SpacingBetweenColumns
*(仅限ExtListBox
)——导致ExtListBox.IntegerCollection
的AlignTabs
方法在每次列表内容更改时被调用,以便所有制表符都足够宽以容纳每列中最宽的项目加上此属性指定的像素数。设置为-1 以禁用自动重新对齐。InitialListItems
*——“代理属性”,允许在设计时设置列表的初始内容!它在属性窗口中显示为“Items”(它修改的属性),并且可以在设计时设置任意次数。然而,在运行时,它只能在列表为空时设置。您唯一想在非设计器生成的代码中设置它的情况是当您在运行时动态创建控件时(否则窗体的InitializeComponents
方法将设置它)——在这种情况下,使用Items
属性填充它更简单。有关属性页和ItemInfoCollection
类的主题,请参阅下方。 (顺便说一句,仅仅操作该类的实例不会填充列表;该实例必须分配给此属性。该属性的设置故意使得 Intellisense 在代码编辑器中不提示它;但它仍然存在——并在代码中包含它不会导致编译器错误。)
方法
SetDataSource
(datasource[, images][, fonts][, textcolors][, backgroundcolors])
*——设置DataSource
属性,并为数据源中的项分配特定的图形信息。DropDownRectangle
[(
displayrectangle)
]* 和DropDownSize
[(
displaysize)
]*(仅限ExtComboBox
)——分别检索组合框下拉列表的Rectangle
和Size
——如果提供了可选参数且其值为True,则为屏幕/完整坐标/尺寸;否则仅为客户区坐标/尺寸。DropDownPointToScreen(
point)
* 和DropDownPointToClient(
point)
*(仅限ExtComboBox
)——分别进行组合框下拉列表上点的客户区到屏幕和屏幕到客户区的转换。IndexFromDropDownPoint
(point[, autoscroll])
* /IndexFromDropDownPoint(
x, y[, autoscroll])
*(仅限ExtComboBox
)——与列表框控件的IndexFromPoint
方法相同,不同之处在于它获取下拉列表客户区坐标点下的项目的索引。值为-1 表示该点不在列表的客户区内。如果 autoscroll 为 True(默认),则下拉列表会自动滚动以显示该点。DropDownButtonRectangle
* 和DropDownButtonSize
*(仅限ExtComboBox
)——分别以客户区坐标检索下拉按钮的Rectangle
和Size
;当控件具有句柄时,这些属性才有意义。FindStringBinarySearch
(searchstring[, exact]) *——定位列表中第一个显示字符串与指定 searchstring 完全匹配(exact = True 或省略)或部分匹配(exact = False;条目必须以 searchstring 开头)的条目,前提是显示字符串按升序字典顺序排序(通过将Sorted
设置为 True 或手动排序列表)。如果找到匹配项,则返回第一个匹配项的索引。如果不存在匹配项,则返回一个负值,该负值是第一个显示字符串按字典顺序排在 searchstring 之后的条目的索引的 1 的补数(使用“Xor -1”获取索引);如果返回值的 1 的补数大于或等于列表的大小,则所有列表项的显示字符串在字典顺序上都 precedes searchstring。(在任何非匹配情况下,索引对应于在保持列表排序的情况下添加 searchstring 的正确插入点。)不要在处理Format
事件的过程cedures中使用此方法,因为此时并非所有显示字符串都一定设置为其正确值。GetItemText
(item) ——返回指定列表对象项的显示字符串;此方法实现与基类不同,因为它反映了项在处理任何Format
事件之后的最终显示文本,前提是您不在处理此类事件的过程procedures中读取它。 返回的文本代表列表中第一个匹配的对象项;如果不存在这样的项,则抛出异常。
事件
CaretIndexChanged
*(仅限ExtListBox
)——当列表框插入符号(CaretIndex
属性)所在的项目索引发生变化时发生;在SelectedIndexChanged
事件之后触发。DopDownMouseMove
*、DropDownMouseDown
*、DropDownMouseUp
* 和DropDownMouseWheel
*(仅限ExtComboBox
)——跟踪组合框控件下拉列表的鼠标操作,只要列表已下拉。它们接受与其常规鼠标事件相同的参数——第二个参数为MouseEventArgs
值——但坐标是相对于下拉列表客户区左上角的(而不是编辑部分的),e.Clicks
返回自控件创建以来下拉列表的点击次数,并且e.Delta
仅对DropDownMouseWheel
事件有意义。这些事件在列表下拉时响应鼠标活动——即使在列表客户区之外(使用DropDownRectangle(False).Contains
检查坐标是否在客户区内)——并在任何“标准”组合框鼠标驱动行为之前触发。此外,它们增强但不会阻止标准行为——这就是为什么我没有为下拉列表创建“点击”和“双击”事件的原因。请注意,这些事件仅适用于列表,而不适用于编辑部分,无论DropDownStyle
是否使列表“下拉”(ComboBoxStyle.DropDown 或 ComboBoxStyle.DropDownList)或“始终存在”(ComboBoxStyle.Simple)。
辅助类的自定义成员
ItemInfo 类
此类的实例包含列表项或潜在列表项的对象及其关联的图形信息。其 Public
成员如下:
Value
——列表项Object
的属性。Image
——要分配给列表项的Image
属性。如果为Nothing
,则在将项分配或添加到列表时,默认情况下将分配基础控件的DefaultImage
属性值(如果该属性也为Nothing
,则为无图像)。Font
——显示项文本时使用的Font
属性;默认为控件的Font
属性。Color
——项文本在未高亮时显示的前景色Color
属性;默认为控件的ForeColor
属性。BackColor
——用于项的背景色Color
属性;默认为控件的BackColor
属性。ToString
——项的实际显示文本方法;始终与“隐藏”属性DisplayString
相同(见下文)——可以是Value.ToString
,可以是宿主控件的DisplayMember
指定的属性的ToString
文本(如果存在),或者是在宿主程序处理Format
事件的处理器中设置的值。(如果ItemInfo
实例尚未分配给实际列表,则有效地为Value.ToString
。)
该类还包含 2 个(Friend
)属性,仅对类库(而不是宿主程序!)可用。
DisplayString
——应在列表中显示的字符串。如果显式设置为 Nothing(默认),则后续读取返回Value.ToString
——默认值——或者宿主控件指定的ExtControl.DisplayMember
属性的ToString
文本——假设ItemInfo
实例已被分配给或从ExtListBox
/ExtComboBox
列表检索,并且DisplayMember
是一个非空值,指定Value
的有效属性(在其类自己的继承层级中具有唯一版本[请参阅上方关于更新的信息])。如果显式设置为实际字符串(即使是空字符串!),则后续读取返回该特定字符串。此属性仅作为宿主程序处理宿主控件的Format
事件的结果而设置。(显式的“set”值是内部存储的值,而“get”值取决于该内部值以及ExtControl.DisplayMember
。)ExtControl
——ExtListBox
或ExtComboBox
的一个实例,指定此ItemInfo
实例被分配到的控件;在“规范化”项以供控件成员或其ObjectCollection
成员使用时始终设置,在分配给列表之前始终为 Nothing。
ExtListBox.ObjectCollection 和 ExtComboBox.ObjectCollection 类(Items 属性)
这些类分别继承自 ListBox.ObjectCollection
和 ComboBox.ObjectCollection
。自定义功能如下:
Item
(index) 获取或设置底层Object
,对应于列表项(默认属性);ItemInfo
(index) 获取或设置列表项的完整图形信息,由ItemInfo
实例指定。(ItemInfo
(index).Value = Item
(index)。)Add
和Insert
功能重载,允许选择性地指定对象的图像、字体和/或文本/背景颜色;每个重载还有一个接受ItemInfo
实例的重载,将列表项及其图形信息作为单个参数。AddRange
功能重载,接受ItemInfo
数组——或接受一个Object
数组或基类/派生类ObjectCollection
实例,并带有可选的图像/字体/文本颜色/背景颜色参数。(如果主要参数是派生类ObjectCollection
,则图形参数是多余的(且被忽略),因为集合已经指定了图形信息。)CopyTo
根据指定的数组类型,将主要对象(ItemInfo.Value
)信息复制到Object
数组,或将完整信息(包括图像、字体和文本/背景颜色)复制到ItemInfo
数组(当数组类型为ItemInfo
时为完整信息)。ToArray
创建一个ItemInfo
数组,包含列表项。- 提供了构造函数重载,允许将集合与相应的父控件关联,并可选地提供
ItemInfo
数组,或带图形信息的ObjectCollection
实例(在指定派生类ObjectCollection
时被忽略),以指定初始内容。
ExtListBox.IntegerCollection 类(CustomTabOffsets 属性)
此类继承自 ListBox.IntegerCollection
;其自定义功能如下:
- 制表符偏移量的
Item
(index) 值以像素为单位指定,而不是对话框单位;偏移量相对于列表条目文本部分的开头。默认属性。 AddRange
允许您指定一个Integer
数组、一个现有的(派生类)IntegerCollection
或一个基类IntegerCollection
作为其参数。(后一种情况,对话框单位将被转换,使用控件的整体“父”Font
,转换为像素。)- 提供了一个
AlignTabs
([spacingbetweencolums][, expandonly]) 方法,该方法测量列表项中所有列的宽度,并创建/扩展制表符偏移量,以便所有相应的列水平对齐——假设所有文本项的左端由于图像宽度相同或控件的AlignText
属性为 True(默认)而水平对齐。它还会设置控件的HorizontalExtent
属性,以防启用了HorizontalScrollbar
。此方法接受一个可选的Integer
参数,用于指定列之间的最小空格像素(spacingbetweencolumns,默认为1),以及一个可选的Boolean
参数,用于指定是否仅在制表符不够宽以容纳所有项目对应的列和最小空格时才移动制表符(默认为False)。默认情况下,它将制表符设置为每列所需的最小宽度加上它们之间的最小间距——不仅扩展了太窄的制表符,还收缩了足够宽的制表符。注意:此方法将父控件的HorizontalExtent
和UseCustomTabOffsets
属性分别设置为 -1 和 True。
与标准的 ListBox
控件不同,如果未指定自定义制表符,ExtListBox
不使用“默认制表符”。如果 UseCustomTabOffsets
为 False
,或者未创建制表符偏移量,则文本中的任何制表符字符都将保持未扩展状态。
ExtListBox.SelectedIndexCollection 类(SelectedIndices 属性)
此类继承自 ListBox.SelectedIndexCollection
,具有以下自定义功能:
ObjectCollectionIndexOf
(以前未记录)返回此集合中给定索引的项目的ExtListBox.ObjectCollection
索引。基本上,它是IndexOf
的反向操作,后者获取此集合中给定ExtListBox.ObjectCollection
索引的项目的索引。ToArray
创建一个Integer
数组,包含选定索引的列表。
ExtListBox.SelectedObjectCollection 类(SelectedItems 属性)
此类继承自 ListBox.SelectedObjectCollection
,并具有以下自定义功能:
ItemInfo
(index),获取或设置列表项的完整信息,作为ItemInfo
实例。CopyTo
将列表复制到数组——根据需要为主要对象(ItemInfo.Value
)信息复制到Object
,或为完整信息复制到ItemInfo
。ToArray
创建一个ItemInfo
数组,包含选择列表。
注意
如果您使用“For Each variable ...
”来枚举 ObjectCollection
(Items
属性)或 SelectedObjectCollection
(SelectedItems
属性),则每个 variable
的类型将是 ItemInfo
,包含条目的完整信息。使用 variable.Value
访问底层 Object
。
设计时设置列表
已创建一个名为 InitialListItems
的“代理属性”,以允许在设计时设置 Items
属性集合的初始内容。(终于!!)只需转到属性窗口中名为“Items”的属性,然后单击省略号 [...] 按钮。属性页如下所示:
它包含一个 ExtListBox
来显示父控件的列表外观(如果父控件是 ExtComboBox
,则在列表下拉时显示)——将父控件的 Items
、AlignText
、DefaultImage
、ImageSize
、ImagePadding
、Font
、ForeColor
、BackColor
、DrawMode
、ItemHeight
、HighlightTextColor
和 HighlightBackColor
属性传递给属性页的列表框。(属性页列表框可以根据需要水平滚动,即使父控件没有设置为可滚动。)
每当在列表框中选择一个项目时,右侧的属性网格将显示选定项目的 ItemInfo
属性 BackColor
、Color
、Font
、Image
和 Value
(始终为未制表符分隔的文本)以供修改。修改属性时,对项目的影响会立即在列表中体现。
Add 按钮在选定项之后插入一个新空白条目(并将焦点切换到网格以便进行修改),Remove 按钮删除选定条目,箭头按钮将选定条目向上或向下移动列表。要将焦点设置到现有项目的网格,请双击该条目(或在选择条目后按 Tab 键切换到网格——或按Ctrl+G)。要查看选定项目的真实文本和背景颜色而不移开焦点,请取消选中“高亮选定项”,以便仅通过虚线焦点矩形来指示项目的选定状态;重新选中它以恢复正常高亮。
列表框和属性网格都具有上下文菜单。列表框的功能与 Add、Remove 和箭头按钮相同,还有一个“转到网格”选项,用于将焦点设置到选定列表条目的网格(与双击项目相同)。其快捷键如下:Ins = Add,Del = Remove,Alt+[Up Arrow] = Move Up,Alt+[Down Arrow] = Move Down,以及 Ctrl+G = Go To Grid。
属性网格的上下文菜单(仅在突出显示属性的名称而非其值时有效!)允许撤销或重做自最近一次选择条目以来的一项或多项更改,或将一项或所有属性设置为“原始”值(最近一次选择条目时的值)或“默认”值(插入条目时的值——继承自父窗体的主要属性)。其快捷键如下:Ctrl+X = Undo change,Ctrl+Y = Redo Change,Ctrl+Shift+O = Use original value for ALL properties,以及 Ctrl+Shift+D = Use default value for ALL properties。
ItemInfoCollection 类
InitialListItems
属性的类型为 ItemInfoCollection
,它继承自 Collection(Of ItemInfo)
,并具有以下自定义功能:
- 构造函数重载,用于指定父控件和可选的
ItemInfo
数组或预先存在的ItemInfoCollection
实例。 AddRange
方法,允许将ItemInfo
元素数组追加到集合中。
请记住,修改集合不会影响列表直到它被分配给 InitialListItems
属性——在设计时使用上述属性页,或在运行时列表为空时(例如,在窗体的 InitializeComponents
方法中)。
代码片段
' Get namespace
Imports ExtListAndCombo
Private Sub ThisProcedure()
' ExtListBox
' set up information for a list item
Dim ItemInfo As ItemInfo = New ItemInfo("Multi-line" & ControlChar.CrLf & "text", _
ImageList1.Images.Item(0), New Font("Arial"), Color.Red, Color.Blue)
' add 1 item
ExtListBox1.Items.Add(ItemInfo) ' specify all info at once
ExtListBox1.Items.Add(Value, Image, Font, TextColor, BackColor) ' specify separetly
' add multiple items
Dim Values() As SomeType, ItemInfos() As ItemInfo
ExtListBox1.Items.AddRange(ItemInfos)
ExtListBox1.Items.AddRange(Values, Image, Font, TextColor, BackColor) ' same graphic info
Dim Images() As Image, Fonts() As Font, TextColors() As Color, BackColors() As Color
ExtListBox1.Items.AddRange(Values, Images, Fonts, TextColors, BackColors) ' different graphics
ExtListBox1.Items.AddRange(Values, Image, Fonts, TextColors, BackColor) ' some info same/some not
ExtListBox1.Items.Add("Column 1, Row 1" & vbTab & "Column two, Row two" & vbCrLf _
& "Column one, Row two" & vbTab & "Column 2, Row 2") ' use default graphics
' handle tabs
ExtListBox1.CustomTabOffsets.AlignTabs(3) ' line up columns with at least 3 pixels between them
ExtListBox1.CustomTabOffsets.AlignTabs(3, True) 'same thing, but don't contract over-wide columns
' auto-adjust horizontal extent and tabs, with 5 pixels between columns as list changes
ExtListBox1.HorizontalScrollbar = True : ExtListBox1.HorizontalExtent = -1
ExtListBox1.SpacingBetweenColumns = 5
' display images of different sizes, but keep text aligned
ExtListBox1.ImageSize = Size.Empty : ExtListBox1.AlignText = True
' ExtComboBox
' set up defaults
ExtComboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
ExtComboBox1.DrawMode = ExtListAndCombo.UseVariableHeight
ExtComboBox1.DisplayMode = ExtListAndCombo.DisplayMode.ShowImageAndText
' handle data
ExtComboBox1.Sorted = True : ExtComboBox.DisplayMember = "ThisMember"
ExtComboBox1.SetDataSource(Data, Images, Fonts, TextColors, BackColors)
' manipulate info
ExtComboBox1.DefaultImage = ImageList1.Item(2)
ExtComboBox1.Items.ItemInfo(Index) = ItemInfo ' set all info for an item
ExtComboBox1.DroppedDown = True ' display list
' handle drop-down list, with horizontal extent auto-adjusting for list changes and custom button
ExtComboBox1.DropDownHorizontalScrollbar = True : ExtComboBox1.DropDownHorizontalExtent = -1
ExtComboBox1.CustomDropDownButton = ThisBitmap
' binary search
Dim index As Integer = ExtListBox.FindStringBinarySearch(ThisString)
If index < 0 Then
' item not present--insert it
index = (index Xor -1)
ExtComboBox1.Insert(index, ThisString)
End If
ExtComboBox1.SelectedIndex = index
End Sub
' mouse event for drop-down list!
Private Sub ExtComboBox1_DropDownMouseMove(sender As Object, _
e As MouseEventArgs) Handles ExtComboBox1.DropDownMouseMove
' get text of item being hovered over
With ExtComboBox1
Dim Index As Integer = .IndexFromDropDownPoint(e.X, e.Y)
If Index = -1 Then
Label1.Text = "no item hovered over"
Else
Label1.Text = .Items(Index)
End If
End With
End Sub
警告!
如果最新功能似乎不受支持,或者您在2021年8月25日 上午1:00 EST 之前已经下载过此文件,请执行此操作
检查“bin”文件夹——它间接包含 DLL 的调试和/或发布版本——是否存在于2 个父文件夹中。如果一个直接位于顶级文件夹(例如,“ExtListAndCombo”)内,而另一个位于其“ExtListAndCombo”子文件夹(例如,“ExListAndCombo\ExtListAndCombo”)内,那么第一个文件夹包含一个过时版本的控件,而第二个文件夹包含正确、最新的版本。您需要删除第一个(直接位于顶级文件夹中的“bin”文件夹),并改用第二个(多一级深度的那个——整体 DLL 路径为“topfoldername\ExtListAndCombo\bin\debugorrelase\ExtListAndCombo.dll”,其中topfoldername 是顶级文件夹名称(默认也为“ExtListAndCombo”),debugorrelease 是“Debug”或“Release”)。
此外,您需要为之前创建的每一个使用 ExtListAndCombo.dll 的宿主项目执行此操作:打开它,并在“References”选项卡下检查 DLL 的源路径。如果路径不包含“ExtListAndCombo”实例(位于顶级文件夹和“bin”之间)(即,它是“ExtListAndCombo\bin”而不是“ExtListAndCombo**\ExtListAndCombo**\bin”),您必须删除引用,使用正确的(更长的)路径重新添加,然后重新生成项目。任何关于缺失或已修改功能的编译时(或运行时)错误应该就会消失。(然后检查您的代码,看看是否移除了任何事件过程的“Handles”子句,并且需要重新添加。)
注释
- 如果您使用了不同的顶级文件夹名称来解压缩下载文件,那么请将上面所说的解释为,上层“ExtListAndCombo”变成了那个不同的名称,而下层“ExtListAndCombo”仍然是“ExtListAndCombo”。(指出这一点就像从婴儿手中抢糖果,但有些人可能匆忙中过于字面理解。)
- 如果您在2021年8月25日 上午1:00 EST之后首次下载本文档,那么您可以忽略所有这些。我已经修复了文件和文件夹的冗余故障!
关注点
为了确保列表中的项目及其各自的图形信息在对列表进行更改时保持同步,完整信息被塞入一个 ItemInfo
实例,这是 MyBase.Item
在 ObjectCollection
中看到的内容。此外,此项目严重依赖继承、重写和(特别是)隐藏预先存在的成员,以插入自定义功能。
值得注意的是,虽然最终用户看到了并使用了这些控件的派生类的集合,但 CLR 看到并使用了其基类的集合。因此,相应的(派生和基类)集合必须同步才能使“后台”工作正常运行。为了方便这一点,ObjectCollection
、SelectedIndexCollection
(仅限 ExtListBox
)和 SelectedObjectCollection
(仅限 ExtListBox
)类在其构造函数中接受父控件作为参数,并依赖内部 BaseCollection
函数过程,这些过程返回 DirectCast
(control, baseclass).Items
、DirectCast
(control, baseclass).SelectedIndices
和 DirectCast(
control, baseclass).SelectItems
,分别供其成员代码使用。
注释
-
为了确保底层基类控件根据(扩展控件的)
Sorted
和DisplayMember
属性的值正确排序和显示项目,MyBase
在其自身的Items
集合的每个Item
中(派生集合中的ItemInfo
)存储了完整的图形信息,其中ToString
方法表示对象本身或指定(扩展控件的)DisplayMember
成员的ToString
方法。此外,DataSource
在后台的调用方式与MyBase
不同于派生控件——后者也不将DisplayMember
和ValueMember
属性的读写传递给MyBase
! -
控件的
FindString
和FindStringExact
方法被隐藏(shadowed)以修复基类控件中的一个已知错误,该错误会导致当起始索引等于最后一个项目时抛出异常。派生类版本使用 -1(列表开头)的索引,只要指定索引是最后一个项目,从而避免了异常。 - 此版本已更新,以修复当允许多选时涉及
SelectedIndices
和SelectedItem
属性的错误(参见上方更多内容)。 ExtComboBox
控件类现在包含大量的子类化逻辑(全部在源代码的早期部分),同时使用了“托管”WndProc
(以实现自定义下拉按钮)和“非托管”Win32 SetWindowSubclass / RemoveWindowSubclass
(以实现下拉列表的鼠标事件)。最终效果是,该控件现在允许宿主程序使用托管代码完成许多原本需要对组合框使用非托管 API 才能完成的操作!(所有“非托管”内容都在幕后。)我鼓励任何想学习任何类型子类化以扩展控件功能的人研究源代码!- 我在演示程序中包含了一个过程来支持显示
ExtComboBox
下拉列表项的工具提示,如下所示(已更新至 2022 年 9 月 13 日!)。
Public Sub ShowExtComboBoxTooltip(ecb As ExtComboBox, tltp As ToolTip, _ ByVal Text As String, _ Optional ByVal X As Integer = -1, Optional ByVal Y As Integer = -1, _ Optional ByVal AreForcingRedraw As Boolean = False) Const TooltipDuration As Integer = 5000 Static OldXPos As Integer = 0, OldYPos As Integer = 0, _ Index As Integer = -1 Dim NewXPos, NewYPos As Integer ' hide tooltip if text is blank If String.IsNullOrWhiteSpace(Text) Then tltp.Hide(ecb) : Exit Sub End If ' get current hover position Dim Rect As Rectangle, XOffset, YOffset As Integer If ecb.DroppedDown Then ' display tooltip over drop-down list item Rect = ecb.DropDownRectangle XOffset = Rect.X : YOffset = ecb.Height + Rect.Y Else ' display tooltip over edit portion Rect = ecb.ClientRectangle XOffset = Rect.X : YOffset = Rect.Y End If If X > -1 Then NewXPos = X + XOffset Else NewXPos = OldXPos 'default End If If Y > -1 Then NewYPos = Y + YOffset Else NewYPos = OldYPos 'default End If ' display tooltip only if selected index or mouse position is changed If ecb.SelectedIndex <> Index Then Index = ecb.SelectedIndex ElseIf NewXPos = OldXPos AndAlso NewYPos = OldYPos _ AndAlso Not AreForcingRedraw Then Exit Sub 'index and position unchanged--allow to fade Else Index = ecb.IndexFromDropDownPoint(NewXPos, NewYPos) End If ' show tooltip if mouse position is in window tltp.Hide(ecb) If Rect.Contains(NewXPos, NewYPos) OrElse Index > -1 Then tltp.Show(Text, ecb, NewXPos, NewYPos + 3 + ecb.Cursor.Size.Height \ 2, _ TooltipDuration) End If ' update position info OldXPos = NewXPos : OldYPos = NewYPos End Sub