C# ListView onScroll 事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1176703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 10:05:45  来源:igfitidea点击:

ListView onScroll event

c#.netwinformslistview

提问by Klinki

I'm programming one easy C# application, and i need onScroll event on Listview. So i created class ListviewEx witch inherits original ListView. I found how to detect scroll message from WinAPI and i modified WndProc method. Now i have this WndProc:

我正在编写一个简单的 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()); 
    } 
}

But problem is, that I dont know how to detect information about scrolling. This data should be in WParam, but in C# is no LOWORD macro like in C++ and i need switch to detect parameters like SB_ BOTTOM, SB_ ENDSCROLL, SB_PAGEUP etc.

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

Is there any way how to replace LOWORD macro in C# ?

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

Or other way how to detect necessary parameters about scrolling?

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

采纳答案by Martijn Laarman

You can define WParam constants as followed:

您可以按如下方式定义 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;

The actual code to inspect the WParam would be something like this:

检查 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 is a great site to get the constant values used in windows32 API without having to inspect header files yourself.

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

See this example

看这个例子

回答by Grammarian

Martijn answer will work but will not catch all scrolling. The WM_VSCROLLmessage is only sent when the user manipulates the scroll bar directly. If the user scrolls using a mouse wheel, or uses the UpArrow/DownArrow/PageUp/PageDown keys, then the WM_VSCROLLwill not be sent.

Martijn 的答案会起作用,但不会捕获所有滚动。该WM_VSCROLL当用户操纵滚动直接禁止时,才发送消息。如果用户使用鼠标滚轮滚动,或使用 UpArrow/DownArrow/PageUp/PageDown 键,则不会发送WM_VSCROLL

You can catch scrolling that is caused by the scroll bar and by the mouse wheel by listening for the LVN_BEGINSCROLLnotification message.

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

Catching scrolling that occurs when using keys is harder. No message that is sent to the control when it scrolls in response to a PageUp key, for example. The best that can be done in that case is to listen for KeyPress events, and then check for changes to the scroll bar positions before and after the event.

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

This could, of course, be complete overkill for your purposes. The WM_VSCROLLmessage may be completely sufficient for what you want. But if you want to catch all possible scrolling, have a look at the code in ObjectListViewwhich already has a Scroll event that catches all these possibilities.

当然,这对于您的目的来说可能是完全矫枉过正。该WM_VSCROLL消息可能是你想要的东西完全够用了。但是,如果您想捕获所有可能的滚动,请查看ObjectListView中的代码,该代码已经具有捕获所有这些可能性的 Scroll 事件。

回答by Klinki

Thanks for your answers. It really helped me :) Now I have what I wanted...

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

Here is code:

这是代码:

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

回答by Ahmed Taha

@Klinki

@克林基

Thank you for you cool contribution I think you forgot to handle horizontal scroll

谢谢你的贡献,我想你忘了处理水平滚动

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