下面是一个去掉了 Vertical ScrollBar 的 ListBox 示例,它可以处理鼠标滚轮消息并自行滚动。
Vertical ScrollBar 默认被移除,除非ScrollAlwaysVisible 属性设置为true 或自定义公共VerticalScrollBar 属性设置为true。
LisBox 滚动设置其TopIndex 属性。 WndProc 中有一个最小/最大检查,其中处理了 WM_MOUSEWHEEL,以确保列表不会滚动超出其限制。
这是一种冗余检查,但如果您需要执行更复杂的计算来确定当前偏移量,它可能会派上用场。
当 ListBox 滚动时,它会引发自定义公共 Scroll 事件。如果需要,您可以创建自定义 EventArgs 类以将特定值传递给事件处理程序。在这里,我只是使用 TopIndex 属性同步所有 ListBox 控件。
请注意,鼠标滚轮将 ListBox 滚动 1 个项目,同时按下 SHIFT 会将滚动设置为 3 个项目。根据需要修改此行为。
using System.ComponentModel;
using System.Windows.Forms;
[DesignerCategory("code")]
public class ListBoxEx : ListBox
{
public event EventHandler<EventArgs> Scroll;
private const int WS_VSCROLL = 0x200000;
private const int WM_MOUSEWHEEL = 0x020A;
private const int MK_SHIFT = 0x0004;
private bool m_VerticalScrollBar = false;
public ListBoxEx() { }
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
if (!ScrollAlwaysVisible && !m_VerticalScrollBar) {
cp.Style &=~WS_VSCROLL;
}
return cp;
}
}
[DefaultValue(false)]
public bool VerticalScrollBar {
get => m_VerticalScrollBar;
set {
if (value != m_VerticalScrollBar) {
m_VerticalScrollBar = value;
RecreateHandle();
}
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg) {
case WM_MOUSEWHEEL:
var wparm = m.WParam.ToInt64();
int button = (short)wparm;
int delta = (int)(wparm >> 16);
int direction = Math.Sign(delta);
int steps = button == MK_SHIFT ? 3 : 1;
TopIndex = Math.Max(Math.Min(Items.Count - 1, TopIndex - (steps * direction)), 0);
Scroll?.Invoke(this, EventArgs.Empty);
m.Result = IntPtr.Zero;
break;
}
}
}
将此自定义控件的三个实例添加到一个窗体并订阅第一个的SelectedIndexChanged 事件(例如)。例如,
private void listBoxEx1_SelectedIndexChanged(object sender, EventArgs e)
{
var lb = (sender as ListBox);
if (listBoxEx2.Items.Count > lb.SelectedIndex) {
listBoxEx2.SelectedIndex = lb.SelectedIndex;
}
if (listBoxEx3.Items.Count > lb.SelectedIndex) {
listBoxEx3.SelectedIndex = lb.SelectedIndex;
}
}
现在,如果你想同步滚动三个控件,订阅第一个的自定义滚动事件:
private void listBoxEx1_Scroll(object sender, EventArgs e)
{
var lb = sender as ListBox;
listBoxEx2.TopIndex = lb.TopIndex;
listBoxEx3.TopIndex = lb.TopIndex;
}
它是这样工作的:
代码示例还处理具有不同项目数的 ListBox 控件
编辑:
如何使用:
ListBoxEx是一个自定义控件类。
要创建此控件:
- 向项目添加一个新的
Class 文件,将其命名为ListBoxEx。
- 用您在此处找到的类内容覆盖该文件中的类定义。
在此代码中添加 2 个 using 指令。
- 构建项目。
现在,在工具箱中,您可以找到新的 ListBoxEx 控件。
要复制此处显示的内容:
- 将它的 3 个实例拖放到一个表单上。
- 在设计器中,选择第一个对象 (
listBoxEx1)。
- 在 PropertyGrid 中,切换到事件 (⚡) 视图。找到
Scroll 和SelectedIndexChanged 事件并双击每个事件。它将为您创建事件处理程序。
- 将您在此处找到的事件处理程序的内容复制到刚刚创建的新事件处理程序中。
或者,订阅代码中的事件:
-
复制您在此处找到的 listBoxEx1_Scroll 和 listBoxEx1_SelectedIndexChanged 处理程序(包括它们的内容)并将它们粘贴到包含 ListBoxEx 控件的表单中。
-
将此添加到表单构造函数中,在InitializeComponent() 之后,以订阅listBoxEx1 的Scroll 和SelectedIndexChanged 事件:
listBoxEx1.Scroll += this.listBoxEx1_Scroll;
listBoxEx1.SelectedIndexChanged += this.listBoxEx1_SelectedIndexChanged;