我有一个`Panel`,其中`AutoScroll`为true。该面板包含许多填充所有可用空间的较小面板,如平铺。当子面板过多无法显示时,我会得到垂直滚动条,这是预期的。
每个“瓷砖”都有一些事件处理程序与它们绑定,以处理MouseDown / MouseUp / MouseMove,因为它们可以拖动。
我遇到的问题是,鼠标滚轮在父面板上不起作用,因为它没有焦点。我无法给它焦点,因为很可能我正在移动子面板时进行滚动,子面板将代替它具有焦点,即使这样也需要解决问题,因为面板不喜欢焦点。
我一直在尝试(并失败)找到一种方法,仅从子级传播鼠标滚轮事件到父级。
我读到,在Winforms中,如果控件无法处理鼠标事件,它将将其冒泡到该控件的父控件,然后到该控件的父控件,依此类推,直到找到合适的处理程序。
考虑到这一点,我想最好的解决方案是使用`WndProc`覆盖子面板上的所有滚动相关事件,并将它们传递到父级,同时保留所有其他事件,但是我承认这不是我的强项,我感到很迷茫。
我尝试过其他一些解决方案,例如使子面板对所有鼠标事件都不可见,但如你所料,这很糟糕。我读到了实现消息过滤器的方法,但是没有理解它。
这是代码,可以为您提供面板及其子元素的基本示例:
这里是我尝试在自定义面板中重写WndProc的结果:
任何帮助或替代方法都非常欢迎。谢谢!
每个“瓷砖”都有一些事件处理程序与它们绑定,以处理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);
}
任何帮助或替代方法都非常欢迎。谢谢!