当我用鼠标控制此控件时,我想转发一条消息(例如WM_MOUSEWHEEL),而不会窃取焦点。使用IMessageFilter(要添加到应用程序消息泵)并使用P / Invoke(d)SendMessage()转发消息,可以轻松解决此问题。问题是:我可以不使用P / Invoke做同样的事情(我在StackOverflow中找到的解决方案使用P / Invoke)吗?如果没有,为什么?
以下代码是我的P / Invoke解决方案。我只使用new MessageForwarder(control, 0x20A)
。
/// <summary>
/// This class implements a filter for the Windows.Forms message pump allowing a
/// specific message to be forwarded to the Control specified in the constructor.
/// Adding and removing of the filter is done automatically.
/// </summary>
public class MessageForwarder : IMessageFilter
{
#region Fields
private Control _Control;
private Control _PreviousParent;
private HashSet<int> _Messages;
private bool _IsMouseOverControl;
#endregion // Fields
#region Constructors
public MessageForwarder(Control control, int message)
: this(control, new int[] { message }) { }
public MessageForwarder(Control control, IEnumerable<int> messages)
{
_Control = control;
_Messages = new HashSet<int>(messages);
_PreviousParent = control.Parent;
_IsMouseOverControl = false;
control.ParentChanged += new EventHandler(control_ParentChanged);
control.MouseEnter += new EventHandler(control_MouseEnter);
control.MouseLeave += new EventHandler(control_MouseLeave);
control.Leave += new EventHandler(control_Leave);
if (control.Parent != null)
Application.AddMessageFilter(this);
}
#endregion // Constructors
#region IMessageFilter members
public bool PreFilterMessage(ref Message m)
{
if (_Messages.Contains(m.Msg) && _Control.CanFocus && !_Control.Focused
&& _IsMouseOverControl)
{
SendMessage(_Control.Handle, m.Msg, m.WParam, m.LParam);
return true;
}
return false;
}
#endregion // IMessageFilter
#region Event handlers
void control_ParentChanged(object sender, EventArgs e)
{
if (_Control.Parent == null)
Application.RemoveMessageFilter(this);
else
{
if (_PreviousParent == null)
Application.AddMessageFilter(this);
}
_PreviousParent = _Control.Parent;
}
void control_MouseEnter(object sender, EventArgs e)
{
_IsMouseOverControl = true;
}
void control_MouseLeave(object sender, EventArgs e)
{
_IsMouseOverControl = false;
}
void control_Leave(object sender, EventArgs e)
{
_IsMouseOverControl = false;
}
#endregion // Event handlers
#region Support
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
#endregion // Support
}
编辑:我的回答中的完整解决方案
答案 0 :(得分:7)
找到一个方法:你必须继承NativeWindow
,在你以任何你喜欢的方式拦截一条消息之后,将所选控件的句柄分配给它,调用受保护的WndProc(在我的例子中,是继承的类)甚至是一个IMessageFilter,所以我可以轻松地将其插入应用程序)。我使用new MessageForwarder(anycontrol, 0x20A)
来重定向鼠标滚轮。
因此,可以在没有p / invoke的情况下拦截和转发消息到任何控件。它虽然隐藏得很好。
/// <summary>
/// This class implements a filter for the Windows.Forms message pump allowing a
/// specific message to be forwarded to the Control specified in the constructor.
/// Adding and removing of the filter is done automatically.
/// </summary>
public class MessageForwarder : NativeWindow, IMessageFilter
{
#region Fields
private Control _Control;
private Control _PreviousParent;
private HashSet<int> _Messages;
private bool _IsMouseOverControl;
#endregion // Fields
#region Constructors
public MessageForwarder(Control control, int message)
: this(control, new int[] { message }) { }
public MessageForwarder(Control control, IEnumerable<int> messages)
{
_Control = control;
AssignHandle(control.Handle);
_Messages = new HashSet<int>(messages);
_PreviousParent = control.Parent;
_IsMouseOverControl = false;
control.ParentChanged += new EventHandler(control_ParentChanged);
control.MouseEnter += new EventHandler(control_MouseEnter);
control.MouseLeave += new EventHandler(control_MouseLeave);
control.Leave += new EventHandler(control_Leave);
if (control.Parent != null)
Application.AddMessageFilter(this);
}
#endregion // Constructors
#region IMessageFilter members
public bool PreFilterMessage(ref Message m)
{
if (_Messages.Contains(m.Msg) && _Control.CanFocus && !_Control.Focused
&& _IsMouseOverControl)
{
m.HWnd = _Control.Handle;
WndProc(ref m);
return true;
}
return false;
}
#endregion // IMessageFilter
#region Event handlers
void control_ParentChanged(object sender, EventArgs e)
{
if (_Control.Parent == null)
Application.RemoveMessageFilter(this);
else
{
if (_PreviousParent == null)
Application.AddMessageFilter(this);
}
_PreviousParent = _Control.Parent;
}
void control_MouseEnter(object sender, EventArgs e)
{
_IsMouseOverControl = true;
}
void control_MouseLeave(object sender, EventArgs e)
{
_IsMouseOverControl = false;
}
void control_Leave(object sender, EventArgs e)
{
_IsMouseOverControl = false;
}
#endregion // Event handlers
}
答案 1 :(得分:2)
我找到了一个更简单的解决方案,只有当您尝试转发的消息有相应的事件时才能应用。例如,对于mousewheel事件:
// Redirect the mouse wheel event from panel1 to panel2.
// When the panel1 is focused and the mouse wheel is used the panel2 will scroll.
private void panel1_MouseWheel(object sender, MouseEventArgs e)
{
// Get the MouseWheel event handler on panel2
System.Reflection.MethodInfo onMouseWheel =
panel2.GetType().GetMethod("OnMouseWheel",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
// Call the panel2 mousehwweel event with the same parameters
onMouseWheel.Invoke(panel2, new object[] { e });
}
答案 2 :(得分:0)
这实际上取决于事件的类型和数量。如何将鼠标移动等事件传递给父控件(例如,使控件表现为“透明”)?
您控件中的一个事件处理程序可能看起来像这样(代码在我的脑海中没有经过测试):
private void MyControl_MouseMove(object sender, MouseEventArgs e)
{
if(Parent == null)
return;
// add this control's offsets first so the coordinates fit to the parent control
e.X += this.Top;
e.Y += this.Left;
if(parent.MouseMove != null)
parent.MouseMove(sender, e);
}