.Net中的同步ListView

我正在使用一个控件将视图从一个ListView绑定到另一个ListView,以便在滚动主ListView时,更新子ListView视图以匹配。

到目前为止,我已经能够让孩子ListViews在点击主滚动条按钮时更新他们的视图。 问题是当单击并拖动ScrollBar本身时,子ListViews不会更新。 我查看了使用Spy ++发送的消息,并发送了正确的消息。

这是我目前的代码:

public partial class LinkedListViewControl : ListView { [DllImport("User32.dll")] private static extern bool SendMessage(IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam); [DllImport("User32.dll")] private static extern bool ShowScrollBar(IntPtr hwnd, int wBar, bool bShow); [DllImport("user32.dll")] private static extern int SetScrollPos(IntPtr hWnd, int wBar, int nPos, bool bRedraw); private const int WM_HSCROLL = 0x114; private const int SB_HORZ = 0; private const int SB_VERT = 1; private const int SB_CTL = 2; private const int SB_BOTH = 3; private const int SB_THUMBPOSITION = 4; private const int SB_THUMBTRACK = 5; private const int SB_ENDSCROLL = 8; public LinkedListViewControl() { InitializeComponent(); } private readonly List _linkedListViews = new List(); public void AddLinkedView(ListView listView) { if (!_linkedListViews.Contains(listView)) { _linkedListViews.Add(listView); HideScrollBar(listView); } } public bool RemoveLinkedView(ListView listView) { return _linkedListViews.Remove(listView); } private void HideScrollBar(ListView listView) { //Make sure the list view is scrollable listView.Scrollable = true; //Then hide the scroll bar ShowScrollBar(listView.Handle, SB_BOTH, false); } protected override void WndProc(ref Message msg) { if (_linkedListViews.Count > 0) { //Look for WM_HSCROLL messages if (msg.Msg == WM_HSCROLL) { foreach (ListView view in _linkedListViews) { SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero); } } } } } 

根据MS Tech论坛上的这篇文章 ,我试图捕获并处理SB_THUMBTRACK事件:

  protected override void WndProc(ref Message msg) { if (_linkedListViews.Count > 0) { //Look for WM_HSCROLL messages if (msg.Msg == WM_HSCROLL) { Int16 hi = (Int16)((int)msg.WParam >> 16); Int16 lo = (Int16)msg.WParam; foreach (ListView view in _linkedListViews) { if (lo == SB_THUMBTRACK) { SetScrollPos(view.Handle, SB_HORZ, hi, true); int wParam = 4 + 0x10000 * hi; SendMessage(view.Handle, WM_HSCROLL, (IntPtr)(wParam), IntPtr.Zero); } else { SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero); } } } } // Pass message to default handler. base.WndProc(ref msg); } 

这将更新子ListView ScrollBar的位置,但不会更改子项中的实际视图。

所以我的问题是:

  1. 拖动主ListView ScrollBar时是否可以更新子ListView?
  2. 如果是这样,怎么样?

我想做同样的事情,在搜索之后我在这里找到了你的代码,这有帮助,但当然没有解决问题。 但在玩完之后,我找到了解决方案。

当我意识到由于滚动按钮有效时,你可以使用它来使滑块工作。 换句话说,当SB_THUMBTRACK事件进入时,我发出重复的SB_LINELEFT和SB_LINERIGHT事件,直到我的子ListView接近master的位置。 是的,这不是完美的,但它足够接近。

在我的例子中,我的主ListView被称为“reportView”,而我的子ListView被称为“summaryView”。 这是我的相关代码:

 public class MyListView : ListView { public event ScrollEventHandler HScrollEvent; protected override void WndProc(ref System.Windows.Forms.Message msg) { if (msg.Msg==WM_HSCROLL && HScrollEvent != null) HScrollEvent(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, (int)msg.WParam)); base.WndProc(ref msg); } } 

然后是事件处理程序本身:

 reportView.HScrollEvent += new ScrollEventHandler((sender,e) => { if ((ushort) e.NewValue != SB_THUMBTRACK) SendMessage(summaryView.Handle, WM_HSCROLL, (IntPtr) e.NewValue, IntPtr.Zero); else { int newPos = e.NewValue >> 16; int oldPos = GetScrollPos(reportView .Handle, SB_HORZ); int pos = GetScrollPos(summaryView.Handle, SB_HORZ); int lst; if (pos != newPos) if (posnewPos && oldPos>newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINELEFT, IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) > newPos && pos!=lst); } }); 

对于那里的while循环的奇怪格式感到抱歉,但这就是我喜欢编写类似的东西的方式。

下一个问题是摆脱子ListView中的滚动条。 我注意到你有一个名为HideScrollBar的方法。 这对我来说并不适用。 在我的情况下,我发现了一个更好的解决方案是将滚动条留在那里,而是“覆盖”它。 我也使用列标题执行此操作。 我只是在主控件下滑动我的子控件以覆盖列标题。 然后我伸展孩子从包含它的面板中掉出来。 然后沿着包含面板的边缘提供一些边框,我抛出一个控件来覆盖我的子ListView的可见底边。 它最终看起来相当不错。

我还添加了一个事件处理程序来同步更改列宽,如下所示:

 reportView.ColumnWidthChanging += new ColumnWidthChangingEventHandler((sender,e) => { summaryView.Columns[e.ColumnIndex].Width = e.NewWidth; }); 

虽然这一切看起来都像是一块垃圾,但它对我有用。

这是猜想只是让精神流动,所以按照你的意愿:在主列表的滚动处理程序中,你可以调用子列表的滚动处理程序(从主服务器传递发送者和eventargs)?

将此添加到您的表单加载:

 masterList.Scroll += new ScrollEventHandler(this.masterList_scroll); 

哪个引用了这个:

 private void masterList_scroll(Object sender, System.ScrollEventArgs e) { childList_scroll(sender, e); } private void childList_scroll(Object sender, System.ScrollEventArgs e) { childList.value = e.NewValue } 

我将创建自己的类,inheritance自ListView以显示垂直和水平滚动事件。

然后我会在我的表单中创建滚动处理程序以同步两个控件

这是示例代码,它应该允许listview发布滚动事件:

 public class MyListView : System.Windows.Forms.ListView { const int WM_HSCROLL = 0x0114; const int WM_VSCROLL = 0x0115; private ScrollEventHandler evtHScroll_m; private ScrollEventHandler evtVScroll_m; public event ScrollEventHandler OnHScroll { add { evtHScroll_m += value; } remove { evtHScroll_m -= value; } } public event ScrollEventHandler OnHVcroll { add { evtVScroll_m += value; } remove { evtVScroll_m -= value; } } protected override void WndProc(ref System.Windows.Forms.Message msg) { if (msg.Msg == WM_HSCROLL && evtHScroll_m != null) { evtHScroll_m(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32())); } if (msg.Msg == WM_VSCROLL && evtVScroll_m != null) { evtVScroll_m(this, new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32())); } base.WndProc(ref msg); } 

现在处理表单中的滚动事件:

设置PInvoke方法以便能够向控件发送Windows消息:

 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int SendMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int iMsg, int iWParam, int iLParam); 

设置事件处理程序(lstMaster和lstChild是两个列表框):

 lstMaster.OnVScroll += new ScrollEventHandler(this.lstMaster_OnVScroll); lstMaster.OnHScroll += new ScrollEventHandler(this.lstMaster_OnHScroll); const int WM_HSCROLL = 0x0114; const int WM_VSCROLL = 0x0115; private void lstMaster_OnVScroll(Object sender, System.ScrollEventArgs e) { SendMessage(lstChild.Handle,WM_VSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); } private void lstMaster_OnHScroll(Object sender, System.ScrollEventArgs e) { SendMessage(lstChild.Handle,WM_HSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); } 

您的问题的一个天真的解决方案是处理父列表视图中的绘制消息并检查链接列表视图是否显示正确的数据。 如果没有,则通过调用EnsureVisible方法更新它们以显示正确的数据。