Comment transmettre les messages (par exemple, roue de souris) à un autre contrôle sans voler la mise au point et sans p / invoquer?

StackOverflow https://stackoverflow.com/questions/6036918

Question

Je veux transmettre un message (comme WM_Mousewheel) lorsque je suis sur ce contrôle avec la souris, sans voler la focalisation. Ce problème peut être facilement résolu en interceptant le message avec un iMessageFilter (à ajouter à la pompe de message d'application) et en le transmettant avec le p / invoke (d) sendMessage (). La question est: puis-je faire de même sans utiliser p / invoke (solutions que j'ai trouvées dans StackOverflow Utiliser P / Invoke)? Sinon, pourquoi?

Le code ci-dessous est ma solution avec P / Invoke. Je l'utilise avec juste 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

}

ÉDITER: Solution complète dans ma réponse

Était-ce utile?

La solution

Trouvé une méthode: vous devez hériter NativeWindow, attribuez la poignée du contrôle choisi à lui un appel le wndproc protégé après avoir intercepté un message de toutes les manières que vous préférez (dans mon cas, la classe héritée est même un iMessageFilter afin que je puisse facilement le brancher à l'application). Je l'utilise avec new MessageForwarder(anycontrol, 0x20A) pour rediriger la roue de la souris.

Il est donc possible d'intercepter et de transmettre les messages à n'importe quel contrôle sans p / invoquer. C'était bien caché.

/// <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
}

Autres conseils

J'ai trouvé une solution beaucoup plus simple qui ne peut être appliquée que si le message que vous essayez de transmettre a un événement correspondant. Par exemple, pour l'événement de la roue de souris:

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

Cela dépend vraiment du type d'événements et de leur numéro. Que diriez-vous de simplement passer des événements tels que le mouvement de la souris vers le contrôle de votre parent (par exemple, pour que le contrôle se comporte "transparent")?

L'un des gestionnaires d'événements sous votre contrôle pourrait ressembler à ceci (code hors de ma tête sans tester):

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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top