c# - 如何在不窃取焦点且不使用 P/Invoke 的情况下将消息(例如鼠标滚轮)转发到另一个控件?

标签 c# focus mousewheel message-pump imessagefilter

我想在我用鼠标悬停在这个控件上时转发一条消息(例如 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

编辑:我回答中的完整解决方案

最佳答案

找到一个方法:你必须继承NativeWindow,将所选控件的句柄分配给它,并在你以任何你喜欢的方式拦截消息后调用 protected 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
}

关于c# - 如何在不窃取焦点且不使用 P/Invoke 的情况下将消息(例如鼠标滚轮)转发到另一个控件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6036918/

相关文章:

c# - 如何压缩两个不同大小的列表以创建一个与原始列表中最长的列表相同的新列表?

c# - 使用一个文本框和多个参数将 SQL 数据库过滤到 DataGridView

winforms - PictureBox的MouseWheel事件?

scrollbar - jScrollPane - 鼠标滚轮不工作

c# - 为什么要复制对象?

c# - 如何使用具有可变持续时间值的 [OutputCache (Duration=2000)] 并重置服务器缓存

android - SlidingDrawer 中的 ListView 在 onResume 后失去焦点

reactjs - React JS - 元素专注于输入不起作用

java - 使焦点不转到第一个组件

winforms - 如何使NumericUpDown只读