c# - 使 MouseWheel 事件从子级传递到父级

标签 c# winforms

我有一个 Panel,其中 AutoScroll 为真。该面板包含许多较小的面板,它们像瓷砖一样填充所有可用空间。当要显示的子面板太多时,我会按预期获得垂直滚动条。
这些“图 block ”中的每一个都有一些事件处理程序绑定(bind)到它们来处理 MouseDown/MouseUp/MouseMove,因为它们可以被拖来拖去。

我遇到的问题是鼠标滚轮滚动在父面板上不起作用,因为它没有焦点。我不能给它焦点,因为很可能我会在移动子面板时滚动,而子面板将有焦点,即使这样也需要解决方法,因为面板不喜欢焦点。

我一直在尝试(但未能成功)找到一种仅将鼠标滚轮事件从子级传播到父级的方法。
我在 Winforms 中读到,如果控件无法处理鼠标事件,它将冒泡到该控件的父级,然后到该控件的父级,依此类推,直到找到合适的处理程序。
考虑到这一点,我认为最好的解决方案是使用 WndProc 覆盖子面板上所有与滚动相关的事件,并将它们传递给父级,同时保持所有其他事件完好无损,但不可否认这不是我的强项,我迷路了。

我已经尝试了其他一些解决方案,例如让子面板对所有鼠标事件不可见,但您可能已经猜到这很糟糕。我读过有关实现消息过滤器的内容,但不理解。

下面的代码将为您提供面板及其子项的非常基本的示例:

private void Form1_Load(object sender, EventArgs e)
{
    Height = 600;
    Width = 300;

    Color[] colors = new Color[]{ Color.PowderBlue, Color.PeachPuff };

    Panel panel = new Panel()
    {
        Height = this.ClientSize.Height - 20,
        Width = 200,
        Top = 10,
        Left = 10,
        BackColor = Color.White,
        BorderStyle = BorderStyle.FixedSingle,
        AutoScroll = true
    };

    for (int i = 0; i < 10; i++)
    {
        Panel subPanel = new Panel()
        {
            Name = @"SubPanel " + i.ToString(),
            Height = 100,
            Width = panel.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 2,
            BackColor = colors[i % 2],
            Top = i * 100
        };
        subPanel.MouseClick += subPanel_MouseClick;
        panel.Controls.Add(subPanel);
    }

    Controls.Add(panel);
}

void subPanel_MouseClick(object sender, MouseEventArgs e)
{
    Panel panel = sender as Panel;
    Text = panel.Name;
}

这是我在自定义面板中覆盖 WndProc 的尝试:

class NoScrollPanel : Panel
{
    private const int WM_HSCROLL = 0x114;
    private const int WM_VSCROLL = 0x115;
    private const int MOUSEWHEEL = 0x020A;
    private const int KEYDOWN = 0x0100;

    protected override void WndProc(ref Message m)
    {
        if ((m.HWnd == Handle) && (m.Msg == MOUSEWHEEL || m.Msg == WM_VSCROLL || (m.Msg == KEYDOWN && (m.WParam == (IntPtr)40 || m.WParam == (IntPtr)35))))
        {
            PostMessage(Parent.Handle, m.Msg, m.WParam, m.LParam);
        }
        else
        {
            base.WndProc(ref m);
        }
    }

    [DllImport("User32.dll")]
    private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}

我们非常欢迎任何帮助或替代方法。谢谢!

最佳答案

在我这边@a-clymer 的解决方案不起作用,可能是不同的环境。目前我的问题没有直接、明确的答案,所以我尝试结合其他专业人士的一些想法并取得成功。

在我当前的项目中,我在面板中包含了一些输入控件。通过创建 ComboBox 的子类并覆盖其 WndProc,我设法使鼠标滚轮滚动面板而不是 ComboBox 项目:

public class ComboBoxWithParentMouseWheel : ComboBox
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

    const int WM_MOUSEWHEEL = 0x020A;

    //thanks to a-clymer's solution
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEWHEEL)
        {
            //directly send the message to parent without processing it
            //according to https://stackoverflow.com/a/19618100
            SendMessage(this.Parent.Handle, m.Msg, m.WParam, m.LParam);
            m.Result = IntPtr.Zero;
        }else base.WndProc(ref m);
    }
}

关于c# - 使 MouseWheel 事件从子级传递到父级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33129854/

相关文章:

c# - 类型 "..."的值不能添加到类型 'uielementcollection' 的集合或字典中

c# - 如何在不使用 "copy from screen"的情况下仅将图片框显示的内容捕获为位图?

c# - 当我将鼠标悬停在子控件上时会触发鼠标事件

C# Winforms WebBrowser 在默认浏览器中打开链接

c# - Windows 窗体 C# 设计器文件中的“枚举”类型中断

c# - 我需要了解为什么当我向标签添加值时我得到空值?

c# - 清除 TreeView

C# 合并运算符不替换 null 方法返回值?

c# - 我如何在 C# .NET(win7 手机)中读入 'nested' 和 'DataContractJsonSerializer' 的 Json 文件?

c# - 为什么 GetType().BaseClass 找不到基类?