我有一个Panel
AutoScroll
为真。此面板包含许多较小的面板,可填充所有可用空间,如瓷砖。当要显示的子面板太多时,我会按预期获得垂直滚动条
这些“tile”中的每一个都有一些与它们相关联的事件处理程序,以处理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);
}
欢迎任何帮助或替代方法。谢谢!
答案 0 :(得分:1)
所有归功于Hans Passant(再次),取自他建议的帖子:https://stackoverflow.com/a/3562449/17034
允许包含面板进行聚焦工作正常。对于上面的演示代码,类不需要更改,只需将其用于包含的面板。我必须对我的项目进行一些调整,以便在必要时调用焦点,但它远非火箭科学。
再次感谢。
答案 1 :(得分:1)
我无法使用任何这些解决方案。我和你一样压倒WndProc fxn。终于找到了解决方案!我注意到如果容纳我的TextBox的容器处于焦点,那么滚动工作,而不是当TextBox处于焦点时。
我不需要改变事件的透明度,我需要将事件发送到另一个控制句柄! (现在看起来很简单,但我已经试图解决这个问题了好几天!)
internal class myTextBox : TextBox
{
const int WM_MOUSEWHEEL = 0x020A;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
m.HWnd = this.Parent.Handle; // Change the Handle of the message
base.WndProc(ref m);
}
}
我在所有Google搜索中都没有看到过这种方法,如果有什么理由不这样做,我希望有人会回复。
答案 2 :(得分:1)
@ a-clymer版本的改进版本,效果更好。
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
// find the first scrollable parent control
Control p = this;
do
{
p = p.Parent;
} while (p != null && !(p is ScrollableControl));
// rewrite the destination handle of the message
if (p != null)
m.HWnd = p.Handle;
}
答案 3 :(得分:0)
在我这边,@ 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);
}
}