2012-03-02 55 views
1

我创建了一个自定义的UserControl,它将自身显示在ToolStripDropDown中以模拟ToolTip功能。当我订阅他们的MouseEnter和MouseLeave事件时,它对于任何控件都能正常工作。使用ToolStripDropDown模拟Tooltip功能

我也想用它来定制对象(不是控件)。我创建了一个定义MouseEnter和MouseLeave事件的接口,以便我可以订阅任何对象(如自定义绘制的基元)到此工具提示。这些类自己确定何时触发MouseEnter和MouseLeave。

我的问题是,当显示工具提示时,包含自定义对象的UserControls不会收到MouseMove事件,即使Tooltip显示在旁边而不是在鼠标下方。如果鼠标不再位于所讨论的对象上,我将根据检查MouseMove生成自己的MouseLeave事件。但显然没有MouseMove事件,MouseLeave从不会触发。

当我在控件上显示工具提示时,除了MouseLeave仍然触发外,同样的事情发生(没有MouseMove事件)。

1)我该如何模拟这个MouseLeave功能?我是否必须使用p/invoke来设置捕捉鼠标移动,还是有人知道更简单的方法?

2)当工具提示显示时,即使ToolStripDropDown或我的UserControl内部都没有触发“GotFocus”事件,只要显示的工具提示不符合工具提示行为,我仍然会失去键盘焦点。我能避免吗?

基本上我希望它是一个完全不可重点,不干扰的工具提示。我看了一个名为SuperTooltip的示例项目,但它具有相同的缺陷功能。我已经尝试将ControlStyles.Selectable设置为false,并没有注意到任何更改。

这里是我创造我提示用户控件的代码:

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

回答

1

我发现我能够通过从表派生并没有使用来完成此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); 
}