等同于 Windows 表单和访问表单中的 Microsoft Forms 2.0 ComboBox .TopIndex 属性?
Equivalent to Microsoft Forms 2.0 ComboBox .TopIndex property in Windows Forms and Access Forms?
(已弃用的)Microsoft Forms 2.0 控件包括一个组合框,它提供了无价的 属性:.TopIndex (documentation here).
似乎此 属性 不适用于 Microsoft Access 2019 中表单中的标准组合框(根据文档),并且不适用于 Windows 表单中的标准组合框(.NET )(这里ComboBox继承了ListControl,没有提供这个属性,而ListBox也继承了ListControl,但是提供了)
我有很多严重依赖 .TopIndex 属性 的旧代码,是时候将该代码转移到其他技术了。
所以我想知道我是否遗漏了文档中的某些内容,以及是否有一个等效的 属性 具有另一个名称,我可以使用它来确定哪些项目在 a 的列表部分中可见组合框。我想知道 Access 2019 中的组合框(我不像这里的许多其他应用程序那样对这个应用程序抱有敌意)以及 Windows Forms 中的组合框。
我知道有很多免费和商业控件(包括组合框)具有针对 Windows 表单的增强功能。除非我遗漏了文档中的某些内容,否则我肯定会那样做(或自己写)。
但是,当涉及到 Access 2019 表单时,情况就完全不同了。我找不到可以在 Access 表单上使用并提供此功能的单个免费第三方 ActiveX/COM 组合框。理论上,我可能可以使用 .NET 编写一个 ActiveX / COM 控件,然后在 Access 2019 表单上使用它,但这似乎很痛苦。
就 .Net WinForm ComboBox 而言,您没有错过任何东西,因为 TopIndex 属性 的功能未实现。也就是说,扩展基本 ComboBox 控件以添加此 属性 非常简单。下面的示例控件应该可以帮助您入门。
此控件将侦听器附加到本机 ListBox 下拉列表并更新 WM_VSCROLL 上的 TopIndex 属性 和 LB_SETCARETINDEX(这会捕获打开时的初始位置)消息。此外,基本 SelectedIndexChange 事件用于捕获由于键盘操作(pgUp/pgDn、箭头 up/down)引起的更改。 TopIndex 属性 在下拉列表关闭后保留,并在打开下拉列表时重置。该控件还公开了一个 TopIndexChanged 事件。
Imports System.Runtime.InteropServices
Public Class ComboBoxEx : Inherits ComboBox
Private listBoxListener As ListBoxNativeWindow
Public Event TopIndexChanged As EventHandler(Of ComboBoxTopIndexArg)
Private _TopIndex As Int32 = -1
Public Sub New()
MyBase.New
listBoxListener = New ListBoxNativeWindow(Me)
End Sub
Public Property TopIndex As Int32
Get
Return _TopIndex
End Get
Private Set(value As Int32)
If value <> _TopIndex Then
_TopIndex = value
RaiseEvent TopIndexChanged(Me, New ComboBoxTopIndexArg(value))
End If
End Set
End Property
Protected Overrides Sub OnDropDown(e As EventArgs)
_TopIndex = -1 ' reset on opening the listbox
MyBase.OnDropDown(e)
End Sub
Private Class ListBoxNativeWindow : Inherits NativeWindow
Private listBoxHandle As IntPtr
Private TopIndex As Int32
Private parent As ComboBoxEx
Public Sub New(ByVal parent As ComboBoxEx)
Me.parent = parent
WireParent()
If parent.IsHandleCreated Then
GetListBoxHandle()
AssignHandle(listBoxHandle)
End If
End Sub
Private Sub WireParent()
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AddHandler parent.SelectedIndexChanged, AddressOf UpdateTopIndexOnIndexChanged
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
GetListBoxHandle()
AssignHandle(listBoxHandle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Private Sub UpdateTopIndexOnIndexChanged(sender As Object, e As EventArgs)
SetParentTopIndex()
End Sub
Private Sub GetListBoxHandle()
Const CB_GETCOMBOBOXINFO As Int32 = &H164
Dim info As New ComboBoxInfo
info.cbSize = Marshal.SizeOf(info)
Dim res As Boolean = SendMessage(Me.parent.Handle, CB_GETCOMBOBOXINFO, Nothing, info)
listBoxHandle = info.hwndList
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_VSCROLL As Int32 = &H115
Const LB_SETCARETINDEX As Int32 = &H19E
MyBase.WndProc(m)
If m.Msg = WM_VSCROLL OrElse m.Msg = LB_SETCARETINDEX Then
SetParentTopIndex()
End If
End Sub
Private Sub SetParentTopIndex()
Const LB_GETTOPINDEX As Int32 = &H18E
parent.TopIndex = SendMessage(listBoxHandle, LB_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero)
End Sub
End Class
Public Class ComboBoxTopIndexArg : Inherits EventArgs
Public Sub New(topIndex As Int32)
Me.TopIndex = topIndex
End Sub
Public ReadOnly Property TopIndex As Int32
End Class
#Region "NativeMethods"
<StructLayout(LayoutKind.Sequential)>
Private Structure ComboBoxInfo
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure RECT
Public Left, Top, Right, Bottom As Int32
End Structure
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, <Out()> ByRef lParam As ComboBoxInfo) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, lParam As IntPtr) As Int32
End Function
#End Region
End Class
我留给您将它包装在一个 ActiveX 公开的包装器中以便在 Access 中使用。使用 Microsoft InteropForms Toolkit 2.1 中的模板,这样做相当容易。请注意,这些模板是使用 "Any CPU" 平台设置的,您需要将其更改为 "x86".
(已弃用的)Microsoft Forms 2.0 控件包括一个组合框,它提供了无价的 属性:.TopIndex (documentation here).
似乎此 属性 不适用于 Microsoft Access 2019 中表单中的标准组合框(根据文档),并且不适用于 Windows 表单中的标准组合框(.NET )(这里ComboBox继承了ListControl,没有提供这个属性,而ListBox也继承了ListControl,但是提供了)
我有很多严重依赖 .TopIndex 属性 的旧代码,是时候将该代码转移到其他技术了。
所以我想知道我是否遗漏了文档中的某些内容,以及是否有一个等效的 属性 具有另一个名称,我可以使用它来确定哪些项目在 a 的列表部分中可见组合框。我想知道 Access 2019 中的组合框(我不像这里的许多其他应用程序那样对这个应用程序抱有敌意)以及 Windows Forms 中的组合框。
我知道有很多免费和商业控件(包括组合框)具有针对 Windows 表单的增强功能。除非我遗漏了文档中的某些内容,否则我肯定会那样做(或自己写)。
但是,当涉及到 Access 2019 表单时,情况就完全不同了。我找不到可以在 Access 表单上使用并提供此功能的单个免费第三方 ActiveX/COM 组合框。理论上,我可能可以使用 .NET 编写一个 ActiveX / COM 控件,然后在 Access 2019 表单上使用它,但这似乎很痛苦。
就 .Net WinForm ComboBox 而言,您没有错过任何东西,因为 TopIndex 属性 的功能未实现。也就是说,扩展基本 ComboBox 控件以添加此 属性 非常简单。下面的示例控件应该可以帮助您入门。
此控件将侦听器附加到本机 ListBox 下拉列表并更新 WM_VSCROLL 上的 TopIndex 属性 和 LB_SETCARETINDEX(这会捕获打开时的初始位置)消息。此外,基本 SelectedIndexChange 事件用于捕获由于键盘操作(pgUp/pgDn、箭头 up/down)引起的更改。 TopIndex 属性 在下拉列表关闭后保留,并在打开下拉列表时重置。该控件还公开了一个 TopIndexChanged 事件。
Imports System.Runtime.InteropServices
Public Class ComboBoxEx : Inherits ComboBox
Private listBoxListener As ListBoxNativeWindow
Public Event TopIndexChanged As EventHandler(Of ComboBoxTopIndexArg)
Private _TopIndex As Int32 = -1
Public Sub New()
MyBase.New
listBoxListener = New ListBoxNativeWindow(Me)
End Sub
Public Property TopIndex As Int32
Get
Return _TopIndex
End Get
Private Set(value As Int32)
If value <> _TopIndex Then
_TopIndex = value
RaiseEvent TopIndexChanged(Me, New ComboBoxTopIndexArg(value))
End If
End Set
End Property
Protected Overrides Sub OnDropDown(e As EventArgs)
_TopIndex = -1 ' reset on opening the listbox
MyBase.OnDropDown(e)
End Sub
Private Class ListBoxNativeWindow : Inherits NativeWindow
Private listBoxHandle As IntPtr
Private TopIndex As Int32
Private parent As ComboBoxEx
Public Sub New(ByVal parent As ComboBoxEx)
Me.parent = parent
WireParent()
If parent.IsHandleCreated Then
GetListBoxHandle()
AssignHandle(listBoxHandle)
End If
End Sub
Private Sub WireParent()
AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
AddHandler parent.SelectedIndexChanged, AddressOf UpdateTopIndexOnIndexChanged
End Sub
Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
GetListBoxHandle()
AssignHandle(listBoxHandle)
End Sub
Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
ReleaseHandle()
End Sub
Private Sub UpdateTopIndexOnIndexChanged(sender As Object, e As EventArgs)
SetParentTopIndex()
End Sub
Private Sub GetListBoxHandle()
Const CB_GETCOMBOBOXINFO As Int32 = &H164
Dim info As New ComboBoxInfo
info.cbSize = Marshal.SizeOf(info)
Dim res As Boolean = SendMessage(Me.parent.Handle, CB_GETCOMBOBOXINFO, Nothing, info)
listBoxHandle = info.hwndList
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_VSCROLL As Int32 = &H115
Const LB_SETCARETINDEX As Int32 = &H19E
MyBase.WndProc(m)
If m.Msg = WM_VSCROLL OrElse m.Msg = LB_SETCARETINDEX Then
SetParentTopIndex()
End If
End Sub
Private Sub SetParentTopIndex()
Const LB_GETTOPINDEX As Int32 = &H18E
parent.TopIndex = SendMessage(listBoxHandle, LB_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero)
End Sub
End Class
Public Class ComboBoxTopIndexArg : Inherits EventArgs
Public Sub New(topIndex As Int32)
Me.TopIndex = topIndex
End Sub
Public ReadOnly Property TopIndex As Int32
End Class
#Region "NativeMethods"
<StructLayout(LayoutKind.Sequential)>
Private Structure ComboBoxInfo
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public stateButton As IntPtr
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure RECT
Public Left, Top, Right, Bottom As Int32
End Structure
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, <Out()> ByRef lParam As ComboBoxInfo) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function SendMessage(hWnd As IntPtr, Msg As Int32, wParam As IntPtr, lParam As IntPtr) As Int32
End Function
#End Region
End Class
我留给您将它包装在一个 ActiveX 公开的包装器中以便在 Access 中使用。使用 Microsoft InteropForms Toolkit 2.1 中的模板,这样做相当容易。请注意,这些模板是使用 "Any CPU" 平台设置的,您需要将其更改为 "x86".