当ListView滚动条到达底部时如何检测

4

如何知道WinForms ListView滚动条到达底部?

当这种情况发生时,我希望列表视图能够填充更多的数据(在理论上,在我的情况下是无限的)。

OnScroll事件可以给出从顶部的滚动值,但我无法知道用户是否可以继续滚动。

3个回答

5

我在伟大的ObjectListView代码项目中找到了一个答案,使用了一些代码:

http://www.codeproject.com/KB/list/ObjectListView.aspx

调用GetScrollInfo函数:

    private const int SIF_RANGE = 0x0001;
    private const int SIF_PAGE = 0x0002;
    private const int SIF_POS = 0x0004;
    private const int SIF_DISABLENOSCROLL = 0x0008;
    private const int SIF_TRACKPOS = 0x0010;
    private const int SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);        
    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern bool GetScrollInfo(IntPtr hWnd, int fnBar, SCROLLINFO scrollInfo);

    public static SCROLLINFO GetFullScrollInfo(ListView lv, bool horizontalBar) {
      int fnBar = (horizontalBar ? SB_HORZ : SB_VERT);

      SCROLLINFO scrollInfo = new SCROLLINFO();
      scrollInfo.fMask = SIF_ALL;
      if (GetScrollInfo(lv.Handle, fnBar, scrollInfo))
        return scrollInfo;
      else
        return null;
    }

使用此数据结构:

    [StructLayout(LayoutKind.Sequential)]
    public class SCROLLINFO
    {
        public int cbSize = Marshal.SizeOf(typeof(SCROLLINFO));
        public int fMask;
        public int nMin;
        public int nMax;
        public int nPage;
        public int nPos;
        public int nTrackPos;
    }
表示滚动条最大可滚动值,包括滚动条本身的长度。因此,实际有用的最大值是-,其中表示滚动条手柄的长度。

这很好用!


我应该使用哪个事件来调用GetFullScrollInfo(...)?我正在使用鼠标移出来查看获取的值,总是给出:滚动信息:28,23,0,936,937,0,0与结构体中的顺序相同(dbsize,fmask,....,nTrackPos) - Rafael Alfredo Zelaya Amaya

0

虚拟模式在这种情况下可能是聪明的,但它无法解决我的问题。 我的数据是无限的(它是一个日期的列表视图),所以我不能显示一个有数百万行的巨大列表视图,直到2100年。 我需要当用户到达底部时列表才会增长。 - adams

0
如果有人需要的话,我借用了上面的代码,并进行了一些改进,以处理使用键盘向下、下一页和结束键、拖动或单击滚动条并到达最大底部,以及在垂直或水平方向上使用鼠标滚轮的onMaximumBottomScroll事件。这对我来说非常有效。
 public partial class OrganizationFilesListView : ListView
{
    // Windows messages
    private const int WM_VSCROLL = 0x0115;
    private const int WM_MOUSEHWHEEL = 0x020E;
    private const int WM_MOUSEWHEEL = 0x020A;
    private const int WM_KEYDOWN = 0x0100;

    // ScrollBar types
    private const int SB_VERT = 1; // only for maximum vertical scroll position

    // ScrollBar interfaces
    private const int SIF_TRACKPOS = 0x10;
    private const int SIF_RANGE = 0x01;
    private const int SIF_POS = 0x04;
    private const int SIF_PAGE = 0x02;
    private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;

    // variable to force to run only once the event
    private bool runningOnMaximumBottomScroll = false;

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        SCROLLINFO si = new SCROLLINFO();
        si.cbSize = (uint)Marshal.SizeOf(si);
        si.fMask = (uint)ScrollInfoMask.SIF_ALL;
        bool isMaximumButtomScroll = false;

        switch (m.Msg)
        {
            case WM_VSCROLL:
                isMaximumButtomScroll = GetScrollInfo(m.HWnd, SB_VERT, ref si) ? (si.nPage + si.nPos) >= si.nMax : false;
                if (isMaximumButtomScroll && !runningOnMaximumBottomScroll)
                {
                    runningOnMaximumBottomScroll = true;
                    onMaximumBottomScroll(this, new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT)));
                    runningOnMaximumBottomScroll = false;
                }
                break;
            case WM_MOUSEHWHEEL:
            case WM_MOUSEWHEEL:
                isMaximumButtomScroll = GetScrollInfo(m.HWnd, SB_VERT, ref si) ? (si.nPage + si.nPos) >= si.nMax : false;
                bool isMouseWheelDown = m.Msg == WM_MOUSEWHEEL ? (int)m.WParam < 0 : (int)m.WParam > 0;
                if (isMaximumButtomScroll && isMouseWheelDown && !runningOnMaximumBottomScroll)
                {
                    runningOnMaximumBottomScroll = true;
                    onMaximumBottomScroll(this, new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT)));
                    runningOnMaximumBottomScroll = false;
                }
                break;
            case WM_KEYDOWN:
                isMaximumButtomScroll = GetScrollInfo(m.HWnd, SB_VERT, ref si) ? (si.nPage + si.nPos) >= (si.nMax - 1) : false;
                switch (m.WParam.ToInt32())
                {
                    case (int)Keys.Down:
                    case (int)Keys.PageDown:
                    case (int)Keys.End:
                        if (isMaximumButtomScroll && !runningOnMaximumBottomScroll)
                        {
                            runningOnMaximumBottomScroll = true;
                            onMaximumBottomScroll(this, new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT)));
                            runningOnMaximumBottomScroll = false;
                        }
                        break;
                }
                break;
        }
    }

    public event ScrollEventHandler onMaximumBottomScroll;

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern int GetScrollPos(IntPtr hWnd, int nBar);

    [Serializable, StructLayout(LayoutKind.Sequential)]
    struct SCROLLINFO
    {
        public uint cbSize;
        public uint fMask;
        public int nMin;
        public int nMax;
        public uint nPage;
        public int nPos;
        public int nTrackPos;
    }

    public enum ScrollInfoMask : uint
    {
        SIF_RANGE = 0x1,
        SIF_PAGE = 0x2,
        SIF_POS = 0x4,
        SIF_DISABLENOSCROLL = 0x8,
        SIF_TRACKPOS = 0x10,
        SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
    }
}

完成了... 尽情享受吧!


我该怎么使用它?我声明了一个类型为“OrganizationFilesListView”的对象,然后以编程方式将该元素插入到winform中吗?谢谢。 - Rafael Alfredo Zelaya Amaya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接