父控制鼠标使用子控件输入/离开事件

Pau*_*ams 21 .net c# mouse events winforms

我有一个C#.NET 2.0 WinForms应用程序.我的应用程序有一个控件,它是两个子控件的容器:标签和某种编辑控件.您可以这样想,外框是父控件:

+---------------------------------+ 
| [Label Control]  [Edit Control] |
+---------------------------------+

我试图在鼠标进入或离开父控件时执行某些操作,但我不在乎鼠标是否移动到其中一个子控件中.我想要一个标志来表示"鼠标在父或子的内部"和"鼠标移动到父控件边界之外".

我已尝试在父控件和两个子控件上处理MouseEnter和MouseLeave,但这意味着当鼠标在控件上移动时,操作会多次开始和结束.换句话说,我明白了:

Parent.OnMouseEnter      (start doing something)
Parent.OnMouseLeave      (stop)
Child.OnMouseEnter       (start doing something)
Child.OnMouseLeave       (stop)
Parent.OnMouseEnter      (start doing something)
Parent.OnMouseLeave      (stop)

中间的OnMouseLeave事件会导致一些不良影响,因为我正在做的事情开始然后停止.我想避免这种情况.

我不想捕获鼠标,因为父控件需要鼠标移动,因为子控件需要鼠标事件,我想要菜单和其他快捷键才能工作.

有没有办法在.NET框架内执行此操作?或者我需要使用Windows鼠标挂钩?

小智 9

我觉得我找到了比当前最受欢迎的解决方案更好的解决方案.

其他提出的解决方案的问题是它们要么相当复杂(直接处理较低级别的消息).

或者他们失败的角落情况:如果鼠标直接从子控件内部移动到容器外部,依赖MouseLeave上的鼠标位置会导致您错过鼠标退出.

虽然这个解决方案并不完全优雅,但它很简单并且有效:

添加一个透明控件,占用要接收MouseEnter和MouseLeave事件的容器的整个空间.

我在Amed的答案中找到了一个很好的透明控制:使控件透明

然后我将其剥离到这个:

public class TranspCtrl : Control
{
    public TranspCtrl()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        SetStyle(ControlStyles.Opaque, true);
        this.BackColor = Color.Transparent;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | 0x20;
            return cp;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

public class ChangeBackgroundOnMouseEnterAndLeave
{
    public Panel Container;
    public Label FirstLabel;
    public Label SecondLabel;

    public ChangeBackgroundOnMouseEnterAndLeave()
    {
        Container = new Panel();
        Container.Size = new Size(200, 60);

        FirstLabel = new Label();
        FirstLabel.Text = "First Label";
        FirstLabel.Top = 5;

        SecondLabel = new Label();
        SecondLabel.Text = "Second Lable";
        SecondLabel.Top = 30;

        FirstLabel.Parent = Container;
        SecondLabel.Parent = Container;

        Container.BackColor = Color.Teal;

        var transparentControl = new TranspCtrl();
        transparentControl.Size = Container.Size;

        transparentControl.MouseEnter += MouseEntered;
        transparentControl.MouseLeave += MouseLeft;

        transparentControl.Parent = Container;
        transparentControl.BringToFront();
    }

    void MouseLeft(object sender, EventArgs e)
    {
        Container.BackColor = Color.Teal;
    }

    void MouseEntered(object sender, EventArgs e)
    {
        Container.BackColor = Color.Pink;
    }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var test = new ChangeBackgroundOnMouseEnterAndLeave();
        test.Container.Top = 20;
        test.Container.Left = 20;
        test.Container.Parent = this;
    }
}
Run Code Online (Sandbox Code Playgroud)

享受适当的MouseLeave和MouseEnter活动!

  • 当您的子控件需要单击或接收焦点时,我认为这不是一个好主意。 (3认同)
  • 此解决方案防止透明控件下的那些控件接收任何鼠标事件 (2认同)

Pau*_*ams 8

经过更多的研究,我发现了Application.AddMessageFilter方法.使用这个,我创建了一个.NET版本的鼠标钩子:

class MouseMessageFilter : IMessageFilter, IDisposable
{
    public MouseMessageFilter()
    {
    }

    public void Dispose()
    {
        StopFiltering();
    }

    #region IMessageFilter Members

    public bool PreFilterMessage(ref Message m)
    {
         // Call the appropriate event
         return false;
    }

    #endregion

    #region Events

    public class CancelMouseEventArgs : MouseEventArgs
    {...}

    public delegate void CancelMouseEventHandler(object source, CancelMouseEventArgs e);
    public event CancelMouseEventHandler MouseMove;
    public event CancelMouseEventHandler MouseDown;
    public event CancelMouseEventHandler MouseUp;

    public void StartFiltering()
    {
        StopFiltering();
        Application.AddMessageFilter(this);
    }

    public void StopFiltering()
    {
        Application.RemoveMessageFilter(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我可以在容器控件中处理MouseMove事件,检查鼠标是否在我的父控件内,然后开始工作.(我还必须跟踪最后一个moused over parent控件,以便我可以停止之前启动的父级.)

----编辑----

在我的表单类中,我创建并连接过滤器:

public class MyForm : Form
{
   MouseMessageFilter msgFilter;

   public MyForm()
   {...
       msgFilter = new MouseMessageFilter();
       msgFilter.MouseDown += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseDown);
       msgFilter.MouseMove += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseMove);
    }

    private void msgFilter_MouseMove(object source, MouseMessageFilter.CancelMouseEventArgs e)
    {
        if (CheckSomething(e.Control)
            e.Cancel = true;
    }   
}
Run Code Online (Sandbox Code Playgroud)


Fre*_*örk 7

您可以像这样确定鼠标是否在控件的范围内(假设此代码位于容器控件中;如果不在,则替换this为对容器控件的引用):

private void MyControl_MouseLeave(object sender, EventArgs e)
{
    if (this.ClientRectangle.Contains(this.PointToClient(Cursor.Position)))
    {
        // the mouse is inside the control bounds
    }
    else
    {
        // the mouse is outside the control bounds
    }
}
Run Code Online (Sandbox Code Playgroud)