【问题标题】:ListView onScroll eventListView onScroll 事件
【发布时间】:2010-11-13 16:26:33
【问题描述】:

我正在编写一个简单的 C# 应用程序,我需要 Listview 上的 onScroll 事件。所以我创建了类 ListviewEx 女巫继承了原始的 ListView。我找到了如何从 WinAPI 检测滚动消息并修改了 WndProc 方法。 现在我有了这个 WndProc:

protected override void WndProc(ref Message m) 
{ 
    base.WndProc(ref m); 

    if (m.Msg == WM_VSCROLL) 
    { 
        onScroll(this, new EventArgs()); 
    } 
}

但问题是,我不知道如何检测有关滚动的信息。这些数据应该在 WParam 中,但在 C# 中没有像 C++ 中那样的 LOWORD 宏,我需要切换来检测 SB_BOTTOM、SB_ENDSCROLL、SB_PAGEUP 等参数。

有什么方法可以替换 C# 中的 LOWORD 宏吗?

或其他方式如何检测有关滚动的必要参数?

【问题讨论】:

    标签: c# .net winforms listview


    【解决方案1】:

    您可以按如下方式定义 WParam 常量:

    private const int WM_HSCROLL = 0x114;
    private const int WM_VSCROLL = 0x115;
    
    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;
    
    private const int SB_LINELEFT = 0;
    private const int SB_LINERIGHT = 1;
    private const int SB_PAGELEFT = 2;
    private const int SB_PAGERIGHT = 3;
    private const int SB_THUMBPOSITION = 4;
    private const int SB_THUMBTRACK = 5;
    private const int SB_LEFT = 6;
    private const int SB_RIGHT = 7;
    private const int SB_ENDSCROLL = 8;
    
    private const int SIF_TRACKPOS = 0x10;
    private const int SIF_RANGE = 0x1;
    private const int SIF_POS = 0x4;
    private const int SIF_PAGE = 0x2;
    private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
    

    检查 WParam 的实际代码是这样的:

    if (m.Msg == WM_VSCROLL)
    {
    
            ScrollInfoStruct si = new ScrollInfoStruct();
            si.fMask = SIF_ALL;
            si.cbSize = (uint)Marshal.SizeOf(si);
            GetScrollInfo(msg.HWnd, SB_VERT, ref si);
            if (msg.WParam.ToInt32() == SB_ENDSCROLL)
            {
                ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, si.nPos);
                onScroll(this, sargs);
            }
    }
    

    pinvoke.net 是一个很棒的网站,可以获取 windows32 API 中使用的常量值,而无需自己检查头文件。

    See this example

    【讨论】:

    • 谢谢!这正是我需要的:)
    • “if (msg.WParam.ToInt32() == SB_ENDSCROLL)”中的 msg 变量是什么?我没有看到msg的定义
    【解决方案2】:

    Martijn 答案会起作用,但不会捕获所有滚动。 WM_VSCROLL 消息仅在用户直接操作滚动条时发送。如果用户使用鼠标滚轮滚动,或者使用 UpArrow/DownArrow/PageUp/PageDown 键,则不会发送 WM_VSCROLL

    您可以通过监听 LVN_BEGINSCROLL 通知消息来捕捉由滚动条和鼠标滚轮引起的滚动。

    捕捉使用键时发生的滚动更难。例如,当控件滚动以响应 PageUp 键时,不会向控件发送消息。在这种情况下,最好的办法是监听 KeyPress 事件,然后检查事件前后滚动条位置的变化。

    当然,对于您的目的而言,这完全是矫枉过正。 WM_VSCROLL 消息可能完全满足您的需求。但是,如果您想捕捉所有可能的滚动,请查看 ObjectListView 中的代码,其中已经有一个 Scroll 事件可以捕捉所有这些可能性。

    【讨论】:

    • 太真实了,我在回答中错过了这一点。
    【解决方案3】:

    感谢您的回答。它真的帮助了我:) 现在我得到了我想要的......

    代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using Microsoft.Win32;
    using System.Reflection;
    
    namespace ControlsEx
    {
        public class ListViewEx : ListView
        {
            // Windows messages
            private const int WM_PAINT      = 0x000F;
            private const int WM_HSCROLL    = 0x0114;
            private const int WM_VSCROLL    = 0x0115;
            private const int WM_MOUSEWHEEL = 0x020A;
            private const int WM_KEYDOWN    = 0x0100;
            private const int WM_LBUTTONUP  = 0x0202;                 
    
            // ScrollBar types
            private const int SB_HORZ = 0;
            private const int SB_VERT = 1;
    
            // 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;
    
            // ListView messages
            private const uint LVM_SCROLL       = 0x1014;
            private const int  LVM_FIRST        = 0x1000;                   
            private const int  LVM_SETGROUPINFO = (LVM_FIRST + 147);  
    
            public enum ScrollBarCommands : int
            {
                SB_LINEUP = 0,
                SB_LINELEFT = 0,
                SB_LINEDOWN = 1,
                SB_LINERIGHT = 1,
                SB_PAGEUP = 2,
                SB_PAGELEFT = 2,
                SB_PAGEDOWN = 3,
                SB_PAGERIGHT = 3,
                SB_THUMBPOSITION = 4,
                SB_THUMBTRACK = 5,
                SB_TOP = 6,
                SB_LEFT = 6,
                SB_BOTTOM = 7,
                SB_RIGHT = 7,
                SB_ENDSCROLL = 8
            }
    
            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
    
                switch(m.Msg)
                {
                    case WM_VSCROLL:
                        ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT));
                        onScroll(this, sargs);
                        break;
    
                    case WM_MOUSEWHEEL:
                        ScrollEventArgs sarg = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT));
                        onScroll(this, sarg);
                        break;
    
                    case WM_KEYDOWN:
                        switch (m.WParam.ToInt32())
                        {
                            case (int)Keys.Down:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.SmallDecrement, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                            case (int)Keys.Up:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.SmallIncrement, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                            case (int)Keys.PageDown:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.LargeDecrement, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                            case (int)Keys.PageUp:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.LargeIncrement, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                            case (int)Keys.Home:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.First, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                            case (int)Keys.End:
                                onScroll(this, new ScrollEventArgs(ScrollEventType.Last, GetScrollPos(this.Handle, SB_VERT)));
                                break;
                        }   
                        break;
                }
    
            }
    
            public int ScrollPosition 
            {
                get
                {
                    return GetScrollPos(this.Handle, SB_VERT);
                }
                set
                {
                    int prevPos;
                    int scrollVal;
    
                    if (ShowGroups == true)
                    {
                        prevPos = GetScrollPos(this.Handle, SB_VERT);
                        scrollVal = -(prevPos - value);
                    }
                    else
                    {
                      // TODO: Add setScrollPosition if ShowGroups == false
                    }
    
                    SendMessage(this.Handle, LVM_SCROLL, (IntPtr)0, (IntPtr)scrollVal);
                }
            }
    
            public event ScrollEventHandler onScroll;
    
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
    
            [DllImport("user32.dll")]
            public static extern int SendMessage(
                  int hWnd,      // handle to destination window
                  uint Msg,       // message
                  long wParam,  // first message parameter
                  long lParam   // second message parameter
                  );
    
            [DllImport("user32.dll")]
            static extern int SendMessage(IntPtr hWnd, int wMsg,
                                           int wParam, int lParam);
    
            [DllImport("user32.dll")]
            static extern int SendMessage(IntPtr hWnd, uint wMsg,
                                           IntPtr wParam, IntPtr lParam);
    
            [DllImport("user32.dll")]
            static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            static extern int GetScrollPos(IntPtr hWnd, int nBar);
    
    
            [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;
            }
        }
    }
    

    【讨论】:

    • 很好,我可能需要借用这段代码。 :) 我注意到 Key down 事件转换为ScrollEventArgs 是相反的,也没有使用LargeIncrementLargeDecrement。所以Keys.Up应该对应SmallDecrement,PageUp=>LargeDecrement,Down=>SmallIncrement,PageDown=>LargeIncrement
    • 我也要借用这段代码 :) 感谢您的贡献。但是,它在 WM_MOUSEWHEEL 和 WM_KEYDOWN 情况下(调用 onScroll 方法)给出“NullReferenceException”错误,原因我不知道。
    【解决方案4】:

    @Klinki

    感谢您的精彩贡献 我想你忘了处理水平滚动

    Case WM_HSCROLL
        Dim sargs As New ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(Me.Handle, SB_HORZ))
                RaiseEvent OnScroll(Me, sargs)
                Exit Select
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-07
      • 2017-08-28
      • 1970-01-01
      • 2019-01-13
      • 2014-12-05
      • 1970-01-01
      相关资源
      最近更新 更多