OnMouseEnter для всех элементов управления в форме - PullRequest
0 голосов
/ 17 марта 2009

У меня есть обработчики событий OnMouseEnter и OnMouseLeave для моей формы. Когда мышь перемещается по форме, я хочу установить непрозрачность на 100%, а когда она уходит, я хочу установить ее на 25% Это работает хорошо, за исключением случаев, когда мышь перемещается над одной из кнопок на форме. Событие OnMouseLeave запускается и снова скрывает форму. Есть ли хороший способ справиться с этим, не подключая OnMouseEnter для каждого элемента управления в форме?

Ответы [ 3 ]

2 голосов
/ 17 марта 2009

РЕДАКТИРОВАТЬ: Я собираюсь оставить этот ответ здесь, хотя он не может быть надежно работать. Причина: помешать кому-то другому попробовать то же самое. Смотрите конец сообщения по той причине, что оно не будет работать.

Вы можете сделать это довольно легко для клиентского прямоугольника, получив позицию курсора и проверив, находится ли он в клиентской области формы:

private void Form1_MouseLeave(object sender, EventArgs e)
{
    Point clientPos = PointToClient(Cursor.Position);
    if (!ClientRectangle.Contains(clientPos))
    {
        this.Opacity = 0.25;
    }
}

Предполагается, что ни один из ваших дочерних элементов управления не изменит непрозрачность.

Однако вы обнаружите, что это далеко не идеальное решение, потому что, когда мышь переходит к строке заголовка, форма переходит на 0,25%. Это можно исправить, проверив, находится ли позиция мыши внутри прямоугольника окна (используя свойство Bounds), но тогда ваше окно останется непрозрачным, если мышь переместится за пределы строки заголовка и из окна.

У вас похожая проблема при вводе строки заголовка снаружи.

Я думаю, вам придется обрабатывать сообщения WM_NCMOUSEENTER и WM_NCMOUSELEAVE, чтобы сделать эту работу надежной.

Почему это не может работать: Даже обработка уведомлений, не относящихся к области клиента, может дать сбой. Мышь может войти в дочерний элемент управления, что предотвратит уведомление формы.

2 голосов
/ 17 марта 2009

Я думаю, что это невозможно сделать без обработки событий MouseEnter и MouseLeave всех дочерних элементов, но вам не нужно связывать их вручную.

Вот код, который я скопировал и вставил из моего проекта. Это делает почти то, что вы описали здесь. Я фактически скопировал идею и структуру с этого сайта .

В конструкторе я вызываю AttachMouseOnChildren (), чтобы прикрепить события.

OnContainerEnter и OnContainerLeave используются для обработки мыши, входящей / выходящей из формы.

    #region MouseEnter & Leave

    private bool _childControlsAttached = false;

    /// <summary>
    /// Attach enter & leave events to child controls (recursive), this is needed for the ContainerEnter & 
    /// ContainerLeave methods.
    /// </summary>
    private void AttachMouseOnChildren() {
        if (_childControlsAttached) {
            return;
        }
        this.AttachMouseOnChildren(this.Controls);
        _childControlsAttached = true;
    }

    /// <summary>
    /// Attach the enter & leave events on a specific controls collection. The attachment
    /// is recursive.
    /// </summary>
    /// <param name="controls">The collection of child controls</param>
    private void AttachMouseOnChildren(System.Collections.IEnumerable controls) {
        foreach (Control item in controls) {
            item.MouseLeave += new EventHandler(item_MouseLeave);
            item.MouseEnter += new EventHandler(item_MouseEnter);
            this.AttachMouseOnChildren(item.Controls);
        }
    }

    /// <summary>
    /// Will be called by a MouseEnter event, with any of the controls within this
    /// </summary>
    void item_MouseEnter(object sender, EventArgs e) {
        this.OnMouseEnter(e);
    }

    /// <summary>
    /// Will be called by a MouseLeave event, with any of the controls within this
    /// </summary>
    void item_MouseLeave(object sender, EventArgs e) {
        this.OnMouseLeave(e);
    }

    /// <summary>
    /// Flag if the mouse is "entered" in this control, or any of its children
    /// </summary>
    private bool _containsMouse = false;

    /// <summary>
    /// Is called when the mouse entered the Form, or any of its children without entering
    /// the form itself first.
    /// </summary>
    protected void OnContainerEnter(EventArgs e) {
        // No longer transparent
        this.Opacity = 1;
    }

    /// <summary>
    /// Is called when the mouse leaves the form. When the mouse leaves the form via one of
    /// its children, this will also call OnContainerLeave
    /// </summary>
    /// <param name="e"></param>
    protected void OnContainerLeave(EventArgs e) {
        this.Opacity = DEFAULT_OPACITY;
    }

    /// <summary>
    /// <para>Is called when a MouseLeave occurs on this form, or any of its children</para>
    /// <para>Calculates if OnContainerLeave should be called</para>
    /// </summary>
    protected override void OnMouseLeave(EventArgs e) {
        Point clientMouse = PointToClient(Control.MousePosition);
        if (!ClientRectangle.Contains(clientMouse)) {
            this._containsMouse = false;
            OnContainerLeave(e);
        }
    }
    /// <summary>
    /// <para>Is called when a MouseEnter occurs on this form, or any of its children</para>
    /// <para>Calculates if OnContainerEnter should be called</para>
    /// </summary>
    protected override void OnMouseEnter(EventArgs e) {
        if (!this._containsMouse) {
            _containsMouse = true;
            OnContainerEnter(e);
        }
    }

    #endregion
1 голос
/ 17 марта 2009

Я думаю, что одним из способов надежной обработки интересующих вас событий мыши является установка IMessageFilter в вашем приложении объекте, из которого вы можете перехватывать все сообщения мыши ( WM_MOUSEMOVE и т. Д.), Даже если они отправляются дочерним элементам управления формы.

Вот некоторый демонстрационный код:

using System;
using System.Windows.Forms;

namespace Test
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        public static Form frm = null;
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            frm = new Form1 {Opacity = 0.25};
            frm.Controls.Add(new Button{Dock = DockStyle.Fill, Text = "Ok"});

            Application.AddMessageFilter(new MouseMoveFilter());
            Application.Run(frm);
        }
    }

    public class MouseMoveFilter : IMessageFilter
    {
        #region IMessageFilter Members
        private const int WM_MOUSELEAVE     = 0x02A3;
        private const int WM_NCMOUSEMOVE    = 0x0A0;
        private const int WM_MOUSEMOVE      = 0x0200;
        private const int WM_NCMOUSELEAVE   = 0x2A2;

        public bool PreFilterMessage(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_NCMOUSEMOVE:
                case WM_MOUSEMOVE:
                    Program.frm.Opacity = 1;
                    break;

                case WM_NCMOUSELEAVE:
                case WM_MOUSELEAVE:
                    if (!Program.frm.Bounds.Contains(Control.MousePosition))
                        Program.frm.Opacity = 0.25;
                    break;

            }
            return false;
        }

        #endregion
    }
}

В качестве альтернативы вы можете наследовать от класса Form и переопределить PreProcessMessage (), чтобы выполнить то же самое ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...