Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Balloon ToolTip Control

, 8 Dec 2005
A balloon tooltip control implementing the IExtender interface.
balloontooltipsample.zip
ToolTipTest.exe
CustomTooltip.dll
balloontooltipsource.zip
BalloonToolTip Source
CustomTooltip
bin
CustomTooltip.csproj.user
ToolTipTest
App.ico
bin
ToolTipTest.csproj.user
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace CustomTooltip
{


	[ProvideProperty("BalloonText", typeof(Control))]
	public class BalloonToolTip : System.ComponentModel.Component , IExtenderProvider
	{
		[DllImport("user32.dll")]
		private static extern IntPtr CreateWindowEx(int exstyle, string classname, string windowtitle, int style, int x, int y, int width, int height, IntPtr parent, int menu,
			int nullvalue, int nullptr);

		[DllImport("user32.dll")]
		private static extern int DestroyWindow(IntPtr hwnd);

		[DllImport("User32.dll")]
		private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

		[DllImport("User32.dll")]
		private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);

		private struct toolinfo
		{
			public int size;
			public int flag;
			public IntPtr parent;
			public int id;
			public Rectangle rect;
			public int nullvalue;
			[MarshalAs(UnmanagedType.LPTStr)]
			public string text;
			public int param;
		}
		
		// Used by some constants values, not by the code.
		private const int WM_USER = 0x0400;
		
		private const int TTM_ADDTOOL				= WM_USER + 50;		
		private const int TTM_DELTOOL				= WM_USER + 51;		
		private const int TTM_ACTIVATE				= WM_USER + 1;		
		private const int TTM_SETMAXTIPWIDTH		= WM_USER + 24;	
		private const int TTM_SETTITLE				= WM_USER + 33;
		private const int TTM_SETDELAYTIME			= WM_USER + 3;
		private const int TTM_UPDATETIPTEXT			= WM_USER + 57;
		private const int TTM_SETTIPBKCOLOR			= WM_USER + 19;
		private const int TTM_SETTIPTEXTCOLOR		= WM_USER + 20;
		private const int TTM_GETTOOLINFO			= WM_USER + 53;
		private const int TTM_SETTOOLINFO			= WM_USER + 54;

		private const int TTS_ALWAYSTIP				= 0x01;
		private const int TTS_NOPREFIX				= 0x02;
		private const int TTS_BALLOON				= 0x40;

		private const int TTF_SUBCLASS				= 0x0010;
		private const int TTF_TRANSPARENT			= 0x0100;

		private const int SWP_NOSIZE				= 0x0001;
		private const int SWP_NOMOVE				= 0x0002;
		private const int SWP_NOACTIVATE			= 0x0010;
		
		private const int TTDT_AUTOPOP		       = 2;
		private const int TTDT_INITIAL		       = 3;
		
		private const int WS_POPUP					= unchecked((int)0x80000000);
		private const int CW_USEDEFAULT				= unchecked((int)0x80000000);
		
		private IntPtr TOPMOST						= new IntPtr(-1);

		
		public enum BalloonIcons: int
		{
			None	= 0,
			Info	= 1,
			Warning	= 2,
			Error	= 3
		}

		private int max;
		private int autopop;
		private int initial;
		private bool enabled;
		private string title;
		private toolinfo tf;
		private System.Collections.Hashtable tooltexts;
		private BalloonIcons icon;
		private IntPtr toolwindow;
		private IntPtr tempptr;
		private Color bgcolor;
		private Color fgcolor;

		public BalloonToolTip()
		{
			// Private members initial values.
			max			= 200;
			autopop		= 5000;
			initial		= 500;
			title		= string.Empty;
			bgcolor		= Color.FromKnownColor(KnownColor.Info);
			fgcolor		= Color.FromKnownColor(KnownColor.InfoText);
			tooltexts	= new System.Collections.Hashtable();
			enabled		= true;
			icon		= BalloonIcons.None;

			// Creating the tooltip control.
			toolwindow = CreateWindowEx(0, "tooltips_class32", string.Empty, WS_POPUP | TTS_BALLOON | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, IntPtr.Zero, 0, 0, 0);
			SetWindowPos(toolwindow, TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
			SendMessage(toolwindow, TTM_SETMAXTIPWIDTH, 0, new IntPtr(max));
			
			// Creating the toolinfo structure to be used later.
			tf = new toolinfo();
			tf.flag = TTF_SUBCLASS | TTF_TRANSPARENT;
			tf.size = Marshal.SizeOf(typeof(toolinfo));
		}

		~BalloonToolTip()
		{
			Dispose(false);
		}

		// Extend any control except itself and the form, this function get called for use automaticly by the designer.
		public bool CanExtend(object extendee)
		{
			if( extendee is Control && !(extendee is BalloonToolTip) && !(extendee is Form))
			{
				return true;
			}

			return false;
		}

		// This is not a regular funtion, its our extender property seprated as two functions for get and set.
		public string GetBalloonText(Control parent)
		{
			if(tooltexts.Contains(parent))
			{
				return tooltexts[parent].ToString();
			}
			else
			{
				return null;
			}
		}

		// This is where the tool text validated and updated for the controls.
		public void SetBalloonText(Control parent, string value)
		{
			if(value == null)
			{
				value = string.Empty;
			}
			// If the tool text have been cleared, remove the control from our service list.
			if(value == string.Empty)
			{
				tooltexts.Remove(parent);

				tf.parent = parent.Handle;

				tempptr = Marshal.AllocHGlobal(tf.size);
				Marshal.StructureToPtr(tf, tempptr, false);
				SendMessage(toolwindow, TTM_DELTOOL, 0, tempptr);
				Marshal.FreeHGlobal(tempptr);
				parent.Resize -= new EventHandler(Control_Resize);

			}
			else
			{
				tf.parent = parent.Handle;
				tf.rect = parent.ClientRectangle;
				tf.text = value;
				tempptr = Marshal.AllocHGlobal(tf.size);
				Marshal.StructureToPtr(tf, tempptr, false);
				
				if(tooltexts.Contains(parent))
				{
					tooltexts[parent] = value;
					SendMessage(toolwindow, TTM_UPDATETIPTEXT, 0, tempptr);
				}
				else
				{
					tooltexts.Add(parent, value);
					SendMessage(toolwindow, TTM_ADDTOOL, 0, tempptr);
					parent.Resize += new EventHandler(Control_Resize);
				}
				
				Marshal.FreeHGlobal(tempptr);

			}
		}

		[DefaultValue(BalloonIcons.None)]
		public BalloonIcons Icon
		{
			get
			{
				return icon;
			}
			set
			{
				icon = value;
				Title = title;
			}
		}
		
		[DefaultValue(200)]
		public int MaximumWidth
		{
			get
			{
				return max;
			}
			set
			{
				// Refuse any strange values, (feel free to modify).
				if(max >= 100 && max <= 2000)
				{
					max = value;
					SendMessage(toolwindow, TTM_SETMAXTIPWIDTH, 0, new IntPtr(max));
				}
			}
		}

		[DefaultValue(true)]
		public bool Enabled
		{
			get
			{
				return enabled;
			}
			set
			{
				enabled = value;
				SendMessage(toolwindow, TTM_ACTIVATE, Convert.ToInt32(enabled), new IntPtr(0));
			}
		}

		public string Title
		{
			get
			{
				return title;
			}
			set
			{
				title = value;
				tempptr = Marshal.StringToHGlobalUni(title);
				SendMessage(toolwindow, TTM_SETTITLE, (int) icon, tempptr);
				Marshal.FreeHGlobal(tempptr);
			}
		}

		// This property is by seconds.
		[DefaultValue(5)]
		public int AutoPop
		{
			get
			{
				return autopop / 1000;
			}
			set
			{
				// Refuse any strange values, (feel free to modify).
				if(value >= 1 && value < 120)
				{
					autopop = value * 1000;
					SendMessage(toolwindow, TTM_SETDELAYTIME, TTDT_AUTOPOP, new IntPtr(autopop));
				}
			}
		}

		// This property is by milliseconds ( 1 second = 1000 millisecond ).
		[DefaultValue(500)]
		public int Initial
		{
			get
			{
				return initial ;
			}
			set
			{
				// Refuse any strange values, (feel free to modify).
				if(value >= 100 && value <= 2000)
				{
					initial = value ;
					SendMessage(toolwindow, TTM_SETDELAYTIME, TTDT_INITIAL, new IntPtr(initial));
				}
			}
		}
		public Color BackColor
		{
			get
			{
				return bgcolor;
			}
			set
			{
				bgcolor = value;
				SendMessage(toolwindow, TTM_SETTIPBKCOLOR, System.Drawing.ColorTranslator.ToWin32(value), new IntPtr(0));
			}
		}
		public Color ForeColor
		{
			get
			{
				return fgcolor;
			}
			set
			{
				fgcolor = value;
				SendMessage(toolwindow, TTM_SETTIPTEXTCOLOR, System.Drawing.ColorTranslator.ToWin32(value), new IntPtr(0));
			}
		}

		// Overriding Dispose is a must to free our window handle we created at the constructor.
		protected override void Dispose(bool disposing)
		{
			if(disposing)
			{
				tooltexts.Clear();
				tooltexts = null;
			}
			DestroyWindow(toolwindow);		// Free the window handle obtained by CreateWindowEx.
			base.Dispose (disposing);

		}

		private void Control_Resize(object sender, EventArgs e)
		{
			tf.parent =((Control)sender).Handle;

			tempptr = Marshal.AllocHGlobal(tf.size);
			Marshal.StructureToPtr(tf, tempptr, false);

			SendMessage(toolwindow, TTM_GETTOOLINFO, 0, tempptr);

			tf = (toolinfo) Marshal.PtrToStructure(tempptr, typeof(toolinfo));
			tf.rect = ((Control)sender).ClientRectangle;

			Marshal.StructureToPtr(tf, tempptr, false);

			SendMessage(toolwindow, TTM_SETTOOLINFO, 0, tempptr);
            
			Marshal.FreeHGlobal(tempptr);
		}
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Noor Abuhamdeh
Team Leader
Jordan Jordan
NOOR ABUHAMDEH
Working as a Technical Team Leader in a software solutions company in Jordan, interested in .net technology, C++

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 8 Dec 2005
Article Copyright 2005 by Noor Abuhamdeh
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid