如何使用鼠标滚轮使DataGridView一次滚动一个项目?
在此控件上使用鼠标滚轮时,我们想覆盖DataGridView的默认行为。默认情况下,DataGridView滚动的行数等于SystemInformation.MouseWheelScrollLines设置。我们想做的是一次只滚动一个项目。
(我们在DataGridView中显示的图像有些大。由于此滚动,三行(典型的系统设置)太多了,经常导致用户滚动到他们甚至看不到的项目。)
我已经尝试了几件事,到目前为止还没有取得太大的成功。这是我遇到的一些问题:
- 我们可以订阅MouseWheel事件,但无法将事件标记为已处理并自行处理。
- 我们可以重写OnMouseWheel,但是似乎从未调用过它。
- 我们可能可以在基本的滚动代码中更正此问题,但由于其他类型的滚动(例如,使用键盘)滚动通过相同的管道,因此听起来听起来很麻烦。
有人有好的建议吗?
这是最终的代码,使用给出的精彩答案:
/// <summary> /// Handle the mouse wheel manually due to the fact that we display /// images, which don't work well when you scroll by more than one /// item at a time. /// </summary> /// /// <param name="sender"> /// sender /// </param> /// <param name="e"> /// the mouse event /// </param> private void mImageDataGrid_MouseWheel(object sender, MouseEventArgs e) { // Hack alert! Through reflection, we know that the passed // in event argument is actually a handled mouse event argument, // allowing us to handle this event ourselves. // See http://tinyurl.com/54o7lc for more info. HandledMouseEventArgs handledE = (HandledMouseEventArgs) e; handledE.Handled = true; // Do the scrolling manually. Move just one row at a time. int rowIndex = mImageDataGrid.FirstDisplayedScrollingRowIndex; mImageDataGrid.FirstDisplayedScrollingRowIndex = e.Delta < 0 ? Math.Min(rowIndex + 1, mImageDataGrid.RowCount - 1): Math.Max(rowIndex - 1, 0); }
解决方案
我会将DataGridView子类化为我自己的自定义控件(我们知道,添加一个新的Windows窗体->自定义控件文件,并将基类从Control更改为DataGridView)。
public partial class MyDataGridView : DataGridView
然后重写WndProc方法并替换类似以下内容:
protected override void WndProc(ref Message m) { if (m.Msg == 0x20a) { int wheelDelta = ((int)m.WParam) >> 16; // 120 = UP 1 tick // -120 = DOWN 1 tick this.FirstDisplayedScrollingRowIndex -= (wheelDelta / 120); } else { base.WndProc(ref m); } }
当然,我们需要检查一下是否未将FirstDisplayedScrollingRowIndex设置为超出网格等范围的数字。但这很好用!
理查德
重写OnMouseWheel而不调用base.OnMouseWheel应该可以工作。一些滚轮鼠标具有特殊设置,我们可能需要对其进行设置才能使其正常工作。看到这篇文章http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1
我只是做了一些自我检查和测试。我使用Reflector进行了调查,发现了几件事。 MouseWheel事件提供了MouseEventArgs参数,但是DataGridView中的OnMouseWheel()重写将其强制转换为HandledMouseEventArgs。在处理MouseWheel
事件时也可以使用。实际上,确实调用了OnMouseWheel(),并且在DataGridView的覆盖中使用了SystemInformation.MouseWheelScrollLines。
所以:
- 我们确实可以处理" MouseWheel"事件,将" MouseEventArgs"强制转换为" HandledMouseEventArgs"并设置" Handled = true",然后执行我们想要的操作。
- 子类化DataGridView,自己重写OnMouseWheel(),并尝试重新创建我在Reflector中阅读的所有代码,除了将SystemInformation.MouseWheelScrollLines替换为1之外。
后者将是一个巨大的痛苦,因为它使用了许多私有变量(包括对ScrollBar的引用),并且我们已经用自己的变量替换了一些变量,并使用了反射来获取/设置其他变量。
更新:由于我现在已经了解到DataGridView具有一个MouseWheel事件,因此我添加了第二个更简单的替代。
实现此目的的一种方法是子类化DataGridView并重写WndProc以添加对WM_MOUSEWHEEL消息的特殊处理。
这个例子捕获了鼠标滚轮的移动,并用对SendKeys.Send
的调用来代替。
(这与仅滚动略有不同,因为它还会选择" DataGridView"的下一行/上一行。但是它可以工作。)
public class MyDataGridView : DataGridView { private const uint WM_MOUSEWHEEL = 0x20a; protected override void WndProc(ref Message m) { if (m.Msg == WM_MOUSEWHEEL) { var wheelDelta = ((int)m.WParam) >> 16; if (wheelDelta < 0) { SendKeys.Send("{DOWN}"); } if (wheelDelta > 0) { SendKeys.Send("{UP}"); } return; } base.WndProc(ref m); } }
第二次服用(与上述相同的警告):
public class MyDataGridView : DataGridView { protected override void OnMouseWheel(MouseEventArgs e) { if (e.Delta < 0) SendKeys.Send("{DOWN}"); else SendKeys.Send("{UP}"); } }