如何在鼠标悬停事件上显示/隐藏控件
本文关键字:显示 隐藏 控件 事件 鼠标 悬停 | 更新日期: 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;
}
它轻,容易和工作。喜欢