Pergunta

Estou tentando lidar com um evento de mouseclick em uma forma específica que deve disparar se o cursor do mouse cair entre um conjunto de coordenadas - digamos um quadrado.

Entendo que, se eu tivesse uma forma vazia, eu poderia simplesmente amarrar ao evento Mousemove e eu vou. Mas, na realidade, pode haver até 10 controles sobrepostos diferentes e, no meu aplicativo de teste, o evento Mousemove apenas dispara se o cursor estiver na própria forma e não se for sobre um controle infantil.

Alguém sabe como lidar com este evento quando há um número desconhecido de controles infantis no tempo de design?

Existe uma frase fácil que eu possa usar?

Foi útil?

Solução

IMHO Há um pouco de situação binária aqui: e não há "uma linha". A única solução que posso ver é colocar seus controles que não implementam eventos em um contêiner .NET que faz.

Quando qualquer controle obtém um clique, o comportamento esperado normal é que ele se tornará o controle ativo do formulário (que sempre pode ser acessado por isso.ActivceControl).

Mas, particularmente, se o controle que você clicou captura o mouse, algo precisa aumentar um evento, pois .NET não implementa o evento "Bubbling" (como o WPF faz).

A maneira usual de lidar com a extensão do comportamento de qualquer objeto que seja selado ou o que quer que seja para escrever um método de extensão, e achei muito fácil escrever extensões de controle, mas não sei se isso o ajudará neste caso. Infelizmente, estou fora do meu país de origem agora e não tenho o Visual Studio para brincar.

Uma estratégia que você pode usar para determinar se um determinado ponto em um formulário se enquadra dentro dos limites de qualquer controle é enumerar as áreas (limites) de todos os controles no formulário por meio de 'forl of the Forms Control.collection (this.Controls). Mas, se você tiver controles sobrepostos, terá a questão de mais de um controle possivelmente contendo um determinado ponto.

Melhor, Bill

Outras dicas

Experimente isso:

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

Eu sei que este post é bastante antigo, mas parece -me que o método mais simples seria para o formulário implementar IMessageFilter. No construtor (ou em OnHandleCreated) você chama

Application.AddMessageFilter(this);

E então você pode pegar as mensagens de todas as janelas na sua implementação de IMessageFilter.PreFilterMessage.

Você provavelmente precisaria usar P/Invoke para o método Win32 Ischild

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

junto com o formulário Handle propriedade para garantir que você esteja lidando com as mensagens certas.

Por que você não usa os manipuladores de eventos do MouseOver dos controles?

Sei que estou um pouco atrasado para o soco, mas estava tendo problemas com isso hoje cedo, usando um painel como uma barra de título. Eu tinha um rótulo para exibir algum texto, uma caixa de imagem e alguns botões todos aninhados dentro do painel, mas eu precisava prender o evento Mousemove, independentemente.

O que eu decidi fazer foi implementar um manipulador de métodos recursivo para fazer isso, pois eu tinha apenas 1 nível de controles aninhados, isso pode não escalar muito bem quando você começa a se aproximar de níveis ridículos de ninho.

Veja como eu fiz:

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

E então configurá -lo é relativamente simples:

Defina seu manipulador "verdadeiro":

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

E, em seguida, configure os assinantes do evento Controls:

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

Relativamente simples de usar, e posso garantir o fato de que funciona :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top