首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >获取和设置winForms ListView滚动位置

获取和设置winForms ListView滚动位置
EN

Stack Overflow用户
提问于 2020-05-06 14:10:53
回答 1查看 990关注 0票数 0

我如何才能:

  • 检索列表视图当前滚动位置为基于像素的坐标?(例:如果ListView一直到底部,那么这个函数应该返回相当于listHeight - viewPortLength的值。
  • 将ListView的当前滚动位置设置为基于像素的坐标,导致ListView跳转到此位置?
  • 在vb.net或C#.net托管代码中执行此操作?
  • 处理ListViews具有“组”?

这里可以假定listView是一个简单的文本列表视图(不是大小图片版本)。

虽然有很多答案指向使用EnsureVisible函数,但所有这些都是随机跳转,以确保所指示的项是可见的。如果需要某个“滚动位置”,而且事先不知道可见项应该是什么(因为底层数据可能会更改),那么这个方法就没有多大用处。

我还找到了使用win32-api的建议,特别是使用setScrollInfosetScrollWindowEx。不幸的是,setScrollInfo实际上只是短暂地修改滚动条本身,使窗口内容保持不变,并且只对那些实现自己特殊滚动行为的人非常有用。从理论上说,setScrollWindowEx 应该通过调整视图和在新内容中闪动来工作,但是当与这个对象一起使用时,said函数会高兴地报告它没有做任何事情就成功了,并且报告说它在[w,h],[x,y]符号中重新绘制了一个[0, 0]大小的矩形。换句话说,ListView的内部状态不是很容易访问。

EN

回答 1

Stack Overflow用户

发布于 2020-05-06 14:10:53

我找到了这个问题的部分解决办法。假设ListView正在扩展/子类。首先,从user32.dllGetScrollInfo导入一个函数,如下所示:

代码语言:javascript
运行
复制
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结构的定义。它有七名成员;

  1. nMinnMax.nMin <= nPos <= nMaxscrollBar自动夹紧。当滚动条完全位于顶部时,则为nPos = nMin。在底部完全向下,nPos = nMax.
  2. cbSize是这个结构的大小。通常,在使用api.
  3. fMask时,cbSize = 28只能用于检索值的一部分。要检索全部,fMask = 7。值:
    1. 0x01:检索nMinnMax
    2. 0x02:检索nPage
    3. 0x04:检索nPosnTrackPos

  1. nPage表示滚动旋钮的(相对)大小:nPage / (nMax - nMin)是相对于条大小的高度,因为y-scrollbar.
  2. nPos是滚动条的当前位置。
  3. nTrackPos是相同的,但是在单击和拖动操作期间会更新,允许您像大多数文本编辑器一样,在拖动滚动条时移动表单元素(而不是默认的)。

第一个窍门是精确地计算出虚单位是如何映射到像素的。幸运的是,对于一个ListView来说,这看起来相当简单:下面的定义“足够接近”;

代码语言:javascript
运行
复制
 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成员变量没有按照显示进行排序。

代码语言:javascript
运行
复制
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并将其显示在顶部。它也不能处理任何次线的运动,因此可以被(高达)半条线的高度,舍入整条线。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61637355

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档