/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WmPop"]/*' /> /// <devdoc> /// Called just before the tooltip is hidden /// </devdoc> /// <internalonly/> private void WmPop() { NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; if (win == null) { win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); } if (win == null) { return; } Control control = win as Control; TipInfo tt = (TipInfo)tools[win]; if (tt == null) { return; } // Must reset the maxwidth to the screen size. // This is required to resolve VSWhidbey bug# 363408 if ((tt.TipType & TipInfo.Type.Auto) != 0 || (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) { Screen screen = Screen.FromPoint(Cursor.Position); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, screen.WorkingArea.Width); } // For non-auto tips (those showned through // the show(...) methods, we need to dissassociate // them from the tip control. if ((tt.TipType & TipInfo.Type.Auto) == 0) { tools.Remove(control); owners.Remove(win.Handle); control.HandleCreated -= new EventHandler(this.HandleCreated); control.HandleDestroyed -= new EventHandler(this.HandleDestroyed); created.Remove(control); if (originalPopupDelay != 0) { AutoPopDelay = originalPopupDelay; originalPopupDelay = 0; } } else { // Clear all other flags except for the // Auto flag to ensure automatic tips can still show tt.TipType = TipInfo.Type.Auto; tt.Position = Point.Empty; tools[control] = tt; } } }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WndProc"]/*' /> /// <devdoc> /// WNDPROC /// </devdoc> /// <internalonly/> private void WndProc(ref Message msg) { switch (msg.Msg) { case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY: NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) msg.GetLParam(typeof(NativeMethods.NMHDR)); if (nmhdr.code == NativeMethods.TTN_SHOW && !trackPosition) { WmShow(); } else if (nmhdr.code == NativeMethods.TTN_POP) { WmPop(); window.DefWndProc(ref msg); } break; case NativeMethods.WM_WINDOWPOSCHANGING: WmWindowPosChanging(ref msg); break; case NativeMethods.WM_WINDOWPOSCHANGED: if (!WmWindowPosChanged()) { window.DefWndProc(ref msg); } break; case NativeMethods.WM_MOUSEACTIVATE: WmMouseActivate(ref msg); break; case NativeMethods.WM_MOVE: WmMove(); break; case NativeMethods.TTM_WINDOWFROMPOINT: WmWindowFromPoint(ref msg); break; case NativeMethods.WM_PRINTCLIENT: goto case NativeMethods.WM_PAINT; case NativeMethods.WM_PAINT: if (ownerDraw && !isBalloon && !trackPosition) { NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); IntPtr dc = UnsafeNativeMethods.BeginPaint(new HandleRef(this,Handle),ref ps); Graphics g = Graphics.FromHdcInternal(dc); try { Rectangle bounds = new Rectangle(ps.rcPaint_left,ps.rcPaint_top, ps.rcPaint_right - ps.rcPaint_left, ps.rcPaint_bottom - ps.rcPaint_top); if (bounds == Rectangle.Empty ) { return; } NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = unchecked( (int) (long)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti)); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; Control ac = Control.FromHandleInternal(ti.hwnd); if (win == null) { win = (IWin32Window)ac; } Font font; IntSecurity.ObjectFromWin32Handle.Assert(); try { font = Font.FromHfont(UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_GETFONT, 0, 0)); } catch (ArgumentException) { // VSWhidbey 209345 - if the current default tooltip font is a non-TrueType font, then // Font.FromHfont throws this exception, so fall back to the default control font. font = Control.DefaultFont; } finally { CodeAccessPermission.RevertAssert(); } OnDraw(new DrawToolTipEventArgs(g, win, ac, bounds, GetToolTip(ac), BackColor, ForeColor, font)); break; } } finally { g.Dispose(); UnsafeNativeMethods.EndPaint(new HandleRef(this,Handle),ref ps); } } //If not OwnerDraw, fall through goto default; default: window.DefWndProc(ref msg); break; } }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WmShow"]/*' /> /// <devdoc> /// Handles the TTN_SHOW message. /// </devdoc> /// <internalonly/> private void WmShow() { //Get the Bounds.... NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; if (win == null) { win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); } if (win == null) { return; } Control toolControl = win as Control; Size currentTooltipSize = r.Size; PopupEventArgs e = new PopupEventArgs(win, toolControl, IsBalloon, currentTooltipSize); OnPopup(e); DataGridView dataGridView = toolControl as DataGridView; if (dataGridView != null && dataGridView.CancelToolTipPopup(this)) { // The dataGridView cancelled the tooltip. e.Cancel = true; } // We need to re-get the rectangle of the tooltip here because // any of the tooltip attributes/properties could have been updated // during the popup event; in which case the size of the tooltip is // affected. e.ToolTipSize is respected over r.Size UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); currentTooltipSize = (e.ToolTipSize == currentTooltipSize) ? r.Size:e.ToolTipSize; if (IsBalloon) { // Get the text display rectangle UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ADJUSTRECT, 1, ref r); if (r.Size.Height > currentTooltipSize.Height) currentTooltipSize.Height = r.Size.Height; } // Set the max possible size of the tooltip to the size we received. // This prevents the operating system from drawing incorrect rectangles // when determing the correct display rectangle. VSWhidbey if (currentTooltipSize != r.Size) { Screen screen = Screen.FromPoint(Cursor.Position); int maxwidth = (IsBalloon) ? Math.Min(currentTooltipSize.Width-2*XBALLOONOFFSET, screen.WorkingArea.Width): Math.Min(currentTooltipSize.Width, screen.WorkingArea.Width); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, maxwidth); } if (e.Cancel) { cancelled = true; SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); } else { cancelled = false; // Only width/height changes are respected, so set top,left to what we got earlier SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.HWND_TOPMOST, r.left, r.top, currentTooltipSize.Width, currentTooltipSize.Height, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER); } } }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WmWindowPosChanging"]/*' /> /// <devdoc> /// Handles the WM_WINDOWPOSCHANGING message. /// </devdoc> /// <internalonly/> private unsafe void WmWindowPosChanging(ref Message m) { if (cancelled) { return; } NativeMethods.WINDOWPOS* wp = (NativeMethods.WINDOWPOS *)m.LParam; Cursor currentCursor = Cursor.CurrentInternal; Point cursorPos = Cursor.Position; NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; if (win == null) { win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); } if (win == null || !IsWindowActive(win)) { return; } TipInfo tt = null; if (win != null) { tt = (TipInfo)tools[win]; if (tt == null) { return; } // Treeview handles its own ToolTips. TreeView treeView = win as TreeView; if (treeView != null) { if (treeView.ShowNodeToolTips) { return; } } } if (IsBalloon) { wp->cx += 2*XBALLOONOFFSET; return; } if ( (tt.TipType & TipInfo.Type.Auto) != 0) { window.DefWndProc(ref m); return; } if ( ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0) && tt.Position == Point.Empty ) { Screen screen = Screen.FromPoint(cursorPos); if (currentCursor != null) { wp->x = cursorPos.X; // Since HotSpot requires a security demand .. we assert this and revert Assert immediately try { IntSecurity.ObjectFromWin32Handle.Assert(); wp->y = cursorPos.Y; if (wp->y + wp->cy + currentCursor.Size.Height - currentCursor.HotSpot.Y > screen.WorkingArea.Bottom) { wp->y = cursorPos.Y - wp->cy; } else { wp->y = cursorPos.Y + currentCursor.Size.Height - currentCursor.HotSpot.Y; } } finally { CodeAccessPermission.RevertAssert(); } } if (wp->x + wp->cx >screen.WorkingArea.Right) { wp->x = screen.WorkingArea.Right - wp->cx; } } else if ((tt.TipType & TipInfo.Type.SemiAbsolute) != 0 && tt.Position != Point.Empty) { Screen screen = Screen.FromPoint(tt.Position); wp->x = tt.Position.X; if (wp->x + wp->cx >screen.WorkingArea.Right) { wp->x = screen.WorkingArea.Right - wp->cx; } wp->y = tt.Position.Y; if (wp->y + wp->cy > screen.WorkingArea.Bottom) { wp->y = screen.WorkingArea.Bottom - wp->cy; } } } m.Result = IntPtr.Zero; }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WmMouseActivate"]/*' /> /// <devdoc> /// Handles the WM_MOUSEACTIVATE message. /// </devdoc> /// <internalonly/> private void WmMouseActivate(ref Message msg) { NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; if (win == null) { win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); } if (win == null) { return; } NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(win, Control.GetSafeHandle(win)), ref r); Point cursorLocation = Cursor.Position; // Do not activate the mouse if its within the bounds of the // the associated tool if (cursorLocation.X >= r.left && cursorLocation.X <= r.right && cursorLocation.Y >= r.top && cursorLocation.Y <= r.bottom) { msg.Result = (IntPtr)NativeMethods.MA_NOACTIVATE; } } }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.WmMove"]/*' /> /// <devdoc> /// Handles the WM_MOVE message. /// </devdoc> /// <internalonly/> private void WmMove() { NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(this, Handle), ref r); NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETCURRENTTOOL, 0, ti); if (ret != 0) { IWin32Window win = (IWin32Window)owners[ti.hwnd]; if (win == null) { win = (IWin32Window)Control.FromHandleInternal(ti.hwnd); } if (win == null) { return; } TipInfo tt = (TipInfo)tools[win]; if (win == null || tt==null) { return; } // Treeview handles its own ToolTips. TreeView treeView = win as TreeView; if (treeView != null) { if (treeView.ShowNodeToolTips) { return; } } // Reposition the tooltip when its about to be shown.. since the tooltip can go out of screen workingarea bounds // Reposition would check the bounds for us. if (tt.Position != Point.Empty) { Reposition(tt.Position, r.Size); } } }
private void SetTool(IWin32Window win, string text, TipInfo.Type type, Point position) { Control tool = win as Control; if (tool != null && tools.ContainsKey(tool)) { bool allocatedString = false; NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); try { ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); ti.hwnd = tool.Handle; ti.uId = tool.Handle; int ret = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETTOOLINFO, 0, ti); if (ret != 0) { ti.uFlags |= NativeMethods.TTF_TRACK; if (type == TipInfo.Type.Absolute || type == TipInfo.Type.SemiAbsolute) { ti.uFlags |= NativeMethods.TTF_ABSOLUTE; } ti.lpszText = Marshal.StringToHGlobalAuto(text); allocatedString = true; } TipInfo tt = (TipInfo)tools[tool]; if (tt == null) { tt = new TipInfo(text, type); } else { tt.TipType |= type; tt.Caption = text; } tt.Position = position; tools[tool] = tt; UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_SETTOOLINFO, 0, ti); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKACTIVATE, 1, ti); } finally { if(allocatedString && IntPtr.Zero != ti.lpszText) { Marshal.FreeHGlobal(ti.lpszText); } } } else { Hide(win); // Need to do this BEFORE we call GetWinTOOLINFO, since it relies on the tools array to be populated // in order to find the toplevelparent. TipInfo tt = (TipInfo)tools[tool]; if (tt == null) { tt = new TipInfo(text, type); } else { tt.TipType |= type; tt.Caption = text; } tt.Position = position; tools[tool] = tt; IntPtr hWnd = Control.GetSafeHandle(win); owners[hWnd] = win; NativeMethods.TOOLINFO_TOOLTIP toolInfo = GetWinTOOLINFO(hWnd); toolInfo.uFlags |= NativeMethods.TTF_TRACK; if (type == TipInfo.Type.Absolute || type == TipInfo.Type.SemiAbsolute) { toolInfo.uFlags |= NativeMethods.TTF_ABSOLUTE; } try { toolInfo.lpszText = Marshal.StringToHGlobalAuto(text); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_ADDTOOL, 0, toolInfo); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKACTIVATE, 1, toolInfo); } finally { if(IntPtr.Zero != toolInfo.lpszText) { Marshal.FreeHGlobal(toolInfo.lpszText); } } } if (tool != null) { // Lets find the Form for associated Control ... // and hook up to the Deactivated event to Hide the Shown tooltip Form baseFrom = tool.FindFormInternal(); if (baseFrom != null) { baseFrom.Deactivate += new EventHandler(this.BaseFormDeactivate); } } }
private NativeMethods.TOOLINFO_TOOLTIP GetWinTOOLINFO(IntPtr hWnd) { NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); ti.hwnd = hWnd; ti.uFlags |= NativeMethods.TTF_IDISHWND | NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; // RightToLeft reading order // Control richParent = TopLevelControl; if (richParent != null && richParent.RightToLeft == RightToLeft.Yes) { bool isWindowMirrored = ((unchecked((int)(long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, hWnd), NativeMethods.GWL_STYLE)) & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL); //Indicates that the ToolTip text will be displayed in the opposite direction //to the text in the parent window. if (!isWindowMirrored) { ti.uFlags |= NativeMethods.TTF_RTLREADING; } } ti.uId = ti.hwnd; return ti; }
/// <include file='doc\ToolTip.uex' path='docs/doc[@for="ToolTip.GetMinTOOLINFO"]/*' /> /// <devdoc> /// Returns a new instance of the TOOLINFO_T structure with the minimum /// required data to uniquely identify a region. This is used primarily /// for delete operations. NOTE: This cannot force the creation of a handle. /// </devdoc> /// <internalonly/> private NativeMethods.TOOLINFO_TOOLTIP GetMinTOOLINFO(Control ctl) { NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP(); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); ti.hwnd = ctl.Handle; ti.uFlags |= NativeMethods.TTF_IDISHWND; ti.uId = ctl.Handle; return ti; }