2012-03-02 39 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); 
}