Scrolling WinForms TreeViews in Sync

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

1 thought on “Scrolling WinForms TreeViews in Sync

  1. Pingback: Synchronizing Scrolling WinForms TreeViews « devioblog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.