我如何才能:
listHeight - viewPortLength
的值。。
这里可以假定listView
是一个简单的文本列表视图(不是大小图片版本)。
虽然有很多答案指向使用EnsureVisible
函数,但所有这些都是随机跳转,以确保所指示的项是可见的。如果需要某个“滚动位置”,而且事先不知道可见项应该是什么(因为底层数据可能会更改),那么这个方法就没有多大用处。
我还找到了使用win32-api的建议,特别是使用setScrollInfo
和setScrollWindowEx
。不幸的是,setScrollInfo
实际上只是短暂地修改滚动条本身,使窗口内容保持不变,并且只对那些实现自己特殊滚动行为的人非常有用。从理论上说,setScrollWindowEx
应该通过调整视图和在新内容中闪动来工作,但是当与这个对象一起使用时,said函数会高兴地报告它没有做任何事情就成功了,并且报告说它在[w,h],[x,y]
符号中重新绘制了一个[0, 0]
大小的矩形。换句话说,ListView的内部状态不是很容易访问。
发布于 2020-05-06 14:10:53
我找到了这个问题的部分解决办法。假设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-偏移.滚动位置定义为幻想-滚动-单位,这些单位是可以定义的,无论被叫者希望他们是,只要他们是32位整数。“滚动操作”也不一定是欧几里德的,但是这些单元用于调整滚动条的大小;例如,可拖动元素的大小或‘页面大小’被定义为‘滚动单元’中的窗口的大小除以总滚动条的大小,带有预先设置的硬编码的最小大小,但是每个单击或拖动或鼠标移动卷轴的大小由使用滚动条的元素编码。
这就引出了这个SCROLLINFO
结构的定义。它有七名成员;
nMin
和nMax
.nMin <= nPos <= nMax
由scrollBar
自动夹紧。当滚动条完全位于顶部时,则为nPos = nMin
。在底部完全向下,nPos = nMax
.cbSize
是这个结构的大小。通常,在使用api.fMask
时,cbSize = 28
只能用于检索值的一部分。要检索全部,fMask = 7
。值:0x01
:检索nMin
,nMax
0x02
:检索nPage
0x04
:检索nPos
和nTrackPos
nPage
表示滚动旋钮的(相对)大小:nPage / (nMax - nMin)
是相对于条大小的高度,因为y-scrollbar.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
接下来,我们必须使用这些信息并设置listView
的Y值.这是最困难的部分;在基本的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
在这里,循环每个项目,找出哪一个有最合适的y坐标,我们的测量,然后设置为顶部。这个解决方案是“愚蠢的”,因为它做了一个表单需要做的计算,以便正确地呈现自己:它需要知道列表元素的呈现顺序。这也不完全正确:如果顶部的可视行是“组分隔符”的一部分,那么我们的setScrollPos
函数将找到最近的ListViewItem
并将其显示在顶部。它也不能处理任何次线的运动,因此可以被(高达)半条线的高度,舍入整条线。
https://stackoverflow.com/questions/61637355
复制相似问题