Windows 窗体同时滚动多个列表框

Posted

技术标签:

【中文标题】Windows 窗体同时滚动多个列表框【英文标题】:Windows Form scrolling simultaneously multiple ListBoxes 【发布时间】:2021-03-13 09:33:59 【问题描述】:

我正在使用 WindowsForms,并且我有两个 ListBox。我可以分别成功滚动两个 ListBox,但我想“同步”滚动。所以当我滚动ListBox1时,我想同时滚动ListBox2,选择ListBox2对应的字段,并与ListBox1对齐。 我已经查看了this post、this post 和许多其他帖子。

这是它的样子:

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

【问题讨论】:

这是可行的,但为什么要重新发明***呢?改为使用具有两列的 DataGridView。 所以你检查了其他帖子,什么不起作用?您的问题的当前状态将与您所指的帖子重复。 @41686d6564 DataGridView 确实可以工作,但这需要在项目中进行大量更改。我会检查并尝试实施它。能否请您指导我关于如何同步两个列表框滚动的最初问题? @nilsK 我的项目中缺少他们所指的许多东西(例如syncListView1_OnVerticalScroll)。 @curious 很抱歉,我现在没有时间为我认为没用的东西编写和测试代码。同样,不要试图重新发明***。使用 DataGridView、ListView 或任何可以包含列的控件,而不是尝试模仿行为。 【参考方案1】:

这似乎是一项复杂的任务。然而,在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 来解决这个问题,但不幸的是,它没有按预期工作,所以我发现的唯一选择是同时使用事件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;

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

这可能足以解决您遇到的问题,但如果您需要一个代码来同时在两个框中选择相应的索引,这里是:

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


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

【讨论】:

确实可以,但我最终像其他评论者建议的那样使用了 DataGridView。不过谢谢。【参考方案2】:

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

【讨论】:

以上是关于Windows 窗体同时滚动多个列表框的主要内容,如果未能解决你的问题,请参考以下文章

Windows 窗体列表框在同一个框中的多个列表

根据选择一个列表框中的项目选择/取消选择多个列表框中的项目 - C# Windows 窗体

下拉列表与可滚动面板中的组合框分开

将 Windows 窗体列表框保存到文本文件 C#

C#中Windows窗体工具栏

将图片框添加到 Windows 窗体