简体   繁体   English

如何使用鼠标滚轮一次使DataGridView滚动一个项目?

[英]How can you make a DataGridView scroll one item at a time using the mouse wheel?

We'd like to override DataGridView's default behavior when using a mouse wheel with this control. 当使用具有此控件的鼠标滚轮时,我们想要覆盖DataGridView的默认行为。 By default, the DataGridView scrolls a number of rows equal the SystemInformation.MouseWheelScrollLines setting. 默认情况下,DataGridView滚动的行数等于SystemInformation.MouseWheelScrollLines设置。 What we'd like to do is scroll just one item at a time. 我们想要做的是一次只滚动一个项目。

(We display images in the DataGridView, which are somewhat large. Because of this scroll three rows (a typical system setting) is too much, often causing the user to scroll to items they can't even see.) (我们在DataGridView中显示图像,它们有点大。由于这个滚动三行(典型的系统设置)太多,经常导致用户滚动到他们甚至看不到的项目。)

I've tried a couple things already and haven't had much success so far. 我已经尝试了几件事,到目前为止还没有取得多大成功。 Here are some issues I've run into: 以下是我遇到的一些问题:

  1. You can subscribe to MouseWheel events but there's no way to mark the event as handled and do my own thing. 您可以订阅MouseWheel事件,但无法将事件标记为已处理并执行自己的操作。

  2. You can override OnMouseWheel but this never appears to be called. 您可以覆盖OnMouseWheel,但似乎从未调用过。

  3. You might be able to correct this in the base scrolling code but it sounds like a messy job since other types of scrolling (eg using the keyboard) come through the same pipeline. 您可能能够在基本滚动代码中更正此问题,但这听起来像一个混乱的工作,因为其他类型的滚动(例如使用键盘)来自同一个管道。

Anyone have a good suggestion? 有人有个好主意吗?

Here's the final code, using the wonderful answer given: 这是最终的代码,使用给出的精彩答案:

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

I just did a little scrounging and testing of my own. 我只是对我自己进行了一些调查和测试。 I used Reflector to investigate and discovered a couple things. 我用Reflector调查并发现了几件事。 The MouseWheel event provides a MouseEventArgs parameter, but the OnMouseWheel() override in DataGridView casts it to Handled MouseEventArgs . MouseWheel事件提供MouseEventArgs参数,但DataGridViewOnMouseWheel()重写将其强制转换为Handled MouseEventArgs This also works when handling the MouseWheel event. 这在处理MouseWheel事件时也有效。 OnMouseWheel() does indeed get called, and it is in DataGridView 's override that it uses SystemInformation.MouseWheelScrollLines . 确实调用了OnMouseWheel() ,它在DataGridView的覆盖中使用了SystemInformation.MouseWheelScrollLines

So: 所以:

  1. You could indeed handle the MouseWheel event, casting MouseEventArgs to HandledMouseEventArgs and set Handled = true , then do what you want. 您确实可以处理MouseWheel事件,将MouseEventArgsHandledMouseEventArgs并设置Handled = true ,然后执行您想要的操作。

  2. Subclass DataGridView , override OnMouseWheel() yourself, and try to recreate all the code I read here in Reflector except for replacing SystemInformation.MouseWheelScrollLines with 1 . 子类DataGridView ,自己重写OnMouseWheel() ,并尝试重新创建我在Reflector中读到的所有代码,除了用1替换SystemInformation.MouseWheelScrollLines

The latter would be a huge pain because it uses a number of private variables (including references to the ScrollBar s) and you'd have replace some with your own and get/set others using Reflection. 后者将是一个巨大的痛苦,因为它使用了许多私有变量(包括对ScrollBar的引用),你将用自己的替换一些,并使用Reflection获取/设置其他变量。

I would subclass the DataGridView into my own custom control (you know, add a new Windows Forms --> Custom Control file and change the base class from Control to DataGridView). 我将DataGridView子类化为我自己的自定义控件(你知道,添加一个新的Windows窗体 - >自定义控件文件并将基类从Control更改为DataGridView)。

public partial class MyDataGridView : DataGridView

Then override the WndProc method and substitute something like so: 然后重写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);
    }
}

Of course, you'll have the check that you don't set FirstDisplayedScrollingRowIndex to a number outside of the range of your grid etc. But this works quite well! 当然,您将检查是否未将FirstDisplayedScrollingRowIndex设置为网格范围之外的数字等。但这样做效果很好!

Richard 理查德

Overriding OnMouseWheel and not calling base.OnMouseWheel should work. 覆盖OnMouseWheel而不是调用base.OnMouseWheel应该可以工作。 Some wheel mice have special settings that you may need to set yourself for it to work properly. 某些滚轮鼠标有特殊设置,您可能需要自行设置它才能正常工作。 See this post http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1 请参阅此文章http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1

UPDATE: Since I've now learned that the DataGridView has a MouseWheel event, I've added a second, simpler override. 更新:由于我现在已经知道DataGridView有一个MouseWheel事件,我添加了第二个更简单的覆盖。

One way to accomplish this is to subclass the DataGridView and override the WndProc to add special handling of the WM_MOUSEWHEEL message. 实现此目的的一种方法是WndProc DataGridView并覆盖WndProc以添加对WM_MOUSEWHEEL消息的特殊处理。

This example catches the mouse wheel movement and replaces it with a call to SendKeys.Send . 此示例捕获鼠标滚轮移动并通过调用SendKeys.Send替换它。

(This is a little different than just scrolling, since it also selects the next/previous row of the DataGridView . But it works.) (这与滚动略有不同,因为它还会选择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);
    }
}

2nd take (with the same caveats as mentioned above): 第二次采取(与上述相同的警告):

public class MyDataGridView : DataGridView
{
    protected override void OnMouseWheel(MouseEventArgs e)
    {
        if (e.Delta < 0)
            SendKeys.Send("{DOWN}");
        else
            SendKeys.Send("{UP}");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM