Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / WinForms / Managed / System / WinForms / ToolTip.cs / 4 / ToolTip.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Runtime.Serialization.Formatters; using System.Threading; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System; using System.Windows.Forms; using System.Windows.Forms.Design; using Hashtable = System.Collections.Hashtable; using System.Drawing; using Microsoft.Win32; using System.Security; using System.Security.Permissions; using System.Text; using System.Drawing.Design; using System.Globalization; ////// /// [ ProvideProperty("ToolTip", typeof(Control)), DefaultEvent("Popup"), ToolboxItemFilter("System.Windows.Forms"), SRDescription(SR.DescriptionToolTip) ] public class ToolTip : Component, IExtenderProvider { const int DEFAULT_DELAY = 500; const int RESHOW_RATIO = 5; const int AUTOPOP_RATIO = 10; const int XBALLOONOFFSET = 10; const int YBALLOONOFFSET = 8; Hashtable tools = new Hashtable(); int[] delayTimes = new int[4]; bool auto = true; bool showAlways = false; ToolTipNativeWindow window = null; Control topLevelControl = null; bool active = true; bool ownerDraw = false; object userData; Color backColor = SystemColors.Info; Color foreColor = SystemColors.InfoText; bool isBalloon; bool isDisposing; string toolTipTitle = string.Empty; ToolTipIcon toolTipIcon = (ToolTipIcon)0; ToolTipTimer timer; Hashtable owners = new Hashtable(); bool stripAmpersands = false; bool useAnimation = true; bool useFading = true; int originalPopupDelay = 0; // setting TTM_TRACKPOSITION will cause redundant POP and Draw Messages.. // Hence we gaurd against this by having this private Flag.. bool trackPosition = false; PopupEventHandler onPopup; DrawToolTipEventHandler onDraw; // Adding a tool twice breaks the ToolTip, so we need to track which // tools are created to prevent this... // Hashtable created = new Hashtable(); private bool cancelled = false; ////// Provides a small pop-up window containing a line of text /// that describes the purpose of a tool or control (usually represented as a /// graphical /// object) in a program. /// ////// /// public ToolTip(IContainer cont) : this() { cont.Add(this); } ////// Initializes a new instance of the ///class, given the container. /// /// /// public ToolTip() { window = new ToolTipNativeWindow(this); auto = true; delayTimes[NativeMethods.TTDT_AUTOMATIC] = DEFAULT_DELAY; AdjustBaseFromAuto(); } ////// Initializes a new instance of the ///class in its default state. /// /// /// [ SRDescription(SR.ToolTipActiveDescr), DefaultValue(true) ] public bool Active { get { return active; } set { if (active != value) { active = value; //lets not actually activate the tooltip if we're in the designer (just set the value) if (!DesignMode && GetHandleCreated()) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (value==true)? 1: 0, 0); } } } } ////// Gets or sets a value indicating whether the ///control is currently active. /// /// /// [ RefreshProperties(RefreshProperties.All), SRDescription(SR.ToolTipAutomaticDelayDescr), DefaultValue(DEFAULT_DELAY) ] public int AutomaticDelay { get { return delayTimes[NativeMethods.TTDT_AUTOMATIC]; } set { if (value < 0) { throw new ArgumentOutOfRangeException("AutomaticDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutomaticDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } SetDelayTime(NativeMethods.TTDT_AUTOMATIC, value); } } ////// Gets or sets /// the time (in milliseconds) that passes before the ///appears. /// /// /// [ RefreshProperties(RefreshProperties.All), SRDescription(SR.ToolTipAutoPopDelayDescr) ] public int AutoPopDelay { get { return delayTimes[NativeMethods.TTDT_AUTOPOP]; } set { if (value < 0) { throw new ArgumentOutOfRangeException("AutoPopDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "AutoPopDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } SetDelayTime(NativeMethods.TTDT_AUTOPOP, value); } } ////// Gets or sets the initial delay for the ///control. /// /// /// [ SRDescription(SR.ToolTipBackColorDescr), DefaultValue(typeof(Color),"Info") ] public Color BackColor { get { return backColor; } set { backColor = value; if (GetHandleCreated()) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(backColor), 0); } } } ////// Gets or sets the BackColor for the ///control. /// /// /// The createParams to create the window. /// ///protected virtual CreateParams CreateParams { [ SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) ] get { CreateParams cp = new CreateParams(); if (TopLevelControl != null) { cp.Parent = TopLevelControl.Handle; } cp.ClassName = NativeMethods.TOOLTIPS_CLASS; if (showAlways) { cp.Style = NativeMethods.TTS_ALWAYSTIP; } if (isBalloon) { cp.Style |= NativeMethods.TTS_BALLOON; } if (!stripAmpersands) { cp.Style |= NativeMethods.TTS_NOPREFIX; } if (!useAnimation) { cp.Style |= NativeMethods.TTS_NOANIMATE; } if (!useFading) { cp.Style |= NativeMethods.TTS_NOFADE; } cp.ExStyle = 0; cp.Caption = null; return cp; } } /// /// /// [ SRDescription(SR.ToolTipForeColorDescr), DefaultValue(typeof(Color),"InfoText") ] public Color ForeColor { get { return foreColor; } set { if (value.IsEmpty) { throw new ArgumentException(SR.GetString(SR.ToolTipEmptyColor, "ForeColor")); } foreColor = value; if (GetHandleCreated()) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(foreColor), 0); } } } internal IntPtr Handle { get { if (!GetHandleCreated()) { CreateHandle(); } return window.Handle; } } ////// Gets or sets the ForeColor for the ///control. /// /// SECREVIEW: Pattern to check if the caller has a permission without bubbling up a security exception /// private bool HasAllWindowsPermission { get { try { IntSecurity.AllWindows.Demand(); return true; } catch (SecurityException) { } return false; } } ////// /// [ SRDescription(SR.ToolTipIsBalloonDescr), DefaultValue(false) ] public bool IsBalloon { get { return isBalloon; } set { if (isBalloon != value) { isBalloon = value; if (GetHandleCreated()) { RecreateHandle(); } } } } // refer VsWhidbey 498263: ToolTips should be shown only on active Windows. private bool IsWindowActive(IWin32Window window) { Control windowControl = window as Control; // We want to enter in the IF block only if ShowParams does not return SW_SHOWNOACTIVATE. // for ToolStripDropDown ShowParams returns SW_SHOWNOACTIVATE, in which case we DONT want to check IsWindowActive and hence return true. if ((windowControl.ShowParams & 0xF) != NativeMethods.SW_SHOWNOACTIVATE) { IntPtr hWnd = UnsafeNativeMethods.GetActiveWindow(); IntPtr rootHwnd =UnsafeNativeMethods.GetAncestor(new HandleRef(window, window.Handle), NativeMethods.GA_ROOT); if (hWnd != rootHwnd) { TipInfo tt = (TipInfo)tools[windowControl]; if (tt != null && (tt.TipType & TipInfo.Type.SemiAbsolute) != 0) { tools.Remove(windowControl); DestroyRegion(windowControl); } return false; } } return true; } ////// Gets or sets the IsBalloon for the ///control. /// /// /// [ RefreshProperties(RefreshProperties.All), SRDescription(SR.ToolTipInitialDelayDescr) ] public int InitialDelay { get { return delayTimes[NativeMethods.TTDT_INITIAL]; } set { if (value < 0) { throw new ArgumentOutOfRangeException("InitialDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "InitialDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } SetDelayTime(NativeMethods.TTDT_INITIAL, value); } } ////// Gets or sets the initial delay for /// the ////// control. /// /// /// Indicates whether the ToolTip will be drawn by the system or the user. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.ToolTipOwnerDrawDescr) ] public bool OwnerDraw { get { return ownerDraw; } [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)] set { ownerDraw = value; } } ////// /// [ RefreshProperties(RefreshProperties.All), SRDescription(SR.ToolTipReshowDelayDescr) ] public int ReshowDelay { get { return delayTimes[NativeMethods.TTDT_RESHOW]; } set { if (value < 0) { throw new ArgumentOutOfRangeException("ReshowDelay", SR.GetString(SR.InvalidLowBoundArgumentEx, "ReshowDelay", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } SetDelayTime(NativeMethods.TTDT_RESHOW, value); } } ////// Gets or sets the length of time (in milliseconds) that /// it takes subsequent ToolTip instances to appear as the mouse pointer moves from /// one ToolTip region to /// another. /// ////// /// [ DefaultValue(false), SRDescription(SR.ToolTipShowAlwaysDescr) ] public bool ShowAlways { get { return showAlways; } set { if (showAlways != value) { showAlways = value; if (GetHandleCreated()) { RecreateHandle(); } } } } ////// Gets or sets a value indicating whether the ////// appears even when its parent control is not active. /// /// /// [ SRDescription(SR.ToolTipStripAmpersandsDescr), Browsable(true), DefaultValue(false) ] public bool StripAmpersands { get { return stripAmpersands; } set { if (stripAmpersands != value) { stripAmpersands = value; if (GetHandleCreated()) { RecreateHandle(); } } } } ////// When set to true, any ampersands in the Text property are not displayed. /// ///[ SRCategory(SR.CatData), Localizable(false), Bindable(true), SRDescription(SR.ControlTagDescr), DefaultValue(null), TypeConverter(typeof(StringConverter)), ] public object Tag { get { return userData; } set { userData = value; } } /// /// /// [ DefaultValue(ToolTipIcon.None), SRDescription(SR.ToolTipToolTipIconDescr) ] public ToolTipIcon ToolTipIcon { get { return toolTipIcon; } set { if (toolTipIcon != value) { //valid values are 0x0 to 0x3 if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolTipIcon.None, (int)ToolTipIcon.Error)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolTipIcon)); } toolTipIcon = value; if (toolTipIcon > 0 && GetHandleCreated()) { // If the title is null/empty, the icon won't display. string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); // Tooltip need to be updated to reflect the changes in the icon because // this operation directly affects the size of the tooltip UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); } } } } ////// Gets or sets an Icon on the ToolTip. /// ////// [ DefaultValue(""), SRDescription(SR.ToolTipTitleDescr) ] public string ToolTipTitle { get { return toolTipTitle; } set { if (value == null) { value = string.Empty; } if (toolTipTitle != value) { toolTipTitle = value; if (GetHandleCreated()) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, toolTipTitle); // Tooltip need to be updated to reflect the changes in the titletext because // this operation directly affects the size of the tooltip UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_UPDATE, 0, 0); } } } } private Control TopLevelControl { get { Control baseVar = null; if (topLevelControl == null) { Control[] regions = new Control[tools.Keys.Count]; tools.Keys.CopyTo(regions, 0); if (regions != null && regions.Length > 0) { for (int i=0; i/// Gets or sets the title of the ToolTip. /// ////// /// [ SRDescription(SR.ToolTipUseAnimationDescr), Browsable(true), DefaultValue(true) ] public bool UseAnimation { get { return useAnimation; } set { if (useAnimation != value) { useAnimation = value; if (GetHandleCreated()) { RecreateHandle(); } } } } ////// When set to true, animations are used when tooltip is shown or hidden. /// ////// /// [ SRDescription(SR.ToolTipUseFadingDescr), Browsable(true), DefaultValue(true) ] public bool UseFading { get { return useFading; } set { if (useFading != value) { useFading = value; if (GetHandleCreated()) { RecreateHandle(); } } } } ////// When set to true, a fade effect is used when tooltips are shown or hidden. /// ////// /// [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipDrawEventDescr)] public event DrawToolTipEventHandler Draw { add { onDraw += value; } remove { onDraw -= value; } } ///Fires in OwnerDraw mode when the tooltip needs to be drawn. ////// /// [SRCategory(SR.CatBehavior),SRDescription(SR.ToolTipPopupEventDescr)] public event PopupEventHandler Popup { add { onPopup += value; } remove { onPopup -= value; } } ///Fires when the tooltip is just about to be shown. ////// /// Adjusts the other delay values based on the Automatic value. /// ///private void AdjustBaseFromAuto() { delayTimes[NativeMethods.TTDT_RESHOW] = delayTimes[NativeMethods.TTDT_AUTOMATIC] / RESHOW_RATIO; delayTimes[NativeMethods.TTDT_AUTOPOP] = delayTimes[NativeMethods.TTDT_AUTOMATIC] * AUTOPOP_RATIO; delayTimes[NativeMethods.TTDT_INITIAL] = delayTimes[NativeMethods.TTDT_AUTOMATIC]; } private void HandleCreated(object sender, EventArgs eventargs) { // Reset the toplevel control when the owner's handle is recreated. ClearTopLevelControlEvents(); topLevelControl = null; CreateRegion((Control)sender); CheckNativeToolTip((Control)sender); CheckCompositeControls((Control)sender); } private void CheckNativeToolTip(Control associatedControl) { //Wait for the Handle Creation.. if (!GetHandleCreated()) { return; } TreeView treeView = associatedControl as TreeView; if (treeView != null) { if (treeView.ShowNodeToolTips) { treeView.SetToolTip(this,GetToolTip(associatedControl)); } } if (associatedControl is ToolBar) { ((ToolBar)associatedControl).SetToolTip(this); } TabControl tabControl = associatedControl as TabControl; if (tabControl!= null) { if (tabControl.ShowToolTips) { tabControl.SetToolTip(this, GetToolTip(associatedControl)); } } if (associatedControl is ListView) { ((ListView)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); } if (associatedControl is StatusBar) { ((StatusBar)associatedControl).SetToolTip(this); } // Label now has its own Tooltip for AutoEllipsis... // So this control too falls in special casing... // We need to disable the LABEL AutoEllipsis tooltip and show // this tooltip always... if (associatedControl is Label) { ((Label)associatedControl).SetToolTip(this); } } private void CheckCompositeControls(Control associatedControl) { if (associatedControl is UpDownBase) { ((UpDownBase)associatedControl).SetToolTip(this, GetToolTip(associatedControl)); } } private void HandleDestroyed(object sender, EventArgs eventargs) { DestroyRegion((Control)sender); } /// /// /// Fires the Draw event. /// private void OnDraw(DrawToolTipEventArgs e) { if(onDraw != null) { onDraw(this,e); } } ////// /// Fires the Popup event. /// private void OnPopup(PopupEventArgs e) { if(onPopup != null) { onPopup(this,e); } } private void TopLevelCreated(object sender, EventArgs eventargs) { CreateHandle(); CreateAllRegions(); } private void TopLevelDestroyed(object sender, EventArgs eventargs) { DestoyAllRegions(); DestroyHandle(); } ////// /// Returns true if the tooltip can offer an extender property to the /// specified target component. /// ///public bool CanExtend(object target) { if (target is Control && !(target is ToolTip)) { return true; } return false; } private void ClearTopLevelControlEvents() { if (this.topLevelControl != null) { this.topLevelControl.ParentChanged -= new EventHandler(this.OnTopLevelPropertyChanged); this.topLevelControl.HandleCreated -= new EventHandler(this.TopLevelCreated); this.topLevelControl.HandleDestroyed -= new EventHandler(this.TopLevelDestroyed); } } /// /// /// Creates the handle for the control. /// ///private void CreateHandle() { if (GetHandleCreated()) { return; } IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate(); try { NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); icc.dwICC = NativeMethods.ICC_TAB_CLASSES; SafeNativeMethods.InitCommonControlsEx(icc); CreateParams cp = CreateParams; // Avoid reentrant call to CreateHandle (VSWhidbey 570764) if (GetHandleCreated()) { return; } window.CreateHandle(cp); } finally { UnsafeNativeMethods.ThemingScope.Deactivate(userCookie); } // If in ownerDraw mode, we don't want the default border. if (ownerDraw) { int style = unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE))); style &= ~NativeMethods.WS_BORDER; UnsafeNativeMethods.SetWindowLong(new HandleRef(this, Handle), NativeMethods.GWL_STYLE, new HandleRef(null, (IntPtr)style)); } // Setting the max width has the added benefit of enabling multiline // tool tips! // UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); Debug.Assert(NativeMethods.TTDT_AUTOMATIC == 0, "TTDT_AUTOMATIC != 0"); if (auto) { SetDelayTime(NativeMethods.TTDT_AUTOMATIC, delayTimes[NativeMethods.TTDT_AUTOMATIC]); delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); } else { for (int i=1; i < delayTimes.Length; i++) { if (delayTimes[i] >= 1) { SetDelayTime(i, delayTimes[i]); } } } // Set active status // UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_ACTIVATE, (active == true) ? 1 : 0, 0); if (BackColor != SystemColors.Info) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(BackColor), 0); } if (ForeColor != SystemColors.ControlText) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(ForeColor), 0); } if (toolTipIcon > 0 || !String.IsNullOrEmpty(toolTipTitle)) { // If the title is null/empty, the icon won't display. string title = !String.IsNullOrEmpty(toolTipTitle) ? toolTipTitle : " "; UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title); } } private void CreateAllRegions() { Control[] ctls = new Control[tools.Keys.Count]; tools.Keys.CopyTo(ctls, 0); for (int i=0; i 0; bool handlesCreated = ctl.IsHandleCreated && TopLevelControl != null && TopLevelControl.IsHandleCreated; if (!created.ContainsKey(ctl) && captionValid && handlesCreated && !DesignMode) { //Call the Sendmessage thru a function.. SetToolInfo(ctl, caption); created[ctl] = ctl; } if (ctl.IsHandleCreated && topLevelControl == null) { // Remove first to purge any duplicates... // ctl.MouseMove -= new MouseEventHandler(this.MouseMove); ctl.MouseMove += new MouseEventHandler(this.MouseMove); } } private void MouseMove(object sender, MouseEventArgs me) { Control ctl = (Control)sender; if (!created.ContainsKey(ctl) && ctl.IsHandleCreated && TopLevelControl != null) { CreateRegion(ctl); } if (created.ContainsKey(ctl)) { ctl.MouseMove -= new MouseEventHandler(this.MouseMove); } } /// /// /// Destroys the handle for this control. /// ////// Required by Label to destroy the handle for the toolTip added for AutoEllipses. internal void DestroyHandle() { if (GetHandleCreated()) { window.DestroyHandle(); } } private void DestroyRegion(Control ctl) { // when the toplevelControl is a form and is Modal, the Handle of the tooltip is releasedbefore we come here. // In such a case the tool wont get deleted from the tooltip. // So we dont check "Handle" in the handlesCreate but check it only foe Non-Nodal dialogs later bool handlesCreated = ctl.IsHandleCreated && topLevelControl != null && topLevelControl.IsHandleCreated && !this.isDisposing; Form topForm = topLevelControl as Form; if (topForm == null || (topForm != null && !topForm.Modal)) { handlesCreated = handlesCreated && GetHandleCreated(); } if (created.ContainsKey(ctl) && handlesCreated && !DesignMode) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_DELTOOL, 0, GetMinTOOLINFO(ctl)); created.Remove(ctl); } } /// /// /// /// protected override void Dispose(bool disposing) { if (disposing) { this.isDisposing = true; try { ClearTopLevelControlEvents(); StopTimer(); // always destroy the handle... // DestroyHandle(); RemoveAll(); window = null; //Unhook the DeactiveEvent... // Lets find the Form for associated Control ... // and hook up to the Deactivated event to Hide the Shown tooltip Form baseFrom = TopLevelControl as Form; if (baseFrom != null) { baseFrom.Deactivate -= new EventHandler(this.BaseFormDeactivate); } } finally { this.isDisposing = false; } } base.Dispose(disposing); } ////// Disposes of the ////// component. /// /// /// Returns the delayTime based on the NativeMethods.TTDT_* values. /// ///private int GetDelayTime(int type) { if (GetHandleCreated()) { return (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_GETDELAYTIME, type, 0); } else { return delayTimes[type]; } } // Can't be a property -- there is another method called GetHandleCreated internal bool GetHandleCreated() { return (window != null ? window.Handle != IntPtr.Zero: false); } /// /// /// 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. /// ///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; } /// /// /// Returns a detailed TOOLINFO_TOOLTIP structure that represents the specified /// region. NOTE: This may force the creation of a handle. /// If the out parameter allocatedString has been set to true, It is the responsibility of the caller /// to free the string buffer referenced by lpszText (using Marshal.FreeHGlobal). /// ///private NativeMethods.TOOLINFO_TOOLTIP GetTOOLINFO(Control ctl, string caption, out bool allocatedString) { allocatedString = false; NativeMethods.TOOLINFO_TOOLTIP ti = GetMinTOOLINFO(ctl); ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP)); ti.uFlags |= NativeMethods.TTF_TRANSPARENT | NativeMethods.TTF_SUBCLASS; // RightToLeft reading order // Control richParent = TopLevelControl; if (richParent != null && richParent.RightToLeft == RightToLeft.Yes && !ctl.IsMirrored) { //Indicates that the ToolTip text will be displayed in the opposite direction //to the text in the parent window. ti.uFlags |= NativeMethods.TTF_RTLREADING; } if (ctl is TreeView || ctl is ListView) { TreeView tv = ctl as TreeView; if (tv != null && tv.ShowNodeToolTips) { ti.lpszText = NativeMethods.InvalidIntPtr; } else { ListView lv = ctl as ListView; if (lv != null && lv.ShowItemToolTips) { ti.lpszText = NativeMethods.InvalidIntPtr; } else { ti.lpszText = Marshal.StringToHGlobalAuto(caption); allocatedString = true; } } } else { ti.lpszText = Marshal.StringToHGlobalAuto(caption); allocatedString = true; } return ti; } 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; } /// /// /// [ DefaultValue(""), Localizable(true), SRDescription(SR.ToolTipToolTipDescr), Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)) ] public string GetToolTip(Control control) { if (control == null) { return String.Empty; } TipInfo tt = (TipInfo)tools[control]; if (tt == null || tt.Caption == null) { return ""; } else { return tt.Caption; } } ////// Retrieves the ///text associated with the specified control. /// /// /// Returns the HWND of the window that is at the specified point. This /// handles special cases where one Control owns multiple HWNDs (i.e. ComboBox). /// ///private IntPtr GetWindowFromPoint(Point screenCoords, ref bool success) { Control baseVar = TopLevelControl; //Special case the ActiveX Controls. if (baseVar != null && baseVar.IsActiveX) { //find the matching HWnd matching the ScreenCoord and find if the Control has a Tooltip. IntPtr hwndControl = UnsafeNativeMethods.WindowFromPoint(screenCoords.X, screenCoords.Y); if (hwndControl != IntPtr.Zero) { Control currentControl = Control.FromHandleInternal(hwndControl); if (currentControl != null && tools != null && tools.ContainsKey(currentControl)) { return hwndControl; } } return IntPtr.Zero; } IntPtr baseHwnd = IntPtr.Zero; if (baseVar != null) { baseHwnd = baseVar.Handle; } IntPtr hwnd = IntPtr.Zero; bool finalMatch = false; while (!finalMatch) { Point pt = screenCoords; if (baseVar != null) { pt = baseVar.PointToClientInternal(screenCoords); } IntPtr found = UnsafeNativeMethods.ChildWindowFromPointEx(new HandleRef(null, baseHwnd), pt.X, pt.Y, NativeMethods.CWP_SKIPINVISIBLE); if (found == baseHwnd) { hwnd = found; finalMatch = true; } else if (found == IntPtr.Zero) { finalMatch = true; } else { baseVar = Control.FromHandleInternal(found); if (baseVar == null) { baseVar = Control.FromChildHandleInternal(found); if (baseVar != null) { hwnd = baseVar.Handle; } finalMatch = true; } else { baseHwnd = baseVar.Handle; } } } if (hwnd != IntPtr.Zero) { Control ctl = Control.FromHandleInternal(hwnd); if (ctl != null) { Control current = ctl; while (current != null && current.Visible) { current = current.ParentInternal; } if (current != null) { hwnd = IntPtr.Zero; } success = true; } } return hwnd; } private void OnTopLevelPropertyChanged(object s, EventArgs e) { ClearTopLevelControlEvents(); this.topLevelControl = null; // We must re-aquire this control. If the existing top level control's handle // was never created, but the new parent has a handle, if we don't re-get // the top level control here we won't ever create the tooltip handle. // this.topLevelControl = TopLevelControl; } /// /// /// ///private void RecreateHandle() { if (!DesignMode) { if (GetHandleCreated()) { DestroyHandle(); } created.Clear(); CreateHandle(); CreateAllRegions(); } } /// /// /// public void RemoveAll() { Control[] regions = new Control[tools.Keys.Count]; tools.Keys.CopyTo(regions, 0); for (int i=0; i/// Removes all of the tooltips currently associated /// with the ///control. /// /// /// Sets the delayTime based on the NativeMethods.TTDT_* values. /// ///private void SetDelayTime(int type, int time) { if (type == NativeMethods.TTDT_AUTOMATIC) { auto = true; } else { auto = false; } delayTimes[type] = time; if (GetHandleCreated() && time >= 0) { UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TTM_SETDELAYTIME, type, time); // Update everyone else if automatic is set... we need to do this // to preserve value in case of handle recreation. // if (auto) { delayTimes[NativeMethods.TTDT_AUTOPOP] = GetDelayTime(NativeMethods.TTDT_AUTOPOP); delayTimes[NativeMethods.TTDT_INITIAL] = GetDelayTime(NativeMethods.TTDT_INITIAL); delayTimes[NativeMethods.TTDT_RESHOW] = GetDelayTime(NativeMethods.TTDT_RESHOW); } } else if (auto) { AdjustBaseFromAuto(); } } /// /// /// public void SetToolTip(Control control, string caption) { TipInfo info = new TipInfo(caption, TipInfo.Type.Auto); SetToolTipInternal(control, info); } ////// Associates ///text with the specified control. /// /// /// /// Associates /// /// Returns true if the AutomaticDelay property should be persisted. /// ///private bool ShouldSerializeAutomaticDelay() { if (auto) { if (AutomaticDelay != DEFAULT_DELAY) { return true; } } return false; } /// /// /// Returns true if the AutoPopDelay property should be persisted. /// ///private bool ShouldSerializeAutoPopDelay() { return !auto; } /// /// /// Returns true if the InitialDelay property should be persisted. /// ///private bool ShouldSerializeInitialDelay() { return !auto; } /// /// /// Returns true if the ReshowDelay property should be persisted. /// ///private bool ShouldSerializeReshowDelay() { return !auto; } /// /// /// Shows a tooltip for specified text, window, and hotspot /// ///private void ShowTooltip(string text, IWin32Window win, int duration) { if (win == null) { throw new ArgumentNullException("win"); } Control associatedControl = win as Control; if (associatedControl != null) { NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(associatedControl, associatedControl.Handle), ref r); Cursor currentCursor = Cursor.CurrentInternal; Point cursorLocation = Cursor.Position; Point p = cursorLocation; Screen screen = Screen.FromPoint(cursorLocation); // Place the tool tip on the associated control if its not already there if ( cursorLocation.X < r.left || cursorLocation.X > r.right || cursorLocation.Y < r.top || cursorLocation.Y > r.bottom ) { // calculate the dimensions of the visible rectangle which // is used to estimate the upper x,y of the tooltip placement NativeMethods.RECT visibleRect = new NativeMethods.RECT(); visibleRect.left = (r.left < screen.WorkingArea.Left) ? screen.WorkingArea.Left:r.left; visibleRect.top = (r.top < screen.WorkingArea.Top) ? screen.WorkingArea.Top:r.top; visibleRect.right = (r.right > screen.WorkingArea.Right) ? screen.WorkingArea.Right:r.right; visibleRect.bottom = (r.bottom > screen.WorkingArea.Bottom) ? screen.WorkingArea.Bottom:r.bottom; p.X = visibleRect.left + (visibleRect.right - visibleRect.left)/2; p.Y = visibleRect.top + (visibleRect.bottom - visibleRect.top)/2; associatedControl.PointToClientInternal(p); SetTrackPosition(p.X, p.Y); SetTool(win, text, TipInfo.Type.SemiAbsolute, p); if (duration > 0) { StartTimer(window, duration); } } else { TipInfo tt = (TipInfo)tools[associatedControl]; if (tt == null) { tt = new TipInfo(text, TipInfo.Type.SemiAbsolute); } else { tt.TipType |= TipInfo.Type.SemiAbsolute; tt.Caption = text; } tt.Position = p; if (duration > 0) { if (this.originalPopupDelay == 0) { this.originalPopupDelay = AutoPopDelay; } AutoPopDelay = duration; } SetToolTipInternal(associatedControl, tt); } } } /// /// /// public void Show(string text, IWin32Window window) { //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 // Check if the foreground window is the TopLevelWindow if (HasAllWindowsPermission && IsWindowActive(window)) { ShowTooltip(text, window, 0); } } ////// Associates ///with the specified control and displays it. /// /// /// public void Show(string text, IWin32Window window, int duration) { if (duration < 0) { throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission && IsWindowActive(window)) { ShowTooltip(text, window, duration); } } ////// Associates ///with the specified control /// and displays it for the specified duration. /// /// /// public void Show(string text, IWin32Window window, Point point) { if (window == null) { throw new ArgumentNullException("window"); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission && IsWindowActive(window)) { //Set The ToolTips... NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); int pointX = r.left + point.X; int pointY = r.top + point.Y; SetTrackPosition(pointX, pointY); SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); } } ////// Associates ///with the specified control and displays it. /// /// /// public void Show(string text, IWin32Window window, Point point, int duration) { if (window == null) { throw new ArgumentNullException("window"); } if (duration < 0) { throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission && IsWindowActive(window)) { //Set The ToolTips... NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); int pointX = r.left + point.X; int pointY = r.top + point.Y; SetTrackPosition(pointX, pointY); SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); StartTimer(window, duration); } } ////// Associates ///with the specified control and displays it. /// /// /// public void Show(string text, IWin32Window window, int x, int y) { if (window == null) { throw new ArgumentNullException("window"); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission && IsWindowActive(window)) { NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); int pointX = r.left + x; int pointY = r.top + y; SetTrackPosition(pointX, pointY); SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); } } ////// Associates ///with the specified control and displays it. /// /// /// public void Show(string text, IWin32Window window, int x, int y, int duration) { if (window == null) { throw new ArgumentNullException("window"); } if (duration < 0) { throw new ArgumentOutOfRangeException("duration", SR.GetString(SR.InvalidLowBoundArgumentEx, "duration", (duration).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission && IsWindowActive(window)) { NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetWindowRect(new HandleRef(window, Control.GetSafeHandle(window)), ref r); int pointX = r.left + x; int pointY = r.top + y; SetTrackPosition(pointX, pointY); SetTool(window, text, TipInfo.Type.Absolute, new Point(pointX, pointY)); StartTimer(window, duration); } } ////// Associates ///with the specified control and displays it. /// /// Private Function to encapsulate TTM_TRACKPOSITION so that this doesnt fire an extra POP event /// private void SetTrackPosition(int pointX, int pointY) { try { trackPosition = true; UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKPOSITION, 0, NativeMethods.Util.MAKELONG(pointX, pointY)); } finally { trackPosition= false; } } ////// /// public void Hide(IWin32Window win) { if (win == null) { throw new ArgumentNullException("win"); } //SecReview: We should not allow Semi-trust apps to call Show on a tooltip. Refer VsWhidbey : 439847 if (HasAllWindowsPermission) { if (window == null) { return; } // VSWhidbey 562045: Only send message if we actually have a ToolTip at this point. if (GetHandleCreated()) { IntPtr hWnd = Control.GetSafeHandle(win); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_TRACKACTIVATE, 0, GetWinTOOLINFO(hWnd)); UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TTM_DELTOOL, 0, GetWinTOOLINFO(hWnd)); } StopTimer(); //Check if the passed in IWin32Window is a Control... Control tool = win as Control; if (tool == null) { owners.Remove(win.Handle); } else { if (tools.ContainsKey(tool)) { SetToolInfo(tool, GetToolTip(tool)); } else { owners.Remove(win.Handle); } // 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); } } // VSWhidbey 562045 - clear off the toplevel control. ClearTopLevelControlEvents(); topLevelControl = null; } } private void BaseFormDeactivate(System.Object sender, System.EventArgs e){ HideAllToolTips(); } private void HideAllToolTips() { Control[] ctls = new Control[owners.Values.Count]; owners.Values.CopyTo(ctls, 0); for (int i=0; i/// Hides ///with the specified control. /// /// /// Starts the timer hiding Positioned ToolTips /// private void StartTimer(IWin32Window owner, int interval) { if (timer == null) { timer = new ToolTipTimer(owner); // Add the timer handler timer.Tick += new EventHandler(TimerHandler); } timer.Interval = interval; timer.Start(); } ////// /// Stops the timer for hiding Positioned ToolTips /// protected void StopTimer() { //VSWhidbey 363538: hold a local ref to timer //so that a posted message doesn't null this //out during disposal. ToolTipTimer timerRef = timer; if (timerRef != null) { timerRef.Stop(); timerRef.Dispose(); timer = null; } } ////// /// Generates updown events when the timer calls this function. /// private void TimerHandler(object source, EventArgs args) { Hide(((ToolTipTimer)source).Host); } ////// /// ~ToolTip() { DestroyHandle(); } ////// Finalizes garbage collection. /// ////// /// /// public override string ToString() { string s = base.ToString(); return s + " InitialDelay: " + InitialDelay.ToString(CultureInfo.CurrentCulture) + ", ShowAlways: " + ShowAlways.ToString(CultureInfo.CurrentCulture); } private void Reposition(Point tipPosition, Size tipSize) { Point moveToLocation = tipPosition; Screen screen = Screen.FromPoint(moveToLocation); // Re-adjust the X position of the tool tip if it bleeds off the screen working area if (moveToLocation.X + tipSize.Width > screen.WorkingArea.Right) { moveToLocation.X = screen.WorkingArea.Right - tipSize.Width; } // re-adjust the Y position of the tool tip if it bleeds off the screen working area. if (moveToLocation.Y + tipSize.Height> screen.WorkingArea.Bottom) { moveToLocation.Y = screen.WorkingArea.Bottom - tipSize.Height; } SafeNativeMethods.SetWindowPos(new HandleRef(this, this.Handle), NativeMethods.HWND_TOPMOST, moveToLocation.X, moveToLocation.Y, tipSize.Width, tipSize.Height, NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOOWNERZORDER); } ////// Returns a string representation for this control. /// ////// /// Handles the WM_MOVE message. /// ///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); } } } /// /// /// Handles the WM_MOUSEACTIVATE message. /// ///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; } } } /// /// /// Handles the WM_WINDOWFROMPOINT message. /// ///private void WmWindowFromPoint(ref Message msg) { NativeMethods.POINT sc = (NativeMethods.POINT)msg.GetLParam(typeof(NativeMethods.POINT)); Point screenCoords = new Point(sc.x, sc.y); bool result = false; msg.Result = GetWindowFromPoint(screenCoords, ref result); } /// /// /// Handles the TTN_SHOW message. /// ///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); } } } /// /// /// Handles the WM_WINDOWPOSCHANGED message. /// We need to Hide the window since the native tooltip actually calls SetWindowPos in its TTN_SHOW even if we cancel showing the /// tooltip : Hence we need to listen to the WindowPosChanged message can hide the window ourselves. /// Refer to VsWhidbey : 490044 for more details. /// ///private bool WmWindowPosChanged() { if (cancelled) { SafeNativeMethods.ShowWindow(new HandleRef(this, Handle), NativeMethods.SW_HIDE); return true; } return false; } /// /// /// Handles the WM_WINDOWPOSCHANGING message. /// ///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; } /// /// /// Called just before the tooltip is hidden /// ///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; } } } /// /// /// WNDPROC /// ///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); 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 = (int) 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(); } try { OnDraw(new DrawToolTipEventArgs(g, win, ac, bounds, GetToolTip(ac), BackColor, ForeColor, font)); } finally { g.Dispose(); UnsafeNativeMethods.EndPaint(new HandleRef(this,Handle),ref ps); } break; } } //If not OwnerDraw, fall through goto default; default: window.DefWndProc(ref msg); break; } } /// /// /// ///private class ToolTipNativeWindow : NativeWindow { ToolTip control; internal ToolTipNativeWindow(ToolTip control) { this.control = control; } protected override void WndProc(ref Message m) { if (control != null) { control.WndProc(ref m); } } } private class ToolTipTimer : Timer { IWin32Window host; public ToolTipTimer(IWin32Window owner) : base(){ this.host = owner; } public IWin32Window Host { get { return host; } } } private class TipInfo { [Flags] public enum Type { None = 0x0000, Auto = 0x0001, Absolute = 0x0002, SemiAbsolute = 0x0004 }; public Type TipType = Type.Auto; private string caption; private string designerText; public Point Position = Point.Empty; public TipInfo(string caption, Type type) { this.caption = caption; this.TipType = type; if (type == Type.Auto) { this.designerText = caption; } } public string Caption { get { return ((this.TipType & (Type.Absolute | Type.SemiAbsolute)) != 0) ? caption:designerText; } set { this.caption = value; } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlNodeReader.cs
- UpdatePanelControlTrigger.cs
- ContentFileHelper.cs
- coordinatorscratchpad.cs
- FileDialog_Vista_Interop.cs
- Profiler.cs
- RadioButton.cs
- Vector3DConverter.cs
- ArrayConverter.cs
- MethodBuilder.cs
- Int32Collection.cs
- xsdvalidator.cs
- ItemsControl.cs
- DeploymentSection.cs
- _NTAuthentication.cs
- BamlBinaryWriter.cs
- EntitySetBase.cs
- BezierSegment.cs
- MarkupExtensionReturnTypeAttribute.cs
- Material.cs
- SettingsSection.cs
- PropertyContainer.cs
- XsdBuildProvider.cs
- XmlSchemaValidationException.cs
- _OSSOCK.cs
- GlyphInfoList.cs
- XPathExpr.cs
- WizardStepCollectionEditor.cs
- DataRowComparer.cs
- ImmComposition.cs
- ToolStripItemBehavior.cs
- InternalResources.cs
- CommandLibraryHelper.cs
- SchemaAttDef.cs
- DataTrigger.cs
- ProcessModelSection.cs
- PixelShader.cs
- NullRuntimeConfig.cs
- DataGridHeaderBorder.cs
- WindowsListBox.cs
- RangeValidator.cs
- DocumentGridContextMenu.cs
- WindowsGrip.cs
- ValidationHelper.cs
- VBCodeProvider.cs
- DataGridViewLinkCell.cs
- SerialPort.cs
- SystemWebSectionGroup.cs
- HelpInfo.cs
- OledbConnectionStringbuilder.cs
- MetaData.cs
- CultureSpecificStringDictionary.cs
- TreeViewHitTestInfo.cs
- FormatVersion.cs
- ErrorReporting.cs
- FirstMatchCodeGroup.cs
- NativeMethods.cs
- MinimizableAttributeTypeConverter.cs
- PageAsyncTask.cs
- FontEditor.cs
- RangeValueProviderWrapper.cs
- DrawingAttributesDefaultValueFactory.cs
- FragmentQuery.cs
- DataException.cs
- DataGridViewCellCancelEventArgs.cs
- TextDecorationLocationValidation.cs
- DataServiceContext.cs
- XmlQualifiedName.cs
- AesCryptoServiceProvider.cs
- DataGridViewTextBoxEditingControl.cs
- MessageDescription.cs
- PriorityItem.cs
- RelatedPropertyManager.cs
- TableRowCollection.cs
- TranslateTransform.cs
- TargetInvocationException.cs
- InertiaTranslationBehavior.cs
- Group.cs
- MenuAutomationPeer.cs
- ErrorWebPart.cs
- WindowsRebar.cs
- LicenseContext.cs
- PKCS1MaskGenerationMethod.cs
- CLSCompliantAttribute.cs
- SystemUnicastIPAddressInformation.cs
- CodeStatement.cs
- MasterPageCodeDomTreeGenerator.cs
- GradientStop.cs
- TransactionManager.cs
- CacheAxisQuery.cs
- BitmapScalingModeValidation.cs
- XhtmlTextWriter.cs
- safemediahandle.cs
- ToolStripPanelRow.cs
- SimpleBitVector32.cs
- CreateUserWizard.cs
- shaper.cs
- ScrollBarRenderer.cs
- SortedList.cs
- ExtensionsSection.cs