在.net同步列表视图 [英] Synchronized ListViews in .Net

查看:99
本文介绍了在.net同步列表视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个控制联系在一起的观点从一个ListView控件到另一个,这样,当主ListView的滚动,孩子的ListView视图更新以匹配。

到目前为止,我已经能够让孩子列表视图主滚动按钮被点击时更新自己的看法。问题是,单击并拖动滚动条本身的时候,孩子列表视图不会更新。我看着使用间谍++正在发送的消息和正确的消息越来越发送。

下面是我目前的code:

 公共部分类LinkedListViewControl:ListView控件
{
[的DllImport(User32.dll中)]
私人静态外部布尔SendMessage函数(IntPtr的HWND,UInt32的味精,IntPtr的wParam中,IntPtr的lParam的);

[的DllImport(User32.dll中)]
私人静态外部布尔ShowScrollBar(IntPtr的HWND,INT wBar,布尔bShow);

[的DllImport(user32.dll中)
私人静态外部INT SetScrollPos(IntPtr的的HWND,诠释wBar,诠释非营利组织,BOOL bRedraw);

私人const int的WM_HSCROLL =量0x114;

私人const int的SB_HORZ = 0;
私人const int的SB_VERT = 1;
私人const int的SB_CTL = 2;
私人const int的SB_BOTH = 3;
私人const int的SB_THUMBPOSITION = 4;
私人const int的SB_THUMBTRACK = 5;
私人const int的SB_ENDSCROLL = 8;

公共LinkedListViewControl()
{
的InitializeComponent();
}

私人只读表< ListView控件> _linkedListViews =新的名单,其中,ListView控件>();

    公共无效AddLinkedView(ListView控件的ListView)
{
如果(!_linkedListViews.Contains(ListView控件))
{
_linkedListViews.Add(ListView控件);

HideScrollBar(ListView控件);
}
}

公共BOOL RemoveLinkedView(ListView控件的ListView)
{
返回_linkedListViews.Remove(ListView控件);
}

私人无效HideScrollBar(ListView控件的ListView)
{
//确保列表视图是滚动
listView.Scrollable = TRUE;

//然后隐藏滚动条
ShowScrollBar(listView.Handle,SB_BOTH,假);
}

保护覆盖无效的WndProc(参考消息MSG)
{
如果(_linkedListViews.Count大于0)
{
//寻找WM_HSCROLL消息
如果(msg.Msg == WM_HSCROLL)
{
的foreach(在_linkedListViews的ListView视图)
{
SendMessage函数(view.Handle,WM_HSCROLL,msg.WParam,IntPtr.Zero);
}
}
}
}
}
 

根据这个帖子上的MS技术论坛我试图捕捉和处理SB_THUMBTRACK事件:

 保护覆盖无效的WndProc(参考消息MSG)
{
如果(_linkedListViews.Count大于0)
{
//寻找WM_HSCROLL消息
如果(msg.Msg == WM_HSCROLL)
{
Int16的HI =(INT16)((INT)msg.WParam>> 16);
Int16的LO =(INT16)msg.WParam;

的foreach(在_linkedListViews的ListView视图)
{
如果(LO == SB_THUMBTRACK)
{
SetScrollPos(view.Handle,SB_HORZ,真);

INT的wParam = 4 + 0x10000的*您好;
SendMessage函数(view.Handle,WM_HSCROLL,(IntPtr的)(wParam中),IntPtr.Zero);
}
				    其他
{
SendMessage函数(view.Handle,WM_HSCROLL,msg.WParam,IntPtr.Zero);
}
}
}
}

//传递消息到默认处理程序。
base.WndProc(REF MSG);
}
 

这将更新孩子的ListView滚动条的位置,但并没有改变孩子的实际看法。

所以,我的问题是:

  1. 是否有可能更新子列表视图当主ListView的滚动条拖动?
  2. 如果是这样,如何​​
解决方案

我想要做同样的事情,和摸索后,我发现你的code在这里,帮助,当然,也没有解决问题。但玩弄后,我已经找到了解决办法。

当我意识到,由于滚动按钮的工作,你可以用它来使滑块工作的重点来了。换句话说,当SB_THUMBTRACK事件到来时,我发出了多次SB_LINELEFT和SB_LINERIGHT事件,直到我的孩子的ListView靠拢的地方船长。是的,这是不完美的,但它足够接近。

在我的情况,我的主人的ListView被称为reportView,而我的孩子的ListView被称为summaryView。这是我的中肯code:

 公共类MyListView:ListView控件
{
公共事件ScrollEventHandler HScrollEvent;

保护覆盖无效的WndProc(参考System.Windows.Forms.Message MSG)
{
如果(msg.Msg == WM_HSCROLL和放大器;&安培;!HScrollEvent = NULL)
HScrollEvent(这一点,新ScrollEventArgs(ScrollEventType.ThumbTrack,(INT)msg.WParam));

base.WndProc(REF MSG);
}
}
 

然后事件处理程序本身:

  reportView.HScrollEvent + =新ScrollEventHandler((发件人,E)=> {
如果((USHORT)e.NewValue!= SB_THUMBTRACK)
SendMessage函数(summaryView.Handle,WM_HSCROLL,(IntPtr的)e.NewValue,IntPtr.Zero);
	其他 {
INT newPos = e.NewValue>> 16;
INT oldPos = GetScrollPos(reportView .handle的,SB_HORZ);
INT POS = GetScrollPos(summaryView.Handle,SB_HORZ);
INT LST;

如果(POS!= newPos)
如果(POS&L​​T; newPos和放大器;&安培; oldPos< newPos)做{LST = POS机; SendMessage函数(summaryView.Handle,WM_HSCROLL,(IntPtr的)SB_LINERIGHT,IntPtr.Zero); }而((POS = GetScrollPos(summaryView.Handle,SB_HORZ))< newPos和放大器;&安培; POS = LST!);
否则,如果(POS> newPos和放大器;&安培; oldPos> newPos)做{LST = POS机; SendMessage函数(summaryView.Handle,WM_HSCROLL,(IntPtr的)SB_LINELEFT,IntPtr.Zero); }而((POS = GetScrollPos(summaryView.Handle,SB_HORZ))> newPos和放大器;&安培; POS = LST!);
}
});
 

很抱歉的同时,奇格式回路存在,但是这就是我preFER以这样的code的东西。

接下来的问题是摆脱在孩子的ListView滚动条。我注意到有一个名为HideScrollBar方法。这并没有真正为我工作。我发现,在我的情况下,一个更好的解决办法离开滚动条出现,但覆盖它来代替。我这样做的列标题为好。我只要将我的孩子控制了主控制下,以覆盖列标题。然后我伸子掉下来包含它的面板。然后提供一点我一起含面板的边缘的边框,我扔在一个控制来支付我的孩子的ListView可见底部边缘。它最终看起来相当漂亮。

我还添加了一个事件处理程序来同步变化的列宽,如:

  reportView.ColumnWidthChanging + =新ColumnWidthChangingEventHandler((发件人,E)=> {
summaryView.Columns [e.ColumnIndex] .WIDTH = e.NewWidth;
});
 

尽管这一切似乎有点杂牌的,它为我工作。

I'm working on a control to tie together the view from one ListView to another so that when the master ListView is scrolled, the child ListView view is updated to match.

So far I've been able to get the child ListViews to update their view when the master scrollbar buttons are clicked. The problem is that when clicking and dragging the ScrollBar itself, the child ListViews are not updated. I've looked at the messages being sent using Spy++ and the correct messages are getting sent.

Here is my current code:

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

Based on this post on the MS Tech Forums I tried to capture and process the SB_THUMBTRACK event:

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

This will update the location of the child ListView ScrollBar but does not change the actual view in the child.

So my questions are:

  1. Is it possible to update the child ListViews when the master ListView ScrollBar is dragged?
  2. If so, how?

解决方案

I wanted to do the same thing, and after searching around I found your code here, which helped, but of course didn't solve the problem. But after playing around with it, I have found a solution.

The key came when I realized that since the scroll buttons work, that you can use that to make the slider work. In other words, when the SB_THUMBTRACK event comes in, I issue repeated SB_LINELEFT and SB_LINERIGHT events until my child ListView gets close to where the master is. Yes, this isn't perfect, but it works close enough.

In my case, my master ListView is called "reportView", while my child ListView is called "summaryView". Here's my pertinent code:

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

And then the event handler itself:

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

Sorry about the odd formatting of the while loops there, but that's how I prefer to code things like that.

The next problem was getting rid of the scroll bars in the child ListView. I noticed you had a method called HideScrollBar. This didn't really work for me. I found a better solution in my case was leaving the scroll bar there, but "covering" it up instead. I do this with the column header as well. I just slide my child control up under the master control to cover the column header. And then I stretch the child to fall out of the panel that contains it. And then to provide a bit of a border along the edge of my containing panel, I throw in a control to cover the visible bottom edge of my child ListView. It ends up looking rather nice.

I also added an event handler to sync changing column widths, as in:

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

While this all seems a bit of a kludge, it works for me.

这篇关于在.net同步列表视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆