Я нашел частичное решение этой проблемы. Предположим, что ListView
расширяется / подклассифицируется. Сначала импортируйте функцию из user32.dll
; GetScrollInfo
, как показано ниже:
Declare Function GetScrollInfo Lib "user32" Alias "GetScrollInfo" (ByVal hWnd As IntPtr, _
ByVal n As Integer, ByRef lpScrollInfo As SCROLLINFO) As Integer
Structure SCROLLINFO
Dim cbSize As Integer
Dim fMask As Integer
Dim nMin As Integer
Dim nMax As Integer
Dim nPage As Integer
Dim nPos As Integer
Dim nTrackPos As Integer
End Structure
Private Const SB_HORZ As Integer = 0
Private Const SB_VERT As Integer = 1
Это можно использовать для получения позиции прокрутки окна как смещения по оси Y. Позиция прокрутки определяется в fantasy-scroll-units, которые можно определить, но вызываемый хочет, чтобы они были, если они 32-битные целые числа. «Действие прокрутки» также не обязательно должно быть евклидовым, но единицы измерения используются для определения размера полосы прокрутки; например, размер перетаскиваемого элемента или «размер страницы» определяется как размер окна в «единицах прокрутки», деленный на размер общей полосы прокрутки, с предварительно установленным жестко заданным минимальным размером, но сколько каждый щелчок прокрутки или перетаскивание или прокрутка колесика мыши кодируются элементом с помощью полосы прокрутки.
Это приводит к определению этой структуры SCROLLINFO
. Он состоит из семи членов;
nMin
и nMax
. nMin <= nPos <= nMax
автоматически зажимается scrollBar
. Когда полоса прокрутки полностью вверху, то nPos = nMin
. Полностью внизу, nPos = nMax
. cbSize
- это размер этой конструкции. Обычно cbSize = 28
при использовании win32 api. fMask
можно использовать для получения только части значений. Чтобы получить все, fMask = 7
. Значения: 0x01
: возвращает nMin
, nMax
0x02
: возвращает nPage
0x04
: возвращает nPos
и nTrackPos
nPage
обозначает (относительный) размер ручки прокрутки: nPage / (nMax - nMin)
будет высотой относительно размера полосы для y-полосы прокрутки. nPos
- текущая позиция полосы прокрутки. nTrackPos
- то же самое, но обновляется во время операции щелчка и перетаскивания, что позволяет вам иметь элемент формы, который перемещается, пока вы перетаскиваете полосу прокрутки (вместо стандартной; когда вы ее отпускаете), как и большинство текста редакторы делают.
Первый трюк состоит в том, чтобы точно выяснить, как мнимые единицы отображаются в пикселях. К счастью, для ListView
это кажется довольно простым: следующее определение становится «достаточно близким»;
Public Function getScrollPos() As Integer
Dim si As SCROLLINFO = Me.getScrollInfo()
Dim x As Integer = GetScrollInfo(Me.Handle, SB_VERT, si)
' The Windows Scrollbar magicunit is the same as pixels, off by exactly one window height.
' Adding nPage gets you a pixel based location.
Return si.nPos + si.nPage
End Function
Затем мы должны использовать эту информацию и установить значение Y для listView
. Это сложная часть; в базовых формах c windows нет готового метода для этой задачи. Однако у каждого элемента есть функция для получения позиции Y. Но наивный метод простого использования Me.items(Y/17)
(примечание; 17 - высота по умолчанию) вообще не работает, потому что переменная-член Me.Items
не сортируется с отображением.
Public Sub setScrollPos(ByVal y As Integer)
Dim si As SCROLLINFO = Me.getScrollInfo()
Dim x As Integer = GetScrollInfo(Me.Handle, SB_VERT, si)
If (y > si.nMax) Then
y = Math.Max(si.nMax, 0)
End If
Dim itemHeight As Integer = Me.Items(0).GetBounds(ItemBoundsPortion.Entire).Height
Dim iClosest = 0
Dim iTmp = 0
Dim delta = Integer.MaxValue
' "Stupidly clever" solution by simply trying all items out and finding out which one is closest to the Y value.
' Implemented as there is no "sorted index" exposed by ListViewItemCollection. (No idea why...)
If Me.Items.Count > 0 Then
For i As Integer = 0 To Me.Items.Count - 1 Step 1
iTmp = Math.Abs(Me.Items.Item(i).Position.Y - y)
If iTmp < delta Then
delta = iTmp
iClosest = i
End If
Next 'i
End If
Me.TopItem = Me.Items.Item(iClosest)
Me.Invalidate()
End Sub
Здесь , l oop по каждому элементу, выясните, какой из них имеет наиболее подходящую координату Y для нашего измерения, а затем установите его как верхний. Решение является «глупым» в том смысле, что оно выполняет вычисление, которое форма должна делать в любом случае, чтобы правильно отображать себя: ему необходимо знать порядок отображения элементов списка. Это также не совсем правильно: если предполагаемая верхняя видимая строка является (частью) «разделителем групп», то наша функция setScrollPos
вместо этого найдет ближайший ListViewItem
и отобразит его вверху. Он также не может обрабатывать любое перемещение субстроки и, таким образом, может быть отключен на (до) половины высоты строки путем округления до целой строки.