Эквивалентно Microsoft CommsBox 2.0. Свойство .TopIndex в формах Windows Forms и Access Forms? - PullRequest
0 голосов
/ 05 октября 2019

(устаревшие) элементы управления 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, который я мог бы использовать в формах доступа и который предоставляет эту функциональность. Теоретически я, вероятно, мог бы написать элемент управления ActiveX / COM с использованием .NET, а затем использовать его в формах Access 2019, но это кажется довольно болезненным.

1 Ответ

1 голос
/ 06 октября 2019

Что касается .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 . Просто обратите внимание, что эти шаблоны настраиваются с использованием платформы «Любой процессор», и вам нужно изменить это значение на «x86».

...