65.9K
CodeProject 正在变化。 阅读更多。
Home

获取 Listbox 的滚动事件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.21/5 (15投票s)

2004 年 6 月 28 日

2分钟阅读

viewsIcon

205923

downloadIcon

1803

标准框架不会告诉你滚动条何时移动或它在什么位置。 此代码为您提供该控制权。

Sample Image - ScrollingListbox.png

引言

此示例演示了如何在派生自 System.Windows.Forms.ListBox 控件的自定义控件中访问滚动条事件,使用 C# .NET。 回答的问题是

  • 我如何获取滚动事件?
  • 我如何获取滚动条位置?

入门

创建一个新的项目(文件,新建,项目...),类型为“Windows 应用程序”。 Visual Studio 将在您的解决方案资源管理器中显示该项目,并在设计模式下显示“Form1.cs”。 右键单击项目节点,选择“添加,添加用户控件...”。 将其命名为“ScrollingListBox.cs”。 现在它在解决方案资源管理器中可见,并且在设计器中看起来像一个灰色方块。

按 F7 进入代码并将基本“UserControl”替换为“ListBox”。 保存、编译,进入设计器。 如您所见,解决方案资源管理器中的图标发生了变化,并且设计器没有显示列表框。 这是您目前的代码

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace WebsiteSamples
{
    /// <summary>
    /// Summary description for ScrollingListBox.
    /// </summary>
    public class ScrollingListBox : System.Windows.Forms.ListBox
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public ScrollingListBox()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            // TODO: Add any initialization after the InitForm call

        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Component Designer generated code
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion
    }
}

Win32 相关

我们将重写 WndProc 并处理滚动条事件。 以下是与 C:\Program Files\Microsoft Visual Studio .NET\Vc7\PlatformSDK\Include\WinUser.h 包含文件中同名的常量。

        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;

        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;

当我们在 WndProc 中识别出滚动消息时,我们需要在 user32.dll 中调用 GetScrollInfo 来找出位置。(微软文档说,当类型为 SB_THUMBPOSITIONSB_THUMBTRACK 时,该位置会通过滚动消息的 wparam 传递,但这根本不是真的。)所以,首先从 DLL 导入 GetScrollInfo

        [DllImport("user32.dll", SetLastError=true) ]
        private static extern int GetScrollInfo(
            IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo );

ScrollInfoStruct 在 Win32 中被称为 SCROLLINFO,它是您在 Microsoft API 中看到的典型查询结构:一个信息结构,加载了它自己的大小和一个问题代码(以及可选的参数用于问题)。在这种情况下,问题是“你的位置是什么?”,或“SIF_ALL”。

        private struct ScrollInfoStruct
        {
            public int cbSize;
            public int fMask;
            public int nMin;
            public int nMax;
            public int nPage;
            public int nPos;
            public int nTrackPos;
        }

最后,检测消息并触发事件

好的,现在让我们在收到滚动消息时触发标准的 ScrollEvent。 为此,我们需要事件,我将其命名为“Scrolled”,因为这是 Microsoft 用于事件的命名方式(例如“Clicked”)。

        [Category("Action")]
        public event ScrollEventHandler Scrolled = null;

WndProc 中,获取滚动消息并触发 Scrolled 事件。 在此示例中,我仅响应 WM_HSCROLL 消息,因为这是自定义绘制列表框控件所需要的消息。 但是,WM_VSCROLL 的代码完全相同。 在此示例中,我只对结束滚动消息感兴趣,该消息在每次滚动操作后都会触发。

        protected override void WndProc(ref System.Windows.Forms.Message msg)
        {
            if( msg.Msg == WM_HSCROLL )
            {
                if( Scrolled != null )
                {
                    ScrollInfoStruct si = new ScrollInfoStruct();
                    si.fMask = SIF_ALL;
                    si.cbSize = Marshal.SizeOf(si);
                    GetScrollInfo(msg.HWnd, 0, ref si);

                    if( msg.WParam.ToInt32() == SB_ENDSCROLL )
                    {
                        ScrollEventArgs sargs = new ScrollEventArgs(
                            ScrollEventType.EndScroll,
                            si.nPos);
                        Scrolled(this, sargs);
                    }
                }
            }
            base.WndProc(ref msg);
        }

为了能够使用 Marshal 类,您必须引用 Interop 命名空间

using System.Runtime.InteropServices;

这应该可以了。 您可以通过捕获垂直滚动条或捕获更多滚动事件来扩展它。 如果您想在任何其他时间知道滚动条的位置,请使用 GetScrollInfo 函数。

此外,我没有尝试过,但它可能适用于任何带有滚动条的控件。

如果您想知道,我需要此代码才能让其他控件与 ListBox“一起滚动”。

© . All rights reserved.