问题
Basically I have two list boxes syncListView1 and syncListView2 on the Form ( windows form application ) Im trying to make their scrollbar sync
I have googled some and i found this class but it does not seem to work :
class SyncListBox : System.Windows.Forms.ListBox
{
[Category("Action")]
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
public event ScrollEventHandler OnHorizontalScroll;
public event ScrollEventHandler OnVerticalScroll;
private const int SB_LINEUP = 0;
private const int SB_LINELEFT = 0;
private const int SB_LINEDOWN = 1;
private const int SB_LINERIGHT = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGELEFT = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_PAGERIGHT = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_PAGETOP = 6;
private const int SB_LEFT = 6;
private const int SB_PAGEBOTTOM = 7;
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;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetScrollInfo(
IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo);
private struct ScrollInfoStruct
{
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
protected override void WndProc(ref System.Windows.Forms.Message msg)
{
if (msg.Msg == WM_HSCROLL)
{
if (OnHorizontalScroll != 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);
OnHorizontalScroll(this, sargs);
}
}
}
if (msg.Msg == WM_VSCROLL)
{
if (OnVerticalScroll != 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);
OnVerticalScroll(this, sargs);
}
}
}
base.WndProc(ref msg);
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// scrolled
//
this.Size = new System.Drawing.Size(120, 95);
this.ResumeLayout(false);
}
}
and in the code behind :
private void syncListView2_OnVerticalScroll(object sender, ScrollEventArgs e)
{
syncListView1.TopIndex = syncListView2.TopIndex;
}
private void syncListView1_OnVerticalScroll(object sender, ScrollEventArgs e)
{
syncListView2.TopIndex = syncListView1.TopIndex;
}
回答1:
This is really bad practice to copy/paste code from article and don't even bother to read everything about the code and user reviews. You are not the first one who get stuck in this place....because one user already commented the problem
So what you probably missed is to subscribe to events. There is 2 options to do that:
- In designer window select first list box. Then in properties window select events tab(1.) and look for OnVerticalScroll event. When you find it click drop down button(2.). The list should contain at least 2 options syncListView1_OnVerticalScroll and syncListView1_OnVerticalScroll. So select
syncListView1_OnVerticalScroll. Do the same thing with with second list box but selectsyncListView1_OnVerticalScrol2

In form where you have list boxes open code view. There should be constructor where is
InitializeComponent()method called. After that method add following code lines.syncListView1.OnVerticalScroll += this.syncListView1_OnVerticalScroll; syncListView2.OnVerticalScroll += this.syncListView2_OnVerticalScroll;
回答2:
You don't need derived class SyncListBox
simply attach eventhandlers as follow for both the listboxes in windows form InitializeComponent() or in windows form constructor
EventHandler handler = (s,e) =>{
if (s == syncListView1)
syncListView2.TopIndex = syncListView1.TopIndex;
if (s == syncListView2)
syncListView1.TopIndex = syncListView2.TopIndex;
};
this.syncListView1.MouseCaptureChanged += handler;
this.syncListView2.MouseCaptureChanged += handler;
this.syncListView1.SelectedIndexChanged += handler;
this.syncListView2.SelectedIndexChanged += handler;
回答3:
Another example...this one will let you keep the original .Net ListBoxes in place, just pass them into the Constructor of SyncListBoxes in the Load() event of the Form. This will handle vertical scrolls, mouse wheel scrolls, and when the ListBoxes scroll as a result of keyboard input (via the SelectedIndexChanged event):
public partial class Form1 : Form
{
private SyncListBoxes _SyncListBoxes = null;
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}
private void Form1_Load(object sender, EventArgs e)
{
this._SyncListBoxes = new SyncListBoxes(this.syncListView1, this.syncListView2);
}
}
public class SyncListBoxes
{
private ListBox _LB1 = null;
private ListBox _LB2 = null;
private ListBoxScroll _ListBoxScroll1 = null;
private ListBoxScroll _ListBoxScroll2 = null;
public SyncListBoxes(ListBox LB1, ListBox LB2)
{
if (LB1 != null && LB1.IsHandleCreated && LB2 != null && LB2.IsHandleCreated &&
LB1.Items.Count == LB2.Items.Count && LB1.Height == LB2.Height)
{
this._LB1 = LB1;
this._ListBoxScroll1 = new ListBoxScroll(LB1);
this._ListBoxScroll1.Scroll += _ListBoxScroll1_VerticalScroll;
this._LB2 = LB2;
this._ListBoxScroll2 = new ListBoxScroll(LB2);
this._ListBoxScroll2.Scroll += _ListBoxScroll2_VerticalScroll;
this._LB1.SelectedIndexChanged += _LB1_SelectedIndexChanged;
this._LB2.SelectedIndexChanged += _LB2_SelectedIndexChanged;
}
}
private void _LB1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this._LB2.TopIndex != this._LB1.TopIndex)
{
this._LB2.TopIndex = this._LB1.TopIndex;
}
if (this._LB2.SelectedIndex != this._LB1.SelectedIndex)
{
this._LB2.SelectedIndex = this._LB1.SelectedIndex;
}
}
private void _LB2_SelectedIndexChanged(object sender, EventArgs e)
{
if (this._LB1.TopIndex != this._LB2.TopIndex)
{
this._LB1.TopIndex = this._LB2.TopIndex;
}
if (this._LB1.SelectedIndex != this._LB2.SelectedIndex)
{
this._LB1.SelectedIndex = this._LB2.SelectedIndex;
}
}
private void _ListBoxScroll1_VerticalScroll(ListBox LB)
{
if (this._LB2.TopIndex != this._LB1.TopIndex)
{
this._LB2.TopIndex = this._LB1.TopIndex;
}
}
private void _ListBoxScroll2_VerticalScroll(ListBox LB)
{
if (this._LB1.TopIndex != this._LB2.TopIndex)
{
this._LB1.TopIndex = this._LB2.TopIndex;
}
}
private class ListBoxScroll : NativeWindow
{
private ListBox _LB = null;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20a;
public event dlgListBoxScroll Scroll;
public delegate void dlgListBoxScroll(ListBox LB);
public ListBoxScroll(ListBox LB)
{
this._LB = LB;
this.AssignHandle(LB.Handle);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_VSCROLL:
case WM_MOUSEWHEEL:
if (this.Scroll != null)
{
this.Scroll(_LB);
}
break;
}
}
}
}
回答4:
Change your listboxes type to SyncListBox and attach to OnVerticalScroll event.
//Omitted for brevity
private SyncListBox listBox1;
private SyncListBox listBox2;
//Omitted for brevity
this.listBox1 = new WindowsFormsApplication1.SyncListBox();
this.listBox2 = new WindowsFormsApplication1.SyncListBox();
//Omitted for brevity
listBox1.OnVerticalScroll += listBox1_OnVerticalScroll;
listBox2.OnVerticalScroll += listBox2_OnVerticalScroll;
//Event handlers
void listBox2_OnVerticalScroll(object sender, ScrollEventArgs e)
{
listBox1.TopIndex = listBox2.TopIndex;
}
void listBox1_OnVerticalScroll(object sender, ScrollEventArgs e)
{
listBox2.TopIndex = listBox1.TopIndex;
}
来源:https://stackoverflow.com/questions/27945992/scrolling-two-listboxes-windows-form-at-same-time