C# 如何使用鼠标滚轮使 DataGridView 一次滚动一项?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/135105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:01:22  来源:igfitidea点击:

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

提问by Ken Wootton

We'd like to override DataGridView's default behavior when using a mouse wheel with this control. By default, the DataGridView scrolls a number of rows equal the SystemInformation.MouseWheelScrollLines setting. What we'd like to do is scroll just one item at a time.

当在此控件上使用鼠标滚轮时,我们希望覆盖 DataGridView 的默认行为。默认情况下,DataGridView 滚动的行数等于 SystemInformation.MouseWheelScrollLines 设置。我们想要做的是一次只滚动一项。

(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.

  2. You can override OnMouseWheel but this never appears to be called.

  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 (e.g. using the keyboard) come through the same pipeline.

  1. 您可以订阅 MouseWheel 事件,但无法将事件标记为已处理并做我自己的事情。

  2. 您可以覆盖 OnMouseWheel 但这似乎永远不会被调用。

  3. 您可能能够在基本滚动代码中更正此问题,但这听起来像是一项混乱的工作,因为其他类型的滚动(例如使用键盘)来自相同的管道。

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

采纳答案by Joel B Fant

I just did a little scrounging and testing of my own. I used Reflectorto investigate and discovered a couple things. The MouseWheelevent provides a MouseEventArgsparameter, but the OnMouseWheel()override in DataGridViewcasts it to HandledMouseEventArgs. This also works when handling the MouseWheelevent. OnMouseWheel()does indeed get called, and it is in DataGridView's override that it uses SystemInformation.MouseWheelScrollLines.

我只是做了一些自己的搜索和测试。我使用Reflector进行调查并发现了一些事情。该MouseWheel事件提供了一个MouseEventArgs参数,但OnMouseWheel()覆盖 inDataGridView将其强制转换为. 这在处理事件时也有效。确实被调用,并且它使用的是 in的覆盖。HandledMouseEventArgsMouseWheelOnMouseWheel()DataGridViewSystemInformation.MouseWheelScrollLines

So:

所以:

  1. You could indeed handle the MouseWheelevent, casting MouseEventArgsto HandledMouseEventArgsand set Handled = true, then do what you want.

  2. Subclass DataGridView, override OnMouseWheel()yourself, and try to recreate all the code I read here in Reflectorexcept for replacing SystemInformation.MouseWheelScrollLineswith 1.

  1. 您确实可以处理该MouseWheel事件,将其强制转换MouseEventArgsHandledMouseEventArgs并设置Handled = true,然后做您想做的事情。

  2. Subclass DataGridView,覆盖OnMouseWheel()自己,并尝试重新创建我在Reflector 中阅读的所有代码,除了替换SystemInformation.MouseWheelScrollLines1.

The latter would be a huge pain because it uses a number of private variables (including references to the ScrollBars) and you'd have replace some with your own and get/set others using Reflection.

后者将是一个巨大的痛苦,因为它使用了许多私有变量(包括对ScrollBars 的引用),并且您必须用自己的变量替换一些并使用反射来获取/设置其他变量。

回答by ZeroBugBounce

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

理查德

回答by Nikki9696

Overriding OnMouseWheel and not calling base.OnMouseWheel should work. 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

覆盖 OnMouseWheel 而不是调用 base.OnMouseWheel 应该可以工作。某些滚轮鼠标具有特殊设置,您可能需要自己设置这些设置才能正常工作。请参阅此帖子http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1

回答by Christoffer Lette

UPDATE:Since I've now learned that the DataGridViewhas a MouseWheelevent, I've added a second, simpler override.

更新:因为我现在知道DataGridView有一个MouseWheel事件,所以我添加了第二个更简单的覆盖。

One way to accomplish this is to subclass the DataGridViewand override the WndProcto add special handling of the WM_MOUSEWHEELmessage.

实现此目的的一种方法是对 进行子类化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}");
    }
}