Windows 表单同时滚动多个列表框

Windows Form scrolling simultaneously multiple ListBoxes

我正在使用 WindowsForms,并且有两个列表框。我可以成功地分别滚动两个列表框,但我想“同步”滚动。因此,当我滚动 ListBox1 时,我也想同时滚动 ListBox2,select ListBox2 的相应字段并将其与 [=11 对齐在同一位置=]. 我已经查看了 this post, 和许多其他帖子。

这是它的样子:

这就是我想要实现的目标:

这似乎是一项复杂的任务。但是,在 this post 的帮助下,我设法找到了这个解决方案。

您将必须创建一个类似于 listBox 并具有事件 Scrolled 的新控件,这可以使用此方法完成。

在您的项目中,右键单击项目节点并select 添加 > 添加用户控件(Windows 表单)。 ..,命名为ScrollingListBox.cs。它将与名称为 ScrollingListBox.Designer.cs 的文件一起出现在 Solution Explorer 中,右键单击后一个文件并将其删除,然后selectScrollingListBox.cs,右击select查看代码,或者按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 来解决这个问题,但不幸的是,它没有按预期工作,所以我找到的唯一选择是同时使用事件 MouseHoverMouseMove这样做的技巧,像这样:

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

如果您在框的白色区域内的任何地方使用鼠标滚轮,这将起作用,不幸的是,如果您在鼠标滑过滚动条时使用它,它将不起作用。

可能这足以解决您遇到的问题,但以防万一您需要一个代码来 select 同时在两个框中输入相应的索引,这里是:

private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{ 
    listBox2.SelectedIndex = listBox1.SelectedIndex;
}

private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{ 
    listBox1.SelectedIndex = listBox2.SelectedIndex;
}

好的,谢谢大家的帮助。我按照 @41686d6564 的建议使用了两列的 DataGridView,它就像一个魅力。