这似乎是一项复杂的任务。然而,在this post 的帮助下,我设法找到了这个解决方案。
您必须创建一个类似于listBox 的新控件,其中包含事件Scrolled,这可以使用此方法完成。
在您的项目中,右键单击项目节点并选择添加 > 添加用户控件(Windows 窗体)...,将其命名为 ScrollingListBox.cs 。它将与一个名为 ScrollingListBox.Designer.cs 的文件一起出现在 Solution Explorer 中,右键单击后一个文件并将其删除,然后选择 ScrollingListBox。 cs,右键单击它并选择查看代码,或按F7,然后将其内容替换为以下代码:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Scroll
{
public partial class ScrollingListBox : ListBox
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
[Category("Action")]
public event ScrollEventHandler Scrolled = null;
private const int WM_VSCROLL = 0x115;
private const int SB_ENDSCROLL = 0x8;
private const int SIF_RANGE = 0x1;
private const int SIF_PAGE = 0x2;
private const int SIF_POS = 0x4;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
private struct ScrollInfoStruct
{
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetScrollInfo(IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo);
protected override void WndProc(ref System.Windows.Forms.Message msg)
{
if (msg.Msg == WM_VSCROLL)
{
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);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
}
}
}
之后,您将在 Toolbox 中拥有一个名为 ScrollingListBox 的新项目,您可以使用它来代替 ListBox,它有一个事件称为 Scrolled,如下所示:
现在您可以将这种新的列表框类型用于您未来的框以及已经制作的框,如果您不想将它们全部删除并使用新类型再次绘制它们,您只需修改 Form1.Designer.cs 文件中的代码行如下:
更改此行,以及类似的行,
this.listBox1 = new System.Windows.Forms.ListBox();
到
this.listBox1 = new Scroll.ScrollingListBox();
还有这条线,以及类似的线,
private System.Windows.Forms.ListBox listBox1;
到
private ScrollingListBox listBox1;
您现在可以使用此事件来检测一个框中发生的滚动并相应地修改另一个框,如下所示:
private void listBox1_Scrolled(object sender, ScrollEventArgs e)
{
listBox2.TopIndex = listBox1.TopIndex;
}
private void listBox2_Scrolled(object sender, ScrollEventArgs e)
{
listBox1.TopIndex = listBox2.TopIndex;
}
这适用于通过单击箭头或用鼠标抓住滚动条来滚动滚动条,但不适用于在框内使用鼠标滚轮。我尝试使用事件MouseWheel 来解决这个问题,但不幸的是,它没有按预期工作,所以我发现的唯一选择是同时使用事件MouseHover 和MouseMove 来解决这个问题,比如这个:
private void listBox1_MouseHover(object sender, EventArgs e)
{
listBox2.TopIndex = listBox1.TopIndex;
}
private void listBox2_MouseHover(object sender, EventArgs e)
{
listBox1.TopIndex = listBox2.TopIndex;
}
private void listBox1_MouseMove(object sender, EventArgs e)
{
listBox2.TopIndex = listBox1.TopIndex;
}
private void listBox2_MouseMove(object sender, EventArgs e)
{
listBox1.TopIndex = listBox2.TopIndex;
}
如果您在框的白色区域内的任何地方使用鼠标滚轮,这将起作用,不幸的是,如果您在鼠标悬停在滚动条上时使用它,它将不起作用。
这可能足以解决您遇到的问题,但如果您需要一个代码来同时在两个框中选择相应的索引,这里是:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
listBox2.SelectedIndex = listBox1.SelectedIndex;
}
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
listBox1.SelectedIndex = listBox2.SelectedIndex;
}