Как захватить события mousemove под дочерними элементами управления

Я пытаюсь обработать событие щелчка мышью в определенной форме, которая должна срабатывать, если курсор мыши попадает между набором координат - скажем, квадратом.

Я понимаю, что если бы у меня была пустая форма, я мог бы просто подключиться к событию mousemove и уйти. Но на самом деле может быть до 10 различных перекрывающихся элементов управления, и в моем тестовом приложении событие mousemove срабатывает только в том случае, если курсор находится на самой фактической форме, а не над дочерним элементом управления.

Кто-нибудь знает, как обрабатывать это событие, когда во время разработки имеется неизвестное количество дочерних элементов управления?

Есть ли простой однострочник, который я могу использовать?


person Grant    schedule 12.11.2009    source источник


Ответы (5)


imho здесь немного бинарная ситуация: и нет «однострочника». единственное решение, которое я вижу, - это поместить ваши элементы управления, которые не реализуют события, в контейнер .NET, который это делает.

Когда какой-либо элемент управления получает щелчок, обычное ожидаемое поведение заключается в том, что он становится активным элементом управления формы (к которому всегда может получить доступ this.ActivceControl).

Но, в частности, если элемент управления, по которому вы щелкнули, захватывает мышь, что-то должно вызвать событие, поскольку .NET не реализует «пузырьковое» событие (как это делает WPF).

Обычный способ справиться с расширением поведения любого объекта, который запечатан или что-то еще, состоит в том, чтобы написать метод расширения, и я нашел написание расширений для Control довольно простым, но я не знаю, поможет ли это вам в этом случае. К сожалению, сейчас я нахожусь за пределами своей страны, и у меня нет Visual Studio, с которой можно поиграться.

Одна из стратегий, которую вы можете использовать для определения того, попадает ли данная точка в форме в пределы границ любого элемента управления, заключается в перечислении областей (границ) всех элементов управления в форме через 'forall of the Forms Control.Collection (this.Controls). Но если у вас есть перекрывающиеся элементы управления, у вас возникает проблема с более чем одним элементом управления, который может содержать данную точку.

лучший, Билл

person BillW    schedule 12.11.2009
comment
TheVillageIdiot первым рассказал о Global Hooks, и вы, возможно, захотите принять его ответ, а не мой. Я также собирался предложить вам ознакомиться с Global Mouse Hooks, такими как: классическая статья Джорджа Мамаладзе: codeproject .com/KB/cs/globalhook.aspx Я могу убедиться, что его код GlobalHook будет компилироваться и нормально работать в бета-версии 2 VS 2010, скомпилированной для FrameworkWork ‹= 3.5 (не работает с FrameWork 4.0). Вы также найдете другие статьи о GlobalHooks на CodeProject. Мне любопытно, какой элемент управления вы используете, который не раскрывает событие mousedown или click. Лучший, - person BillW; 13.11.2009

попробуй это:

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);
    }
}
person TheVillageIdiot    schedule 12.11.2009
comment
ОП говорит, что элементы управления, которые он использует, не реализуют MouseMove, если только я его не понимаю. - person Robert Harvey; 12.11.2009
comment
Да, я прочитал это только после отправки ответа. Это оставляет ему возможность установить глобальные хуки мыши. - person TheVillageIdiot; 12.11.2009
comment
Рад, что вы не удалили свой пост, хотя ОП хотел чего-то другого. Помогли мне в моей ситуации. - person Chuck Savage; 31.08.2011
comment
@TheVillageIdiot Спасибо, очень помогло. Можно изменить событие, чтобы получить относительное положение, например Point pt = e.Location; Control c = отправитель как Control; if (sender != this) { // Цикл от этого дочернего элемента к (этому) do { // Настройка точки для каждого родительского контейнера pt.Offset(c.Left, c.Top); c = c.Родительский; } пока (с != это); } MessageBox.Show(string.Format(X: {0}, Y:{1}, pt.X, pt.Y)); - person clamchoda; 31.07.2018

Я знаю, что этот пост довольно старый, но мне кажется, что самым простым способом было бы реализовать форму IMessageFilter. В конструкторе (или в OnHandleCreated) вы вызываете

Application.AddMessageFilter(this);

и тогда вы сможете перехватывать сообщения всех окон в вашей реализации IMessageFilter.PreFilterMessage.

Вам, вероятно, потребуется использовать P/Invoke для метода WIN32 IsChild.

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

вместе со свойством Handle формы, чтобы убедиться, что вы обрабатываете правильные сообщения.

person stritch000    schedule 21.08.2012

Почему бы вам просто не использовать обработчики событий при наведении курсора мыши?

person Robert Harvey    schedule 12.11.2009
comment
Элементы управления, с которыми я работаю, не отображают нужные мне события. - person Grant; 12.11.2009

Я знаю, что немного опоздал с ударом, но сегодня у меня были проблемы с этим при использовании панели в качестве строки заголовка. У меня была метка для отображения текста, графического окна и нескольких кнопок, вложенных в панель, но мне все равно нужно было перехватывать событие MouseMove.

Что я решил сделать, так это реализовать рекурсивный обработчик методов для этого, поскольку у меня был только 1 уровень вложенных элементов управления, это может не слишком хорошо масштабироваться, когда вы начинаете приближаться к нелепым уровням вложенности.

Вот как я это сделал:

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

А затем настроить его относительно просто:

Определите свой «истинный» обработчик:

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

А затем настройте подписчиков событий управления:

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

Относительно прост в использовании, и я могу поручиться за то, что это работает :)

person Jason Larke    schedule 03.07.2012