Как синхронизировать прокрутку в двух древовидных списках с помощью ползунка
Я использую Visual Studio 2010 (C#) и приложение Windows Forms.
У меня есть два дерева рядом, и я выяснил, как синхронизировать прокрутку с помощью кнопок вверх / вниз на полосе прокрутки, но когда я использую ползунок, он не перемещает другое дерево. Я взял пример списка просмотра, который работает, но тот же код не работает для древовидных представлений.
Пока что в основном виде:
[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 для управления древовидной структурой, и это будет работать, если вы перетаскиваете ползунок, но наоборот, он работает только с кнопками вверх-вниз.
3 ответа
Вместо того, чтобы использовать SendMessage и пометить вашу DLL как небезопасную, вы можете использовать GetScrollPos
а также SetScrollPos
функции от user32.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);
}
}
Редактировать: Добавлена пересылка сообщения WM_MOUSEWHEEL в соответствии с предложением MinnesotaFat.
Я сделал это с TextBoxes
раньше, но я думаю, что решение должно работать и для вас:
// 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);
Затем просто прикреплен к обоим ListView
прокрутите события и сделайте что-то вроде этого:
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);
}
Ответ DoctaJonez работает на удивление. Для полноты, если вы добавите еще одно условие к if
заявление в WndProc
Метод, вы также можете обрабатывать события прокрутки колесика мыши:
if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL)
И объявить WM_MOUSEWHEEL:
private cont int WM_MOUSEWHEEL = 0x020A;