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

C# 如何在鼠标悬停事件上显示/隐藏控件,c#,winforms,C#,Winforms,我最近一直在开发一个应用程序,发现自己陷入了一个简单但恼人的问题 我想在输入某个特定控件的父控件时使其可见/不可见,并能够在此控件上执行事件(例如:单击)。问题是,当我输入想要显示的控件时,鼠标悬停甚至对父控件不起作用。这导致我想要显示的控件闪烁(鼠标悬停工作->控件显示->鼠标悬停不再工作->控件隐藏->鼠标悬停工作->等等) 我已经找到了这个“解决方案”来帮助我拥有一些“稳定”的东西 //使控件正确显示的计时器。 私有无效计时器(对象o、ElapsedEventArgs e) { 尝试 {

我最近一直在开发一个应用程序,发现自己陷入了一个简单但恼人的问题

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

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

//使控件正确显示的计时器。
私有无效计时器(对象o、ElapsedEventArgs e)
{
尝试
{
ItemToHideDisplay.Visible=true;
var mousePoint=this.PointToClient(Cursor.Position);
如果(mousePoint.X>this.Width||
鼠标点.X<0||
鼠标点.Y>此高度||
鼠标点(Y<0)
{
HideDisplayTimer.Stop();
ItemToHideDisplay.Visible=false;
基地,离港(e);;
}
}
抓住
{
//我们不希望应用程序崩溃。。。
}
}
MouseCenter上受保护的覆盖无效(事件参数e)
{
HideDisplayTimer.Start();
base.onmouseinter(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);
        }
    }
在MouseLeave上受保护的覆盖无效(事件参数e)
{
var mousePoint=this.PointToClient(Cursor.Position);
如果(mousePoint.X>this.Width||
鼠标点.X<0||
鼠标点.Y>此高度||
鼠标点(Y<0)
{
基地,离港(e);;
}
}
这样,当输入我显示的控件(输入父控件)时,不会触发鼠标离开事件! 它起作用了

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

你可以使控件对鼠标事件“透明”。所以鼠标事件将通过它

您必须编写自己的类来继承所需的控件。例如,如果一个
标签
是您的特定控件,则创建一个继承
标签
的新类-您得到了要点。:-)

在新类中,然后使用窗口消息使控件忽略鼠标事件:

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);
    }
}
您可以阅读更多信息。

我会在这里做一个技巧:)我会将该控件包装成一个新控件:)检查此项

XAML:


它很轻,容易操作。享受

您可以为表单注册一个消息过滤器,并预处理表单中的事件。由于这一点,您不必覆盖您的孩子控制等

在父窗体中注册后,也将适用于子窗体,因此即使子窗体覆盖了窗体的一部分,目标控件仍应根据鼠标位置显示/消失

在下面的示例中,表单上有一个面板,该面板内部有一个按钮

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;
        }
    }
}
公共部分类表单1:表单
{
公共表格1()
{
初始化组件();
}
内部无效CaptureMouseMove(点位置)
{
if(面板1.矩形到屏幕(面板1.ClientRectangle.包含(位置))
{
按钮1.可见=真实;
Console.WriteLine(位置+”在“+面板1.矩形到屏幕(面板1.ClientRectangle));
}
其他的
{
按钮1.可见=假;
Console.WriteLine(位置+“out”+面板1.矩形屏幕(面板1.ClientRectangle));
}
}
内部bool Form1\u ProcessMouseMove(消息m)
{
点位置=新点(m.LParam.ToInt32()&0xffff,m.LParam.ToInt32()>>16);
控制中心=控制从手柄(m.HWnd);
如果(ctr!=null)
{
位置=中心指向屏幕(位置);
}
其他的
{
pos=该点至屏幕(pos);
}
此.CaptureMouseMove(pos);
返回false;
}
专用MouseMoveMessageFilter mouseMessageFilter;
受保护的覆盖无效加载(事件参数e)
{
基础荷载(e);
//在此处添加过滤器
this.mouseMessageFilter=新的MouseMoveMessageFilter();
this.mouseMessageFilter.TargetForm=此;
this.mouseMessageFilter.ProcessMouseMove=this.Form1\u ProcessMouseMove;
Application.AddMessageFilter(this.mouseMessageFilter);
}
关闭时受保护的覆盖无效(事件参数e)
{
//从这里取下过滤器
Application.RemoveMessageFilter(this.mouseMessageFilter);
基础。一旦关闭(e);
}
私有类MouseMoveMessageFilter:IMessageFilter
{
公共表单TargetForm{get;set;}
public Func ProcessMouseMove;
公共bool预过滤器消息(参考消息m)
{
如果(TargetForm.IsDisposed)返回false;
//WM_鼠标移动
如果(m.Msg==0x0200)
{
if(ProcessMouseMove!=null)
返回进程mousemove(m);
}
返回false;
}
}
}

问题用winforms标记。这看起来像WPF.ups,没错。对不起,这个想法可以复制。我已经试过了
<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;        
}
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;
        }
    }
}