获取 Listbox 的滚动事件






4.21/5 (15投票s)
2004 年 6 月 28 日
2分钟阅读

205923

1803
标准框架不会告诉你滚动条何时移动或它在什么位置。 此代码为您提供该控制权。
引言
此示例演示了如何在派生自 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_THUMBPOSITION
或 SB_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
“一起滚动”。