C# 如何使用滑块在两个TreeView中同步滚动

C# 如何使用滑块在两个TreeView中同步滚动,c#,winforms,treeview,C#,Winforms,Treeview,我正在使用VisualStudio2010(C#)和Windows窗体应用程序 我有两个并列的treeview,我已经知道如何使用滚动条上的向上/向下按钮来同步滚动,但是当我使用滑块时,它不会移动另一个treeview。我举了一个listview示例,它可以工作,但同样的代码不适用于TreeView 到目前为止,我以主要形式: [DllImport("User32.dll")] public static extern int SendMessage(IntPtr hWnd, u

我正在使用VisualStudio2010(C#)和Windows窗体应用程序

我有两个并列的treeview,我已经知道如何使用滚动条上的向上/向下按钮来同步滚动,但是当我使用滑块时,它不会移动另一个treeview。我举了一个listview示例,它可以工作,但同样的代码不适用于TreeView

到目前为止,我以主要形式:

    [DllImport("User32.dll")]
    public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

    private void myListBox1_Scroll(ref Message m)
    {
        SendMessage(myListBox2.Handle, (uint)m.Msg, (uint)m.WParam, (uint)m.LParam);
    }
我创建了一个控件:

public partial class MyTreeView : TreeView
{
    public MyTreeView()
    {
        InitializeComponent();
    }

    public event ScrollEventHandler Scroll;
    public delegate void ScrollEventHandler(ref Message m);


    private const int WM_VSCROLL = 0x115;

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        if (m.Msg == WM_VSCROLL)
            if (Scroll != null)
            {
                Scroll(ref m);
            }

        base.WndProc(ref m);
    }

}
我在表格上加了两个


我可以使用相同的代码在树状视图中使用listivew控件,如果拖动滑块,该控件将起作用,但反过来,它仅适用于上下按钮。

我以前使用过
文本框
,但我认为解决方案也适用于您:

// Get/Set Scroll positions of a control handle
private unsafe Win32.POINT GetScrollPos(System.IntPtr myHandle)
{
   Win32.POINT res = new Win32.POINT();
   IntPtr ptr = new IntPtr(&res);
   Win32.SendMessage(myHandle, Win32.EM_GETSCROLLPOS, 0, ptr);
   return res;
}

private unsafe void SetScrollPos(Win32.POINT point, System.IntPtr myHandle)
{
   IntPtr ptr = new IntPtr(&point);
   Win32.SendMessage(myHandle, Win32.EM_SETSCROLLPOS, 0, ptr);
}
Win32详细信息

public const int WM_USER = 0x400;
public const int EM_GETSCROLLPOS  = (WM_USER + 221);
public const int EM_SETSCROLLPOS  = (WM_USER + 222);

[StructLayout(LayoutKind.Sequential)]
public struct POINT 
{
   public int x;
   public int y;
}

[DllImport("user32")] public static extern int SendMessage(
    HWND hwnd, int wMsg, int wParam, IntPtr lParam);
然后连接到两个
列表视图
滚动事件并执行如下操作:

private void ListView1Scrolled(object sender, System.EventArgs e)
{
   SetScrollPos(GetScrollPos(ListView1.Handle), ListView2.Handle);
}

private void ListView2Scrolled(object sender, System.EventArgs e)
{
   SetScrollPos(GetScrollPos(ListView2.Handle), ListView1.Handle);
}

您可以使用user32.DLL中的
GetScrollPos
SetScrollPos
函数,而不是使用SendMessage并将DLL标记为不安全

我已经将代码包装到MyTreeView类中,因此它被很好地封装

您只需调用
AddLinkedTreeView
方法,如下所示:

treeView1.AddLinkedTreeView(treeView2);
这是MyTreeView类的源代码

public partial class MyTreeView : TreeView
{
    public MyTreeView() : base()
    {
    }

    private List<MyTreeView> linkedTreeViews = new List<MyTreeView>();

    /// <summary>
    /// Links the specified tree view to this tree view.  Whenever either treeview
    /// scrolls, the other will scroll too.
    /// </summary>
    /// <param name="treeView">The TreeView to link.</param>
    public void AddLinkedTreeView(MyTreeView treeView)
    {
        if (treeView == this)
            throw new ArgumentException("Cannot link a TreeView to itself!", "treeView");

        if (!linkedTreeViews.Contains(treeView))
        {
            //add the treeview to our list of linked treeviews
            linkedTreeViews.Add(treeView);
            //add this to the treeview's list of linked treeviews
            treeView.AddLinkedTreeView(this);

            //make sure the TreeView is linked to all of the other TreeViews that this TreeView is linked to
            for (int i = 0; i < linkedTreeViews.Count; i++)
            {
                //get the linked treeview
                var linkedTreeView = linkedTreeViews[i];
                //link the treeviews together
                if (linkedTreeView != treeView)
                    linkedTreeView.AddLinkedTreeView(treeView);
            }
        }
    }

    /// <summary>
    /// Sets the destination's scroll positions to that of the source.
    /// </summary>
    /// <param name="source">The source of the scroll positions.</param>
    /// <param name="dest">The destinations to set the scroll positions for.</param>
    private void SetScrollPositions(MyTreeView source, MyTreeView dest)
    {
        //get the scroll positions of the source
        int horizontal = User32.GetScrollPos(source.Handle, Orientation.Horizontal);
        int vertical = User32.GetScrollPos(source.Handle, Orientation.Vertical);
        //set the scroll positions of the destination
        User32.SetScrollPos(dest.Handle, Orientation.Horizontal, horizontal, true);
        User32.SetScrollPos(dest.Handle, Orientation.Vertical, vertical, true);
    }

    protected override void WndProc(ref Message m)
    {
        //process the message
        base.WndProc(ref m);

        //pass scroll messages onto any linked views
        if (m.Msg == User32.WM_VSCROLL || m.Msg == User32.WM_MOUSEWHEEL)
        {
            foreach (var linkedTreeView in linkedTreeViews)
            {
                //set the scroll positions of the linked tree view
                SetScrollPositions(this, linkedTreeView);
                //copy the windows message
                Message copy = new Message
                {
                    HWnd = linkedTreeView.Handle,
                    LParam = m.LParam,
                    Msg = m.Msg,
                    Result = m.Result,
                    WParam = m.WParam
                };
                //pass the message onto the linked tree view
                linkedTreeView.RecieveWndProc(ref copy);
            }                               
        }
    }

    /// <summary>
    /// Recieves a WndProc message without passing it onto any linked treeviews.  This is useful to avoid infinite loops.
    /// </summary>
    /// <param name="m">The windows message.</param>
    private void RecieveWndProc(ref Message m)
    {
        base.WndProc(ref m);
    }

    /// <summary>
    /// Imported functions from the User32.dll
    /// </summary>
    private class User32
    {
        public const int WM_VSCROLL = 0x115;
        public const int WM_MOUSEWHEEL = 0x020A;  

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar);

        [DllImport("user32.dll")]
        public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);
    }
}
公共部分类MyTreeView:TreeView
{
public MyTreeView():base()
{
}
私有列表linkedTreeViews=新列表();
/// 
///将指定的树视图链接到此树视图
///滚动,另一个也会滚动。
/// 
///树视图将链接。
public void AddLinkedTreeView(MyTreeView treeView)
{
if(treeView==此)
抛出新ArgumentException(“无法将TreeView链接到自身!”,“TreeView”);
如果(!LinkedTreeView.Contains(treeView))
{
//将树视图添加到我们的链接树视图列表中
linkedTreeViews.Add(treeView);
//将此添加到树视图的链接树视图列表中
AddLinkedTreeView(此);
//确保该树视图链接到该树视图链接到的所有其他树视图
for(int i=0;i

编辑:根据MinnesotaFat的建议添加了WM_MOUSEWHEEL消息的转发。

DoctaJonez的回答效果非常好。为完整起见,如果在
WndProc
方法中的
if
语句中添加另一个条件,则还可以处理鼠标滚轮滚动事件:

if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL)
并声明WM_鼠标滚轮:

private cont int WM_MOUSEWHEEL = 0x020A;

任何从.Net使用Win32的人都会发现它非常有用。这是一个很好的资源,我发现它非常宝贵。我什么时候叫'treeView1.AddLinkedTreeView(treeView2);'确切地我有.NET2.0并且没有TreeView的滚动事件。。。