开发者

Detecting when ListView scrollbar reaches the bottom

How do I know when the WinForms ListView scrollbar reaches it's bottom?

When this happens, I want the listview to be populated with more data (whi开发者_Python百科ch is endless in theory in my case).

The OnScroll event gives me the scroll value from the top, but I have no way of knowing if the user can scroll any further or not.


I found an answer using some code from the great ObjectListView code-project: http://www.codeproject.com/KB/list/ObjectListView.aspx

call 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;
    }

with this data struct:

    [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;
    }

the nMax gives the total max scroll value including the scroll handle itself, so the actually useful max value is nMax - nPage, where nPage is the size of the scroll handle.

This works great !


I'm not able to answer your question directly, but from your description, it sounds like you really want to look into using the virtual mode of a list view for managing a large dataset.

http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.virtualmode.aspx


In the case somebody needs this, I borrow the above code and I improved a little bit to handle onMaximumBottomScroll event using keyboard with down, next page, and end keys, dragging or clicking the scrollbar and reaching the max bottom, and using the mousewheel on vertically or horizontally. This is working for me like a charm.

 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),
    }
}

Done... enjoy it!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜