2012-03-02 1 views
1

도구 설명 기능을 에뮬레이트하기 위해 ToolStripDropDown에 자신을 표시하는 사용자 지정 UserControl을 만들었습니다. MouseEnter 및 MouseLeave 이벤트를 구독 할 때 대부분 Control에서 잘 작동합니다.ToolStripDropDown을 사용하여 도구 설명 기능 에뮬레이션

컨트롤이 아닌 사용자 지정 개체에도 사용하고 싶습니다. MouseEnter 및 MouseLeave 이벤트를 정의하는 인터페이스를 작성하여이 객체 설명 (예 : 사용자 정의 그린 프리미티브)을이 툴팁에 등록 할 수 있습니다. 이러한 클래스는 MouseEnter 및 MouseLeave를 트리거 할시기를 결정하기 위해 자체적으로 작업합니다.

내 문제는 툴팁이 표시 될 때 툴팁이 마우스가 아닌 옆면에 보이더라도 사용자 정의 객체를 포함하는 내 UserControls가 MouseMove 이벤트를받지 않는다는 것입니다. 마우스가 문제의 객체 위에 더 이상 없다면 MouseMove를 검사하여 내 자신의 MouseLeave 이벤트를 생성합니다. 그러나 MouseMove 이벤트가 없으면 MouseLeave는 실행되지 않습니다.

컨트롤에 툴팁을 표시하면 MouseLeave가 여전히 실행된다는 점을 제외하고는 똑같은 일이 발생합니다 (MouseMove 이벤트 없음).

1) 어떻게이 MouseLeave 기능을 에뮬레이션 할 수 있습니까? SetCapture 마우스 움직임에 p/invoke를 사용해야합니까? 아니면 누구나 쉬운 방법을 알고 있습니까?

2) ToolStripDropDown 또는 내 UserControl이 "GotFocus"이벤트를 발생시키지 않더라도 도구 설명이 바람직하지 않은 한 키보드 포커스를 잃어 버리며 툴팁 동작이 바람직하지 않습니다. 나는 그것을 피할 수 있습니까?

기본적으로 나는 완전히 집중력이없고 간섭하지 않는 툴팁이되고 싶습니다. SuperTooltip이라는 샘플 프로젝트를 살펴 보았지만 동일한 결함 기능이 있습니다. ControlStyles.Selectable을 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 
); 

답변

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