private void DoLayout(IWin32Window window, Control content, StemPosition stemPosition, ref Rectangle toolTipBounds) { int bubbleSize = Win32.SendMessage(Handle, Win32.TTM_GETBUBBLESIZE, 0, ref _toolInfo); int bubbleWidth = bubbleSize & 0xFFFF; int bubbleHeight = bubbleSize >> 16; // centre our content on the bubble-area of the tooltip content.Left = (bubbleWidth - content.Width) / 2; if (StemPosition.BottomLeft == stemPosition || StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition) { // stem is below the bubble content.Top = (bubbleHeight - content.Height) / 2; } else { // stem is on top of the bubble int bubbleOffset = toolTipBounds.Height - bubbleHeight; content.Top = (bubbleHeight - content.Height) / 2 + bubbleOffset; } _contentPanel = new ContentPanel(Handle); _contentPanel.Width = toolTipBounds.Width; _contentPanel.Height = toolTipBounds.Height; _contentPanel.Controls.Add(content); Win32.SetWindowPos(Handle, Win32.HWND_TOPMOST, toolTipBounds.X, toolTipBounds.Y, 0, 0, Win32.SWP_NOACTIVATE | Win32.SWP_NOSIZE | Win32.SWP_NOOWNERZORDER); }
/// <summary> /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> at the specified relative position. /// </summary> /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param> /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param> /// <param name="x">The horizontal offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param> /// <param name="y">The vertical offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param> /// <param name="stemPosition">The desired position for the stem of the balloon.</param> /// <param name="duration">The time in milliseconds for which the <see cref="InteractiveToolTip"/> should be displayed, or zero for indefinite display.</param> /// <remarks> /// <para> /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>, /// or <see cref="Hide"/> is called, or the specified <paramref name="duration"/> is exceeded. It will be drawn with the stem positioned at the specified part /// of the balloon, tip located at the specified position relative to the top-left corner of <paramref name="window"/>. If there is insufficient space on the /// display for this, the stem and balloon may be repositioned to accomodate this. /// </para> /// <para> /// If the mouse is moved over the <see cref="InteractiveToolTip"/>, the duration timer will be halted. It will resume - with its original value - when the mouse /// leaves the <see cref="InteractiveToolTip"/> again. /// </para> /// </remarks> public void Show(Control content, IWin32Window window, int x, int y, StemPosition stemPosition, int duration) { if (null == content || null == window) { throw new ArgumentNullException(); } Hide(); _currentToolTip = new ToolTipWindow(content, window, x, y, stemPosition, UseAnimation, UseFading); if (duration > 0) { _currentToolTip.MouseEnter += delegate(object sender, EventArgs e) { _durationTimer.Stop(); }; _currentToolTip.MouseLeave += delegate(object sender, EventArgs e) { if (duration > 0) { _durationTimer.Start(); } }; _currentToolTip.MouseDown += delegate(object sender, EventArgs e) { if (duration > 0) { duration = 1; } _durationTimer.Start(); }; _durationTimer.Interval = duration; _durationTimer.Start(); } if (null != ToolTipShown) { ToolTipShown(this, new InteractiveToolTipEventArgs(window)); } }
/// <summary> /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> at the specified relative position. /// </summary> /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param> /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param> /// <param name="location">A <see cref="T:System.Drawing.Point"/> containing the offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param> /// <param name="stemPosition">The desired position for the stem of the balloon.</param> /// <param name="duration">The time in milliseconds for which the <see cref="InteractiveToolTip"/> should be displayed, or zero for indefinite display.</param> /// <remarks> /// <para> /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>, /// or <see cref="Hide"/> is called, or the specified <paramref name="duration"/> is exceeded. It will be drawn with the stem positioned at the specified part /// of the balloon, tip located at the specified position relative to the top-left corner of <paramref name="window"/>. If there is insufficient space on the /// display for this, the stem and balloon may be repositioned to accomodate this. /// </para> /// <para> /// If the mouse is moved over the <see cref="InteractiveToolTip"/>, the duration timer will be halted. It will resume - with its original value - when the mouse /// leaves the <see cref="InteractiveToolTip"/> again. /// </para> /// </remarks> public void Show(Control content, IWin32Window window, Point location, StemPosition stemPosition, int duration) { Show(content, window, location.X, location.Y, stemPosition, duration); }
/// <summary> /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> with its stem in the specified position. /// </summary> /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param> /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param> /// <param name="stemPosition">The desired position for the stem of the balloon.</param> /// <remarks> /// <para> /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>, /// or <see cref="Hide"/> is called. It will be drawn with the stem positioned at the specified part of the balloon, tip located at either the top-left corner /// (if <paramref name="stemPosition"/> specifies the bottom edge) or the bottom-left corner (if <paramref name="stemPosition"/> specifies the top edge) of /// <paramref name="window"/>. If there is insufficient space on the display for this, the stem and balloon may be repositioned to accomodate this. /// </para> /// </remarks> public void Show(Control content, IWin32Window window, StemPosition stemPosition) { Show(content, window, 0, 0, stemPosition, 0); }
public ToolTipWindow(Control content, IWin32Window window, int x, int y, StemPosition stemPosition, bool useAnimation, bool useFading) { Window = window; CreateParams createParams = new CreateParams(); createParams.ClassName = Win32.TOOLTIPS_CLASS; createParams.Style = Win32.TTS_ALWAYSTIP | Win32.TTS_BALLOON; if (!useAnimation) { createParams.Style |= Win32.TTS_NOANIMATE; } if (!useFading) { createParams.Style |= Win32.TTS_NOFADE; } CreateHandle(createParams); // first, work out the actual stem-position: the supplied value is a hint, but may have to be changed if there isn't enough space to accomodate it string contentSpacing = GetSizingText(content); // this is where the caller would like us to be Rectangle toolTipBounds = CalculateToolTipLocation(contentSpacing, Window, x, y, stemPosition); Screen currentScreen = Screen.FromHandle(Window.Handle); Rectangle screenBounds = currentScreen.WorkingArea; stemPosition = AdjustStemPosition(stemPosition, ref toolTipBounds, ref screenBounds); // and this is where we'll actually end up toolTipBounds = CalculateToolTipLocation(contentSpacing, Window, x, y, stemPosition); toolTipBounds.X = Math.Max(0, toolTipBounds.X); toolTipBounds.Y = Math.Max(0, toolTipBounds.Y); // build the tooltip window _toolInfo = CreateTool(contentSpacing, Window, stemPosition); // initial position to force the stem into the correct orientation int initialX = screenBounds.X; int initialY = screenBounds.Y; if (StemPosition.TopLeft == stemPosition || StemPosition.BottomLeft == stemPosition) { initialX += StemInset; } else if (StemPosition.TopCentre == stemPosition || StemPosition.BottomCentre == stemPosition) { initialX += screenBounds.Width / 2; } else { initialX += screenBounds.Width - StemInset; } if (StemPosition.BottomLeft == stemPosition || StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition) { initialY += screenBounds.Height; } Win32.SendMessage(Handle, Win32.TTM_TRACKPOSITION, 0, (initialY << 16) | initialX); // and finally display it Win32.SendMessage(Handle, Win32.TTM_TRACKACTIVATE, 1, ref _toolInfo); DoLayout(Window, content, stemPosition, ref toolTipBounds); _contentPanel.MouseDown += delegate(object sender, MouseEventArgs e) { if (null != MouseDown && _mouseOverToolTip) { _mouseOverToolTip = true; MouseDown(this, e); } }; _contentPanel.MouseEnter += delegate(object sender, EventArgs e) { if (null != MouseEnter && !_mouseOverToolTip) { _mouseOverToolTip = true; MouseEnter(this, e); } }; _contentPanel.MouseLeave += delegate(object sender, EventArgs e) { // only send the event if the mouse has actually left the balloon and not simply moved from _contentPanel to the caller-supplied content if (null != MouseLeave && _mouseOverToolTip && null == _contentPanel.GetChildAtPoint(_contentPanel.PointToClient(Control.MousePosition))) { _mouseOverToolTip = false; MouseLeave(this, e); } }; }
private Rectangle CalculateToolTipLocation(string contentSpacing, IWin32Window window, int x, int y, StemPosition stemPosition) { Rectangle toolTipBounds = new Rectangle(); Size toolTipSize = GetToolTipWindowSize(contentSpacing); Win32.RECT windowBounds = new Win32.RECT(); Win32.GetWindowRect(window.Handle, ref windowBounds); x += windowBounds.left; if (StemPosition.TopLeft == stemPosition || StemPosition.BottomLeft == stemPosition) { toolTipBounds.X = x - StemInset; } else if (StemPosition.TopCentre == stemPosition || StemPosition.BottomCentre == stemPosition) { toolTipBounds.X = x - (toolTipSize.Width / 2); } else { toolTipBounds.X = x - toolTipSize.Width + StemInset; } if (StemPosition.TopLeft == stemPosition || StemPosition.TopCentre == stemPosition || StemPosition.TopRight == stemPosition) { toolTipBounds.Y = windowBounds.bottom - y; } else { toolTipBounds.Y = y + windowBounds.top - toolTipSize.Height; } toolTipBounds.Width = toolTipSize.Width; toolTipBounds.Height = toolTipSize.Height; return(toolTipBounds); }
private StemPosition AdjustStemPosition(StemPosition stemPosition, ref Rectangle toolTipBounds, ref Rectangle screenBounds) { if (toolTipBounds.Left < screenBounds.Left) { // the window is too close to the left edge of the display if (StemPosition.TopCentre == stemPosition || StemPosition.TopRight == stemPosition) { stemPosition = StemPosition.TopLeft; } else if (StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition) { stemPosition = StemPosition.BottomLeft; } } else if (toolTipBounds.Right > screenBounds.Right) { // the window is too close to the right edge of the display if (StemPosition.TopCentre == stemPosition || StemPosition.TopLeft == stemPosition) { stemPosition = StemPosition.TopRight; } else if (StemPosition.BottomCentre == stemPosition || StemPosition.BottomLeft == stemPosition) { stemPosition = StemPosition.BottomRight; } } if (toolTipBounds.Top < screenBounds.Top) { // the window is too close to the top edge of the display switch (stemPosition) { case StemPosition.BottomLeft: stemPosition = StemPosition.TopLeft; break; case StemPosition.BottomCentre: stemPosition = StemPosition.TopCentre; break; case StemPosition.BottomRight: stemPosition = StemPosition.TopRight; break; } } else if (toolTipBounds.Bottom > screenBounds.Bottom) { // the window is too close to the bottom edge of the display switch (stemPosition) { case StemPosition.TopLeft: stemPosition = StemPosition.BottomLeft; break; case StemPosition.TopCentre: stemPosition = StemPosition.BottomCentre; break; case StemPosition.TopRight: stemPosition = StemPosition.BottomRight; break; } } return(stemPosition); }
private Win32.TOOLINFO CreateTool(string contentSpacing, IWin32Window window, StemPosition stemPosition) { Win32.TOOLINFO ti = new Win32.TOOLINFO(); ti.cbSize = Marshal.SizeOf(ti); ti.uFlags = Win32.TTF_IDISHWND | Win32.TTF_TRACK | Win32.TTF_TRANSPARENT; ti.uId = window.Handle; ti.hwnd = window.Handle; ti.lpszText = contentSpacing; if (StemPosition.BottomCentre == stemPosition || StemPosition.TopCentre == stemPosition) { ti.uFlags |= Win32.TTF_CENTERTIP; } if (0 == Win32.SendMessage(Handle, Win32.TTM_ADDTOOL, 0, ref ti)) { throw new Exception(); } // enable multi-line text-layout Win32.SendMessage(Handle, Win32.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); return(ti); }