【问题标题】:How to display/hide a control on mouse hover event如何在鼠标悬停事件上显示/隐藏控件
【发布时间】:2015-07-28 19:51:38
【问题描述】:

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

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

我找到了这个“解决方案”来帮助我获得一些“稳定”的东西。

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

这样在进入我已经显示的控件时(进入父控件),鼠标离开事件是不会触发的! 有效!

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

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

    您可以使控件对鼠标事件“透明”。所以鼠标事件只会通过它。

    您必须编写自己的类来继承所需的控件。例如,如果 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);
        }
    }
    

    您可以阅读有关WndProc at MSDN 的更多信息。

    【讨论】:

    • 但我希望仍然能够单击显示的控件。也可以这样过滤吗?
    • 要么你没有在你的问题中这么说,要么我没有找到它。无论如何,这使得它变得更加复杂,但它是可能的,因为我已经这样做了。我可以在几个小时内挖掘我的代码,但你可以搜索SetLayeredWindowAttributes。请注意,您正在深入挖掘 winforms... ;)
    • 我没说,对不起。虽然我找到了解决方案。你可以看看:)。感谢这个,我相信它以后会对我有所帮助!
    【解决方案2】:

    您可以为您的表单注册一个消息过滤器并预处理表单的mouse move 事件。多亏了这一点,您不必覆盖您的子控件等。

    message filter,一旦在父表单中注册,也将适用于子表单,因此即使您的表单的一部分被子表单覆盖,您的目标控件仍应根据鼠标出现/消失位置。

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

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

    【讨论】:

      【解决方案3】:

      我会在这里做一个技巧 :) 我会将控件包装到一个新控件中 :) 检查一下。

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

      它轻巧、简单且有效。享受

      【讨论】:

      • 这个问题是用 winforms 标记的。这看起来像 WPF。
      • UPS,真的。很抱歉,这个想法可以复制。
      • 我已经尝试过包装的东西(在winforms中)。有用。有时。但它不是很稳定。
      • 并非如此。如果我没记错的话,Winforms 会在您输入 cild 后立即在父级上引发“MouseLeave”事件 - 但我可能在这里错了。
      猜你喜欢
      • 2012-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多