Scrolling WinForms TreeViews in Sync

January 16, 2012

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


Windows 7 and DirectX APIs for .Net

August 18, 2010

For those who want to access Windows 7 features or DirectX from .Net, MSDN provides its Windows® API Code Pack for Microsoft® .NET Framework (3.5 SP1). Additional downloads may be necessary as sketched in the article.

For other APIs, see my previous posts.


Managed Windows API library for .Net

February 18, 2009

I mentioned accessing native Win32 APIs by .Net some time ago.

Today I came across a Sourceforge project called Managed Windows API, which provides a set of class to access various aspects of Win32. A couple of tools illustrate the use of the library.

I like TreeSizeSharp, a re-implementation of the TreeSize tool I often use if I find there’s not enough free space on my disks (again).


Native WinAPI with .Net

March 12, 2008

In a previous post I noted that some WinAPIs can be found on the web by searching for the string NativeMethods, which seems to be a Microsoft standard naming convention.

Today I came across pinvoke.net, a website dedicated to the documentation of WinAPI calls from .Net.

There is even a plugin for Visual Studio (2003 and 2005) that integrates pinvoke’s wiki contents into the IDE.