使用ToolStripDropDown模拟工具提示功能
本文关键字:功能 工具提示 模拟 ToolStripDropDown 使用 | 更新日期: 2023-09-27 18:26:23
我创建了一个自定义UserControl,它在ToolStripDropDown中显示自己以模拟ToolTip功能。当我订阅他们的MouseEnter和MouseLeave事件时,它对任何控件都很好。
我还想将它用于自定义对象(而不是控件)。我已经创建了一个定义MouseEnter和MouseLeave事件的界面,这样我就可以向该工具提示订阅任何对象(例如自定义绘制的基本体)。这些类自己决定何时触发MouseEnter和MouseLeave。
我的问题是,当显示工具提示时,我的包含自定义对象的UserControls不会接收MouseMove事件,即使工具提示显示在鼠标的侧面而不是下面。如果鼠标不再位于有问题的对象上,我将基于在MouseMove中的检查生成我自己的MouseLeave事件。但显然,如果没有MouseMove事件,MouseLeave就永远不会开火。
当我在控件上显示工具提示时,会发生同样的事情(没有MouseMove事件),只是MouseLeave仍然会触发。
1) 如何模拟MouseLeave功能?我必须使用p/invoke来SetCapture鼠标移动吗?或者有人知道更简单的方法吗?
2) 当工具提示显示时,即使ToolStripDropDown或其中的UserControl都没有触发"GotFocus"事件,只要工具提示显示,我仍然会失去键盘焦点,这也是不理想的工具提示行为。我能避免吗?
基本上,我希望它是一个完全不可聚焦,无干扰的工具提示。我看过一个名为SuperTooltip的示例项目,但它也有同样的缺陷。我尝试过设置ControlStyles。可选择为false,但没有注意到任何更改。
这是我创建工具提示UserControl:的代码
public CustomTooltip()
{
this.SetStyle(ControlStyles.Selectable, false);
dropDown = new ToolStripDropDown();
dropDown.AutoSize = false;
dropDown.Margin = Padding.Empty;
dropDown.Padding = Padding.Empty;
host = new ToolStripControlHost(this);
host.AutoSize = false;
host.Margin = Padding.Empty;
host.Padding = Padding.Empty;
this.Location = new Point(0, 0);
dropDown.Items.Add(host);
}
我用显示
dropDown.Show(
new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Current.Size.Height),
ToolStripDropDownDirection.BelowRight
);
我发现我可以通过从Form派生而完全不使用ToolStripDropDown来实现这一点。此类模拟工具提示的功能,并允许自定义淡入/淡出参数。您可以为MouseEnter和MouseLeave订阅控件或任何定义并实现ITooltipTarget的类。
public abstract class CustomTooltip : Form
{
#region Static
protected static readonly int FadeInterval = 25;
protected static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOMOVE = 0x0002;
private const int SWP_NOACTIVATE = 0x0010;
private const int WS_POPUP = unchecked((int)0x80000000);
private const int WS_EX_TOPMOST = 0x00000008;
private const int WS_EX_NOACTIVATE = 0x08000000;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
#endregion
protected Dictionary<object, object> subscriptions;
protected Timer popupTimer;
protected Timer fadeTimer;
protected bool isFading = false;
protected int fadeDirection = 1;
[DefaultValue(500)]
/// <summary>
/// Delay in milliseconds before the tooltip is shown. 0 means no delay.
/// </summary>
public int PopupDelay
{
get
{
return _popupDelay;
}
set
{
_popupDelay = value;
if (value > 0)
popupTimer.Interval = value;
else
popupTimer.Interval = 1;
}
}
private int _popupDelay = 500;
[DefaultValue(0)]
/// <summary>
/// How long to spend fading in and out in milliseconds. 0 means no fade.
/// </summary>
public int FadeTime
{
get
{
return _fadeTime;
}
set
{
_fadeTime = value;
}
}
private int _fadeTime = 0;
public virtual new object Tag
{
get
{
return base.Tag;
}
set
{
base.Tag = value;
OnTagChanged(EventArgs.Empty);
}
}
public CustomTooltip()
{
this.SetStyle(ControlStyles.Selectable, false);
subscriptions = new Dictionary<object, object>();
popupTimer = new Timer();
popupTimer.Interval = PopupDelay;
popupTimer.Tick += new EventHandler(popupTimer_Tick);
fadeTimer = new Timer();
fadeTimer.Interval = FadeInterval;
fadeTimer.Tick += new EventHandler(fadeTimer_Tick);
this.Visible = false;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.ControlBox = false;
this.StartPosition = FormStartPosition.Manual;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= WS_POPUP;
cp.ExStyle |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
return cp;
}
}
protected override bool ShowWithoutActivation
{
get
{
return true;
}
}
protected virtual void Subscribe(Control control, object tag)
{
subscriptions.Add(control, tag);
control.MouseEnter += new EventHandler(Item_MouseEnter);
control.MouseLeave += new EventHandler(Item_MouseLeave);
}
protected virtual void Subscribe(ITooltipTarget item, object tag)
{
subscriptions.Add(item, tag);
item.MouseEnter += new EventHandler(Item_MouseEnter);
item.MouseLeave += new EventHandler(Item_MouseLeave);
}
public virtual void Unsubscribe(Control control)
{
control.MouseEnter -= new EventHandler(Item_MouseEnter);
control.MouseLeave -= new EventHandler(Item_MouseLeave);
subscriptions.Remove(control);
}
public virtual void Unsubcribe(ITooltipTarget item)
{
item.MouseEnter -= new EventHandler(Item_MouseEnter);
item.MouseLeave -= new EventHandler(Item_MouseLeave);
subscriptions.Remove(item);
}
public void ClearSubscriptions()
{
foreach (object o in subscriptions.Keys)
{
if (o is Control)
Unsubscribe((Control)o);
else if (o is ITooltipTarget)
Unsubscribe((ITooltipTarget)o);
}
}
protected virtual void OnTagChanged(EventArgs e)
{
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
Item_MouseLeave(null, EventArgs.Empty);
}
private void Item_MouseEnter(object sender, EventArgs e)
{
Tag = subscriptions[sender];
popupTimer.Start();
}
private void Item_MouseLeave(object sender, EventArgs e)
{
if (FadeTime > 0)
FadeOut();
else
this.Hide();
popupTimer.Stop();
}
protected virtual void FadeIn()
{
isFading = true;
Opacity = 0;
fadeDirection = 1;
fadeTimer.Start();
}
protected virtual void FadeOut()
{
isFading = true;
Opacity = 1;
fadeDirection = -1;
fadeTimer.Start();
}
private void popupTimer_Tick(object sender, EventArgs e)
{
if (isFading)
this.Hide();
if (FadeTime > 0)
FadeIn();
Location = new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Size.Height);
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
Show();
popupTimer.Stop();
}
private void fadeTimer_Tick(object sender, EventArgs e)
{
if (Opacity == 0 && fadeDirection == -1)
{
isFading = false;
fadeTimer.Stop();
this.Hide();
}
else if (Opacity == 1 && fadeDirection == 1)
{
fadeTimer.Stop();
isFading = false;
}
else
{
double change = ((double)fadeTimer.Interval / (double)FadeTime) * (double)fadeDirection;
Opacity += change;
}
}
}
public interface ITooltipTarget
{
event EventHandler MouseEnter;
event EventHandler MouseLeave;
}
要使用上述类,您只需要从CustomTooltip派生,即可制作自己的自定义工具提示。派生类将使用Tag属性来确定显示的内容。例如,如果我想要一个将图像与对象关联起来并绘制该图像的工具提示,我会执行以下操作:
public class CustomImageTooltip : CustomTooltip
{
public Image Image
{
get
{
if (Tag is Image)
return Tag as Image;
else
return null;
}
}
public CustomImageTooltip()
{
InitializeComponent();
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
}
public void Subscribe(Control control, Image image)
{
base.Subscribe(control, image);
}
public void Subscribe(ITooltipTarget item, Image image)
{
base.Subscribe(item, image);
}
protected override void OnTagChanged(EventArgs e)
{
base.OnTagChanged(e);
if (Image != null)
this.Size = Image.Size;
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.White);
if (Image != null)
g.DrawImage(
Image,
new RectangleF(0, 0, ClientSize.Width, ClientSize.Height),
new RectangleF(0, 0, Image.Size.Width, Image.Size.Height),
GraphicsUnit.Pixel
);
g.DrawRectangle(Pens.Black, 0, 0, ClientRectangle.Width - 1, ClientRectangle.Height - 1);
}
}
为了在应用程序中使用这个CustomImageTooltip类,您只需要订阅和取消订阅该类的一个实例:
// Constructor
customImageTooltip = new CustomImageTooltip();
foreach (CustomObject o in myCustomObjects)
{
customImageTooltip.Subscribe(o, o.Image);
}
// Destructor
foreach (CustomObject o in myCustomObjects)
{
customImageTooltip.Unsubscribe(o);
}