Как пересылать сообщения (например, колесо мыши) на другой элемент управления без кражи фокуса и без P / Invoke? - PullRequest
6 голосов
/ 18 мая 2011

Я хочу переслать сообщение (например, 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

}

РЕДАКТИРОВАТЬ : полное решение в моем ответе

Ответы [ 3 ]

7 голосов
/ 23 мая 2011

Найден метод: вам нужно унаследовать 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
}
2 голосов
/ 25 января 2013

Я нашел гораздо более простое решение, которое может быть применено, только если сообщение, которое вы пытаетесь переслать, имеет соответствующее событие.Например, для события 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 });
}
0 голосов
/ 18 мая 2011

Это действительно зависит от вида событий и их количества.Как насчет простой передачи событий, таких как движение мыши, вашему родительскому элементу управления (например, чтобы сделать элемент управления «прозрачным»)?

Один из обработчиков событий в вашем элементе управления может выглядеть следующим образом (код из моей головы безтестирование):

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);
}
...