For a WinForms application that implements the comparison of hierarchical data I needed to synchronize the scrollbar position of two TreeView controls: if the user moves the scrollbar in one TreeView, the other TreeView should move to the same position.
A first approach of the solution is sketched under this question on SO which subclasses the TreeView class and responds to the WM_VSCROLL message. Other discussions pointed to a solution which is currently not available, but can be fetched from the Wayback Machine.
The subclassed TreeView interacts with the Windows TreeView using the GetScrollInfo and SetScrollInfo functions of the Win32 API and the SCROLLINFO structure.
I extended the code I had found to also handle the WM_HSCROLL and WM_MOUSEWHEEL messages by implementing the WndProc() method:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case NativeMethods.WM_VSCROLL:
case NativeMethods.SBM_SETSCROLLINFO:
RaiseScrollVEvent(m.HWnd);
break;
case NativeMethods.WM_HSCROLL:
RaiseScrollHEvent(m.HWnd);
break;
case NativeMethods.WM_MOUSEWHEEL:
RaiseScrollVEvent(m.HWnd);
RaiseScrollHEvent(m.HWnd);
break;
}
}
The RaiseScroll?Event() methods retrieve the scrollbar position (vertical or horizontal) of the tree view and raise a corresponding event in the Form’s code:
private void RaiseScrollVEvent(IntPtr handle)
{
ScrollInfoStruct si = new ScrollInfoStruct();
si.fMask = NativeMethods.SIF_ALL;
si.cbSize = Marshal.SizeOf(si);
NativeMethods.GetScrollInfo(handle, NativeMethods.SB_VERT, ref si);
ScrollEventArgs e = new ScrollEventArgs();
e.ScrollInfo = si;
OnScrollV(e);
}
To adjust the scrollbar position of a tree view, we need a ScrollToPosition?() method like this:
public void ScrollToPositionV(ScrollInfoStruct si)
{
ScrollInfoStruct siOwn = new ScrollInfoStruct();
siOwn.fMask = NativeMethods.SIF_ALL;
siOwn.cbSize = Marshal.SizeOf(si);
NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SB_VERT,
ref siOwn);
if (siOwn.nPos != si.nPos)
{
NativeMethods.SetScrollInfo(this.Handle.ToInt32(),
NativeMethods.SB_VERT, ref si, true);
NativeMethods.SendMessageLong(this.Handle.ToInt32(),
NativeMethods.WM_VSCROLL,
NativeMethods.SB_THUMBTRACK + 0x10000 * si.nPos, 0);
}
}