如何在鼠标悬停事件上显示/隐藏控件

本文关键字:显示 隐藏 控件 事件 鼠标 悬停 | 更新日期: 2023-09-27 18:05:09

我最近一直在开发一个应用程序,发现自己被一个简单但令人讨厌的问题卡住了。

我想使一个特定的控件可见/不可见,当我进入它的父,并能够执行事件(例如:点击)在这个控件。问题是,当我进入我想要显示的控件时,鼠标悬停甚至不能在父控件上工作。这导致我想要显示的控件闪烁(鼠标悬停工作->控件显示->鼠标悬停不再工作->控件隐藏->鼠标悬停工作->等)。

我已经找到了这个"解决方案"来帮助我有一些"稳定"的东西。

    // Timer to make the control appearing properly.
    private void Timer_Elapsed(object o, ElapsedEventArgs e)
    {
        try
        {
            ItemToHideDisplay.Visible = true;
            var mousePoint = this.PointToClient(Cursor.Position);
            if (mousePoint.X > this.Width ||
                mousePoint.X < 0 ||
                mousePoint.Y > this.Height ||
                mousePoint.Y < 0)
            {
                HideDisplayTimer.Stop();
                ItemToHideDisplay.Visible = false;
                base.OnMouseLeave(e);
            }
        }
        catch
        {
            // We don't want the application to crash...
        }
    }
    protected override void OnMouseEnter(EventArgs e)
    {
        HideDisplayTimer.Start();
        base.OnMouseEnter(e);
    }

基本上,当我进入对象时,计时器启动并每50毫秒检查一次鼠标是否在父对象中。如果是,则显示该控件。如果没有,定时器停止,控制隐藏。

这工作。耶。但是我觉得这个解决方案很难看。

所以我的问题是:有没有另一种方法,另一种解决方案比这个更漂亮?

如果我说得不够清楚,请告诉我:)

提前感谢!

编辑:嘿,我想我自己找到了!

的技巧是重写父控件的OnMouseLeave:

    protected override void OnMouseLeave(EventArgs e)
    {
        var mousePoint = this.PointToClient(Cursor.Position);
        if (mousePoint.X > this.Width ||
mousePoint.X < 0 ||
mousePoint.Y > this.Height ||
mousePoint.Y < 0)
        {
            base.OnMouseLeave(e);
        }
    }

这样,当进入我所显示的控件(进入父控件)时,鼠标离开事件不会被触发!它的工作原理!

谢谢你的回答。我猜你可以继续发表你的想法,因为我在网上没有看到很多解决方案:)

如何在鼠标悬停事件上显示/隐藏控件

可以设置控件对鼠标事件"透明"。所以鼠标事件会通过它。

你必须编写你自己的类来继承你想要的控件。例如,如果Label是您的特定控件,那么创建一个继承Label的新类—您就明白了。: -)

在你的新类中,你可以使用窗口消息使你的控件忽略鼠标事件:

protected override void WndProc(ref Message m)
{
    const int WM_NCHITTEST = 0x0084;
    const int HTTRANSPARENT = -1;
    switch(m.Msg)
    {
        case WM_NCHITTEST:
            m.Result = (IntPtr)HTTRANSPARENT;
            break;
        default:
            base.WndProc(ref m);
    }
}

你可以在MSDN上阅读更多关于WndProc的信息

您可以为表单注册一个消息过滤器,并对表单的鼠标移动事件进行预处理。多亏了这一点,你不必重写你的子控件等。

消息过滤器,一旦在父窗体中注册,也将对子窗体起作用,所以即使您的窗体的一部分被子窗体覆盖,您的目标控件仍应根据鼠标位置显示/消失。

在下面的例子中,表单上有一个面板,面板里面有一个按钮。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    internal void CaptureMouseMove(Point location)
    {
        if (panel1.RectangleToScreen(panel1.ClientRectangle).Contains(location))
        {
            button1.Visible = true;
            Console.WriteLine(location + "in " + panel1.RectangleToScreen(panel1.ClientRectangle));
        }
        else
        {
            button1.Visible = false;
            Console.WriteLine(location + "out " + panel1.RectangleToScreen(panel1.ClientRectangle));
        }
    }
    internal bool Form1_ProcessMouseMove(Message m)
    {
        Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
        Control ctr = Control.FromHandle(m.HWnd);
        if (ctr != null)
        {
            pos = ctr.PointToScreen(pos);
        }
        else
        {
            pos = this.PointToScreen(pos);
        }
        this.CaptureMouseMove(pos);
        return false;
    }
    private MouseMoveMessageFilter mouseMessageFilter;
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        // add filter here
        this.mouseMessageFilter = new MouseMoveMessageFilter();
        this.mouseMessageFilter.TargetForm = this;
        this.mouseMessageFilter.ProcessMouseMove = this.Form1_ProcessMouseMove;
        Application.AddMessageFilter(this.mouseMessageFilter);
    }
    protected override void OnClosed(EventArgs e)
    {
        // remove filter here
        Application.RemoveMessageFilter(this.mouseMessageFilter);
        base.OnClosed(e);
    }
    private class MouseMoveMessageFilter : IMessageFilter
    {
        public Form TargetForm { get; set; }
        public Func<Message, bool> ProcessMouseMove;
        public bool PreFilterMessage(ref Message m)
        {
            if (TargetForm.IsDisposed) return false;
            //WM_MOUSEMOVE
            if (m.Msg == 0x0200)
            {
                if (ProcessMouseMove != null)
                   return ProcessMouseMove(m);
            }
            return false;
        }
    }
}

我要在这里做一个技巧:)我将控件包装到一个新的控件中:)看看这个。

XAML:

<UserControl MouseEnter="Border_MouseEnter" MouseLeave="UserControl_MouseLeave" Margin="100" Background="Transparent">
    <UserControl x:Name="ControlToHide" Background="Red">
        <Button Content="hi"  Width="100" Height="100"/>
    </UserControl>
</UserControl>

背后的代码:

private void Border_MouseEnter(object sender, MouseEventArgs e)
{
    this.ControlToHide.Visibility = System.Windows.Visibility.Hidden;
}
private void UserControl_MouseLeave(object sender, MouseEventArgs e)
{
    this.ControlToHide.Visibility = System.Windows.Visibility.Visible;        
}

它轻,容易和工作。喜欢