2008-09-25 144 views
7

我们想在此控件中使用鼠标滚轮时重写DataGridView的默认行为。默认情况下,DataGridView滚动一些等于SystemInformation.MouseWheelScrollLines设置的行。我们想要做的是一次只滚动一个项目。我们在DataGridView中显示的图像有些大,因为这种滚动三行(一个典型的系统设置)太多了,通常会导致用户滚动到他们甚至看不到的项目。)如何让DataGridView使用鼠标滚轮一次滚动一个项目?

我已经尝试了几件事情,迄今为止还没有取得太大的成功。这里有一些问题,我碰到的:

  1. 您可以订阅鼠标滚轮事件,但没有办法,以纪念事件的处理,做我自己的事情。

  2. 您可以重写OnMouseWheel,但这似乎永远不会被调用。

  3. 您可能可以在基本滚动代码中更正此问题,但由于其他类型的滚动(例如,使用键盘)通过相同的管道传递,因此听起来像是一团乱七八糟的工作。

任何人都有很好的建议吗?

下面是最后的代码,使用给出精彩的答案:

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

回答

4

我只是做了一些小小的检查和测试。我用Reflector来调查和发现一些事情。 MouseWheel事件提供MouseEventArgs参数,但中的OnMouseWheel()覆盖会将其转换为HandledMouseEventArgs。这在处理MouseWheel事件时也有效。 OnMouseWheel()确实被调用,它在DataGridView的覆盖,它使用SystemInformation.MouseWheelScrollLines

所以:

  1. 你确实可以处理MouseWheel事件,铸造MouseEventArgsHandledMouseEventArgs并设置Handled = true,然后做你想做的。

  2. 子类DataGridView,覆盖OnMouseWheel()自己,并尝试重新所有我读到这里Reflector除了与1更换SystemInformation.MouseWheelScrollLines的代码。

后者将是一个巨大的痛苦,因为它使用了一些私有变量(包括对ScrollBar S变量)和你有你自己的替换一些和获取/设置其他使用反思。

1

我会继承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到数字的范围外的检查你的网格等,但这工作得很好!

理查德

+0

谢谢。我认为这会起作用,尽管我选择的答案更简单。 – 2008-09-25 20:44:25

1

UPDATE:既然我现在已经了解到,DataGridViewMouseWheel事件,我添加了一个第二,简单的覆盖。

完成此操作的一种方法是继承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}"); 
    } 
}