我正在使用一个控件将视图从一个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<ListView> _linkedListViews = new List<ListView>();
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论坛上的this post,我尝试捕获并处理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的位置,但不会更改子项中的实际视图。
所以我的问题是:
答案 0 :(得分: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 (pos<newPos && oldPos<newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINERIGHT,IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) < newPos && pos!=lst);
else if (pos>newPos && 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;
});
虽然这一切看起来都像是一块垃圾,但它对我有用。
答案 1 :(得分:1)
这是猜想只是为了让精神流动,所以请按照你的意愿: 在主列表的滚动处理程序中,是否可以调用子列表的滚动处理程序(从主服务器传递发送者和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
}
答案 2 :(得分:1)
我将创建自己的类,继承自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);
}
答案 3 :(得分:0)
您问题的一个天真的解决方案是处理父列表视图中的绘制消息,并检查链接列表视图是否显示正确的数据。如果没有,则通过调用EnsureVisible方法更新它们以显示正确的数据。