Pregunta

Estoy tratando de manejar un evento de mouseclick en una forma particular que debe disparar si el cursor del mouse cae entre un conjunto de coordenadas, digamos un cuadrado.

Entiendo que si tuviera una forma vacía, simplemente podría vincularme con el evento MouseMove y fuera. Pero en realidad, puede haber hasta 10 controles superpuestos diferentes y en mi aplicación de prueba el evento MouseMove solo se dispara si el cursor está en la forma real y no si está sobre un control de un niño.

¿Alguien sabe cómo manejar este evento cuando hay un número desconocido de controles para niños en el momento de diseño?

¿Hay una línea fácil que pueda usar?

¿Fue útil?

Solución

En mi humilde opinión, hay un poco de situación binaria aquí: y no hay "una sola línea". La única solución que puedo ver es obtener sus controles que no implementen eventos en un contenedor .NET que lo haga.

Cuando cualquier control recibe un clic, el comportamiento esperado normal es que se convertirá en el control activo de la forma (a la que siempre se puede acceder a este.ActivCecontrol).

Pero, particularmente si el control que hizo clic en captura el mouse, algo tiene que plantear un evento ya que .NET no implementa el evento "burbujeante" (como lo hace WPF).

La forma habitual de lidiar con la extensión del comportamiento de cualquier objeto que esté sellado o lo que sea para escribir un método de extensión, y he encontrado que escribir extensiones para el control es bastante fácil, pero no sé si eso lo ayudará en este caso. Desafortunadamente, estoy fuera de mi país de origen en este momento, y no tengo un estudio visual para jugar.

Una estrategia que puede usar para determinar si un punto dado en un formulario cae dentro de los límites de cualquier control es enumerar las áreas (límites) de todos los controles en la forma a través de 'para todos los formularios control.Collection (this.controls). Pero, si tiene controles superpuestos, tiene el problema de más de un control que posiblemente contiene un punto dado.

Lo mejor, Bill

Otros consejos

prueba esto:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        AddMouseMoveHandler(this);
    }

    private void AddMouseMoveHandler(Control c)
    {
        c.MouseMove += MouseMoveHandler;
        if(c.Controls.Count>0)
        {
            foreach (Control ct in c.Controls)
                AddMouseMoveHandler(ct);
        }
    }

    private void MouseMoveHandler(object sender, MouseEventArgs e)
    {
        lblXY.Text = string.Format("X: {0}, Y:{1}", e.X, e.Y);
    }
}

Sé que esta publicación es bastante antigua, pero me parece que el método más simple sería para implementar el formulario IMessageFilter. En el constructor (o en OnHandleCreated) llama

Application.AddMessageFilter(this);

y luego puede atrapar los mensajes de todas las ventanas en su implementación de IMessageFilter.PreFilterMessage.

Es probable que necesite usar p/invoke para el método Win32 Ischild

[DllImport("user32.dll")]
public static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);

junto con la forma Handle Propiedad para garantizar que esté manejando los mensajes correctos.

¿Por qué no usas los controladores de eventos Mouseover de los controles?

Sé que llego un poco tarde al golpe, pero estaba teniendo problemas con esto hoy cuando usé un panel como barra de título. Tenía una etiqueta para mostrar algo de texto, una caja de imágenes y algunos botones anidados dentro del panel, pero necesitaba atrapar el evento MouseMove independientemente.

Lo que decidí hacer era implementar un controlador de métodos recursivo para hacer esto, ya que solo tenía 1 nivel de controles anidados, esto puede no escalar demasiado bien cuando comienzas a abordar niveles ridículos de anidación.

Así es como lo hice:

    protected virtual void NestedControl_Mousemove(object sender, MouseEventArgs e)
    {
        Control current = sender as Control;
        //you will need to edit this to identify the true parent of your top-level control. As I was writing a custom UserControl, "this" was my title-bar's parent.
        if (current.Parent != this) 
        {
            // Reconstruct the args to get a correct X/Y value.
            // you can ignore this if you never need to get e.X/e.Y accurately.
            MouseEventArgs newArgs = new MouseEventArgs
            (
                e.Button, 
                e.Clicks, 
                e.X + current.Location.X, 
                e.Y + current.Location.Y, 
                e.Delta
            );
            NestedControl_Mousemove(current.Parent, newArgs);
        }
        else
        {
            // My "true" MouseMove handler, called at last.
            TitlebarMouseMove(current, e);
        }
    }

    //helper method to basically just ensure all the child controls subscribe to the NestedControl_MouseMove event.
    protected virtual void AddNestedMouseHandler(Control root, MouseEventHandler nestedHandler)
    {
        root.MouseMove += new MouseEventHandler(nestedHandler);
        if (root.Controls.Count > 0)
            foreach (Control c in root.Controls)
                AddNestedMouseHandler(c, nestedHandler);
    }

Y luego configurarlo es relativamente simple:

Defina su controlador "verdadero":

    protected virtual void TitlebarMouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            this.Text = string.Format("({0}, {1})", e.X, e.Y);
        }
    }

Y luego configure los suscriptores del evento de controles:

//pnlDisplay is my title bar panel.
AddNestedMouseHandler(pnlDisplay, NestedControl_Mousemove);

Relativamente simple de usar, y puedo garantizar el hecho de que funciona :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top