Эквивалентно свойству ComboBox Microsoft Forms 2.0 .TopIndex в Windows Forms и Access Forms?
Элементы управления Microsoft Forms 2.0 (устаревшие) включают поле со списком, которое предоставляет бесценное свойство: .TopIndex (документация здесь).
Кажется, что это свойство недоступно для стандартных комбинированных списков в формах в Microsoft Access 2019 (согласно документации) и недоступно для стандартных комбинированных списков в Windows Forms (.NET) (здесь ComboBox наследуется от ListControl и не предоставляет этого свойство, а ListBox также наследуется от ListControl, но предоставляет его).
У меня много старого кода, который сильно зависит от свойства.TopIndex, и пора перенести этот код на другие технологии.
Поэтому я хотел бы знать, пропустил ли я что-то в документации и есть ли эквивалентное свойство с другим именем, которое я мог бы использовать, чтобы определить, какие элементы видны в части списка в поле со списком. Я хотел бы знать это для комбинированных списков в Access 2019 (я не так враждебен к этому приложению, как многие другие здесь), а также для комбинированных списков в Windows Forms.
Я знаю, что существует множество бесплатных и коммерческих элементов управления (включая поля со списком) с расширенными функциями для Windows Forms. Я обязательно пойду по этому пути (или напишу свой), если я что-то не пропустил в документации.
Однако с формами Access 2019 ситуация совершенно иная. Мне не удалось найти ни одного бесплатного стороннего комбинированного списка ActiveX / COM, который я мог бы использовать в формах Access и который предоставляет эту функцию. Теоретически я, вероятно, мог бы написать элемент управления ActiveX / COM с помощью.NET, а затем использовать его в формах Access 2019, но это кажется довольно болезненным.
1 ответ
Что касается.Net WinForm ComboBox, вы ничего не пропустили, так как не реализована функциональность свойства TopIndex. Тем не менее, довольно просто расширить базовый элемент управления ComboBox для добавления этого свойства. Следующий пример элемента управления должен помочь вам начать работу.
Этот элемент управления присоединяет слушателя к собственному раскрывающемуся списку ListBox и обновляет свойство TopIndex в сообщениях WM_VSCROLL и LB_SETCARETINDEX (это фиксирует начальную позицию при открытии). Кроме того, базовое событие SelectedIndexChange используется для фиксации изменений, вызванных действиями клавиатуры (pgUp/pgDn, стрелка вверх / вниз). Свойство 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".