Click here to Skip to main content
15,887,585 members
Articles / Programming Languages / C#
Article

NotifyWindow: A different MSN Messenger style notification window

Rate me:
Please Sign up or sign in to vote.
4.85/5 (48 votes)
2 Aug 2004Public Domain2 min read 462.4K   10.2K   213   112
Another MSN Messenger-like notification window, this one does its own drawing

Introduction

NotifyWindow displays an MSN Messenger-like notification window. If you want to display your own images in the notification window, you may prefer to use John O'Byrne's TaskbarNotifer. NotifyWindow may be easier to use if you just intend to display text. Animations are used in opening and closing the NotifyWindow, and the window is displayed TopMost while not stealing focus. The window will be shown for a default of 11 seconds, although this can be changed. NotifyWindow does all of its own drawing, so no extra image files are required.

Using the code

It is pretty simple to display text using NotifyWindow.

C#
// Display the text "This is a sample NotifyWindow"
NotifyWindow nw = new NotifyWindow ("This is a sample NotifyWindow");
nw.Notify();

// The following two lines of code will display a window that 
// looks exactly like the one shown at the beginning of this article.
NotifyWindow nw = new NotifyWindow ("NotifyWindow", 
  "This is a sample notification created with NotifyWindow");
nw.Notify();

If desired, a variety of other options can be changed - such as fonts and colors. The included TestNotifyWindow application will let you play around with a few of the settings, but the code displayed here should serve as a more complete reference.

C#
NotifyWindow nw = new NotifyWindow();

nw.Text = "This is the NotifyWindow text";
nw.Title = "Title Text";

// Change the background style.  Other valid 
// styles are Solid, VerticalGradient,
// HorizontalGradient and BackwardDiagonalGradient  
// (Default: VerticalGradient)
nw.BackgroundStyle = NotifyWindow.BackgroundStyles.ForwardDiagonalGradient;

// Change the background colors  
// (Default: BackColor=SteelBlue, GradientColor=WhiteSmoke)
nw.BackColor = Color.SpringGreen;
nw.GradientColor = Color.White;

// Change the text and title colors.  (Default: ControlText)
nw.TextColor = Color.Blue;
nw.TitleColor = Color.Black;

// Change the color displayed when the text is pressed.  (Default: Gray)
nw.PressedColor = Color.Red;

// Use non-default fonts.  If TitleFont is not set 
// by the user, nw.Font will be used.
nw.Font = new Font ("Tahoma", 8.25F);
nw.TitleFont = new Font ("Tahoma", 8.25F, FontStyle.Bold);

// Change NotifyWindow size.  (Default: 130, 110)
nw.SetDimensions (nwWidth, nwHeight);

// Do not close the NotifyWindow if the mouse 
// cursor is over the window.  (Default: true)
nw.WaitOnMouseOver = true;

// Set up an EventHandler to be called if the text or title are clicked.
nw.TextClicked += new System.EventHandler (nwTextClicked);
nw.TitleClicked += new System.EventHandler (nwTitleClicked);

// Display the window for 20 seconds, or 20000ms.  (Default: 11000ms)
nw.WaitTime = 20000;

// Now show the NotifyWindow.
nw.Notify();

This is how the NotifyWindow created with the above code will look:

Programmers can also use their own Blend (for the background) or StringFormat (which will be used when Text and Title are drawn) if desired by setting the nw.Blend and nw.StringFormat variables.

Points of Interest

NotifyWindow is unique because it does all of its own drawing. The background is drawn using Graphics.FillRectangle with either a LinearGradientBrush (default) or a SolidBrush. The borders are drawn using a series of calls to Graphics.DrawRectangle and Graphics.DrawLine. On Windows XP or higher systems with Visual Styles enabled, the close button is drawn using DrawThemeBackground() from UxTheme.dll - otherwise, ControlPaint.DrawCaptionButton is used.

An obstacle faced in both this and similar applications has been displaying the window on top without stealing focus. Both Form.Show() and setting TopMost = true individually activate the form, which steals focus. We get around this by calling ShowWindow() and SetWindowPos() with arguments instructing the operating system not to activate the window.

C#
const Int32 HWND_TOPMOST = -1;
const Int32 SWP_NOACTIVATE = 0x0010;
const Int32 SW_SHOWNOACTIVATE = 4;
[DllImport ("user32.dll")]
protected static extern bool ShowWindow (IntPtr hWnd, Int32 flags);
[DllImport ("user32.dll")]
protected static extern bool SetWindowPos (IntPtr hWnd, 
  Int32 hWndInsertAfter, Int32 X, Int32 Y, Int32 cx, Int32 cy, uint uFlags);

...

// Show the window without activating it.
ShowWindow (this.Handle, SW_SHOWNOACTIVATE);

// Equivalent to setting TopMost = true, except don't activate the window.
SetWindowPos (this.Handle, HWND_TOPMOST, Left, Top, Width, Height, SWP_NOACTIVATE);

A similar NotifyWindow class was originally implemented for an open-source project called ChronosXP. When it became apparant that others might like to use this code outside of that project, it was modified, removing the ChronosXP-specific parts and making it more generic.

A class called NotifyWindow2000 is included with the distribution that will display the NotifyWindow indefinitely until there is mouse or keyboard activity, similar to balloon windows. It uses SetWindowsHookEx() with WH_KEYBOARD_LL/WH_MOUSE_LL to detect user activity, so it will only work with Windows 2000 or higher. If anyone knows how to do this on older versions of Windows I would like to hear about it.

History

  • Initial coding: July 28, 2004

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Architect Onestop Internet
United States United States
Web architect and PM, in the E-commerce space.

Comments and Discussions

 
AnswerRe: Using notify window in a ThreadFunction Pin
Robert Misiak17-May-06 3:19
professionalRobert Misiak17-May-06 3:19 
GeneralRe: Using notify window in a ThreadFunction Pin
jis17-May-06 18:41
jis17-May-06 18:41 
GeneralRe: Using notify window in a ThreadFunction Pin
Manjit Sooch20-Jul-06 13:33
Manjit Sooch20-Jul-06 13:33 
GeneralRe: Using notify window in a ThreadFunction Pin
shane198527-Sep-07 9:21
shane198527-Sep-07 9:21 
GeneralRe: Using notify window in a ThreadFunction Pin
lxiongh3-Sep-09 4:03
lxiongh3-Sep-09 4:03 
GeneralRe: Using notify window in a ThreadFunction Pin
LeoLB12-Jun-13 8:10
LeoLB12-Jun-13 8:10 
GeneralRe: Using notify window in a ThreadFunction Pin
EinsteinXXL24-Apr-15 10:43
EinsteinXXL24-Apr-15 10:43 
GeneralStacking windows code Pin
jdmwood29-Mar-06 0:13
jdmwood29-Mar-06 0:13 
Hi there,

Nice code! Thanks very much. I have just amended it to allow stacking of messages above each other (i.e. upwards towards the top of the screen) like Messenger does

Apologies for the cut n' paste (not sure how else to send it.)

John Wood

// NotifyWindow.cs<br />
// Copyright © 2004 by Robert Misiak <rmisiak@users.sourceforge.net><br />
// All Rights Reserved.<br />
//<br />
// Permission is granted to use, modify and distribute this code, as long as credit is given to the original author, and the copyright notice<br />
// is retained.<br />
//<br />
// Based on a similar implementation used in ChronosXP, an open-source project:  http://chronosxp.sourceforge.net<br />
//<br />
// JW 29/03/2006: Added code to allow multiple windows to show in a stacked layout, each <br />
//                higher up than the other so they don't overlay each other anymore. This is <br />
//                how MSN Messenger shows them. <br />
//<br />
//                Also there is a parameter to prevent ignore calls if there are already<br />
//                lots of windows being shown. <br />
<br />
<br />
using System;<br />
using System.Drawing;<br />
using System.Drawing.Drawing2D;<br />
using System.Reflection;<br />
using System.Windows.Forms;<br />
using System.Runtime.InteropServices;<br />
<br />
namespace NotifyWindow<br />
{<br />
	/// <summary><br />
	/// Display An MSN-Messenger-Style NotifyWindow.<br />
	/// </summary><br />
	public class NotifyWindow : System.Windows.Forms.Form<br />
	{<br />
		<br />
		//Used to keep track of which windows are active<br />
		//This stores NotifyWindow objects.<br />
		static System.Collections.ArrayList activeWindows = new System.Collections.ArrayList();<br />
<br />
		//JW: The max number of windows to display at once<br />
		public int MaxActiveWindows= 6; //6 is about right for my screen<br />
<br />
		#region Public Variables<br />
		/// <summary><br />
		/// Gets or sets the title text to be displayed in the NotifyWindow.<br />
		/// </summary><br />
		public string Title;<br />
		/// <summary><br />
		/// Gets or sets the Font used for the title text.<br />
		/// </summary><br />
		public System.Drawing.Font TitleFont;<br />
		/// <summary><br />
		/// Gets or sets the Font used when the mouse hovers over the main body of text.<br />
		/// </summary><br />
		public System.Drawing.Font HoverFont;<br />
		/// <summary><br />
		/// Gets or sets the Font used when the mouse hovers over the title text.<br />
		/// </summary><br />
		public System.Drawing.Font TitleHoverFont;<br />
		/// <summary><br />
		/// Gets or sets the style used when drawing the background of the NotifyWindow.<br />
		/// </summary><br />
		public BackgroundStyles BackgroundStyle;<br />
		/// <summary><br />
		/// Gets or sets the Blend used when drawing a gradient background for the NotifyWindow.<br />
		/// </summary><br />
		public System.Drawing.Drawing2D.Blend Blend;<br />
		/// <summary><br />
		/// Gets or sets the StringFormat used when drawing text in the NotifyWindow.<br />
		/// </summary><br />
		public System.Drawing.StringFormat StringFormat;<br />
		/// <summary><br />
		/// Gets or sets a value specifiying whether or not the window should continue to be displayed if the mouse cursor is inside the bounds<br />
		/// of the NotifyWindow.<br />
		/// </summary><br />
		public bool WaitOnMouseOver;<br />
		/// <summary><br />
		/// An EventHandler called when the NotifyWindow main text is clicked.<br />
		/// </summary><br />
		public event System.EventHandler TextClicked;<br />
		/// <summary><br />
		/// An EventHandler called when the NotifyWindow title text is clicked.<br />
		/// </summary><br />
		public event System.EventHandler TitleClicked;<br />
		/// <summary><br />
		/// Gets or sets the color of the title text.<br />
		/// </summary><br />
		public System.Drawing.Color TitleColor;<br />
		/// <summary><br />
		/// Gets or sets the color of the NotifyWindow main text.<br />
		/// </summary><br />
		public System.Drawing.Color TextColor;<br />
		/// <summary><br />
		/// Gets or sets the gradient color which will be blended in drawing the background.<br />
		/// </summary><br />
		public System.Drawing.Color GradientColor;<br />
		/// <summary><br />
		/// Gets or sets the color of text when the user clicks on it.<br />
		/// </summary><br />
		public System.Drawing.Color PressedColor;<br />
		/// <summary><br />
		/// Gets or sets the amount of milliseconds to display the NotifyWindow for.<br />
		/// </summary><br />
		public int WaitTime;<br />
		/// <summary><br />
		/// Gets or sets the full height of the NotifyWindow, used after the opening animation has been completed.<br />
		/// </summary><br />
		public int ActualHeight;<br />
		/// <summary><br />
		/// Gets or sets the full width of the NotifyWindow.<br />
		/// </summary><br />
		public int ActualWidth;<br />
<br />
		public enum BackgroundStyles { BackwardDiagonalGradient, ForwardDiagonalGradient, HorizontalGradient, VerticalGradient, Solid };<br />
		public enum ClockStates { Opening, Closing, Showing, None };<br />
		public ClockStates ClockState;<br />
		#endregion<br />
<br />
		#region Protected Variables<br />
		protected bool closePressed = false, textPressed = false, titlePressed = false, closeHot = false, textHot = false, titleHot = false;<br />
		protected Rectangle rClose, rText, rTitle, rDisplay, rScreen, rGlobClose, rGlobText, rGlobTitle, rGlobDisplay;<br />
		protected System.Windows.Forms.Timer viewClock;<br />
		#endregion<br />
<br />
		#region Constructor<br />
		/// <param name="title">Title text displayed in the NotifyWindow</param><br />
		/// <param name="text">Main text displayedin the NotifyWindow</param><br />
		public NotifyWindow (string title, string text) : this() { Title = title; Text = text; }<br />
		/// <param name="text">Text displayed in the NotifyWindow</param><br />
		public NotifyWindow (string text) : this() { Text = text; }<br />
		public NotifyWindow()<br />
		{<br />
			SetStyle (ControlStyles.UserMouse, true);<br />
			SetStyle (ControlStyles.UserPaint, true);<br />
			SetStyle (ControlStyles.AllPaintingInWmPaint, true);		// WmPaint calls OnPaint and OnPaintBackground<br />
			SetStyle (ControlStyles.DoubleBuffer, true);<br />
<br />
			ShowInTaskbar = false;<br />
			FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;<br />
			StartPosition = System.Windows.Forms.FormStartPosition.Manual;<br />
<br />
			// Default values<br />
			BackgroundStyle = BackgroundStyles.VerticalGradient;<br />
			ClockState = ClockStates.None;<br />
			BackColor = Color.SteelBlue;<br />
			GradientColor = Color.WhiteSmoke;<br />
			PressedColor = Color.Gray;<br />
			TitleColor = SystemColors.ControlText;<br />
			TextColor = SystemColors.ControlText;<br />
			WaitOnMouseOver = true;<br />
			ActualWidth = 130;<br />
			ActualHeight = 110;<br />
			WaitTime = 11000;<br />
		}<br />
		#endregion<br />
<br />
		#region Public Methods<br />
		/// <summary><br />
		/// Sets the width and height of the NotifyWindow.<br />
		/// </summary><br />
		public void SetDimensions (int width, int height)<br />
		{<br />
			ActualWidth = width;<br />
			ActualHeight = height;<br />
		}<br />
<br />
<br />
		//JW: How much to adjust the height by (allows stacking of boxes)<br />
		private int heightAdjustment = 0;<br />
<br />
		/// <summary><br />
		/// Displays the NotifyWindow.<br />
		/// </summary><br />
		public void Notify()<br />
		{<br />
			//JW: Prevent multiple displays<br />
			if (activeWindows.Count > MaxActiveWindows)<br />
			{<br />
				//Should we dispose now?<br />
				this.Dispose(true);<br />
				return;<br />
			}<br />
<br />
			if (Text == null || Text.Length < 1)<br />
				throw new System.Exception ("You must set NotifyWindow.Text before calling Notify()");<br />
<br />
			Width = ActualWidth;<br />
			rScreen = Screen.GetWorkingArea (Screen.PrimaryScreen.Bounds);<br />
			Height = 0;<br />
			Top = rScreen.Bottom;<br />
			Left = rScreen.Width - Width - 11;<br />
<br />
			//JW: Work out the stacking adjustment<br />
			//{{{<br />
			heightAdjustment = activeWindows.Count * this.ActualHeight;<br />
			Top -= heightAdjustment;<br />
<br />
			//Record that the window exists in the static member<br />
			activeWindows.Add(this);<br />
			//}}}<br />
			<br />
<br />
			if (HoverFont == null)<br />
				HoverFont = new Font (Font, Font.Style | FontStyle.Underline);<br />
			if (TitleFont == null)<br />
				TitleFont = Font;<br />
			if (TitleHoverFont == null)<br />
				TitleHoverFont = new Font (TitleFont, TitleFont.Style | FontStyle.Underline);<br />
			if (this.StringFormat == null)<br />
			{<br />
				this.StringFormat = new StringFormat();<br />
				this.StringFormat.Alignment = StringAlignment.Center;<br />
				this.StringFormat.LineAlignment = StringAlignment.Center;<br />
				this.StringFormat.Trimming = StringTrimming.EllipsisWord;<br />
			}<br />
<br />
			rDisplay = new Rectangle (0, 0, Width, ActualHeight);<br />
			rClose = new Rectangle (Width - 21, 10, 13, 13);<br />
<br />
			int offset;<br />
			if (Title != null)<br />
			{<br />
				using (Graphics fx = CreateGraphics())<br />
				{<br />
					SizeF sz = fx.MeasureString (Title, TitleFont, ActualWidth - rClose.Width - 22, this.StringFormat);<br />
					rTitle = new Rectangle (11, 12, (int) Math.Ceiling (sz.Width), (int) Math.Ceiling (sz.Height));<br />
					offset = (int) Math.Max (Math.Ceiling (sz.Height + rTitle.Top + 2), rClose.Bottom + 5);<br />
				}<br />
			}<br />
			else<br />
			{<br />
				offset = rClose.Bottom + 1;<br />
				rTitle = new Rectangle (-1, -1, 1, 1);<br />
			}<br />
<br />
			rText = new Rectangle (11, offset, ActualWidth - 22, ActualHeight - (int)(offset * 1.5));<br />
			// rGlob* are Rectangle's Offset'ed to their actual position on the screen, for use with Cursor.Position.<br />
			rGlobClose = rClose;<br />
			rGlobClose.Offset (Left, rScreen.Bottom - ActualHeight);<br />
			rGlobText = rText;<br />
			rGlobText.Offset (Left, rScreen.Bottom - ActualHeight);<br />
			rGlobTitle = rTitle;<br />
			if (Title != null)<br />
				rGlobTitle.Offset (Left, rScreen.Bottom - ActualHeight);<br />
			rGlobDisplay = rDisplay;<br />
			rGlobDisplay.Offset (Left, rScreen.Bottom - ActualHeight);<br />
			rGlobClose = rClose;<br />
			rGlobClose.Offset (Left, rScreen.Bottom - ActualHeight);<br />
			rGlobDisplay = rDisplay;<br />
			rGlobDisplay.Offset (Left, rScreen.Bottom - ActualHeight);<br />
<br />
			// Use unmanaged ShowWindow() and SetWindowPos() instead of the managed Show() to display the window - this method will display<br />
			// the window TopMost, but without stealing focus (namely the SW_SHOWNOACTIVATE and SWP_NOACTIVATE flags)<br />
			ShowWindow (Handle, SW_SHOWNOACTIVATE);<br />
			<br />
			//JW Note heightAdjustment<br />
			//was:<br />
			//  SetWindowPos (Handle, HWND_TOPMOST, rScreen.Width - ActualWidth - 11, rScreen.Bottom, ActualWidth, 0, SWP_NOACTIVATE);<br />
			SetWindowPos (Handle, HWND_TOPMOST, rScreen.Width - ActualWidth - 11, rScreen.Bottom - heightAdjustment, ActualWidth, 0, SWP_NOACTIVATE);<br />
<br />
			viewClock = new System.Windows.Forms.Timer();<br />
			viewClock.Tick += new System.EventHandler (viewTimer);<br />
			viewClock.Interval = 1;<br />
			viewClock.Start();<br />
<br />
			ClockState = ClockStates.Opening;<br />
		}<br />
		#endregion<br />
<br />
		#region Drawing<br />
		protected override void OnPaint (System.Windows.Forms.PaintEventArgs e)<br />
		{<br />
			// Draw the close button and text.<br />
			drawCloseButton (e.Graphics);<br />
<br />
			Font useFont;  Color useColor;<br />
			if (Title != null)<br />
			{<br />
				if (titleHot)<br />
					useFont = TitleHoverFont;<br />
				else<br />
					useFont = TitleFont;<br />
				if (titlePressed)<br />
					useColor = PressedColor;<br />
				else<br />
					useColor = TitleColor;<br />
				using (SolidBrush sb = new SolidBrush (useColor))<br />
					e.Graphics.DrawString (Title, useFont, sb, rTitle, this.StringFormat);<br />
			}<br />
<br />
			if (textHot)<br />
				useFont = HoverFont;<br />
			else<br />
				useFont = Font;<br />
			if (textPressed)<br />
				useColor = PressedColor;<br />
			else<br />
				useColor = TextColor;<br />
			using (SolidBrush sb = new SolidBrush (useColor))<br />
				e.Graphics.DrawString (Text, useFont, sb, rText, this.StringFormat);<br />
		}<br />
<br />
		protected override void OnPaintBackground (System.Windows.Forms.PaintEventArgs e)<br />
		{<br />
			// First paint the background<br />
			if (BackgroundStyle == BackgroundStyles.Solid)<br />
			{<br />
				using (SolidBrush sb = new SolidBrush (BackColor))<br />
					e.Graphics.FillRectangle (sb, rDisplay);<br />
			}<br />
			else<br />
			{<br />
				LinearGradientMode lgm;<br />
				switch (BackgroundStyle)<br />
				{<br />
					case BackgroundStyles.BackwardDiagonalGradient:<br />
						lgm = LinearGradientMode.BackwardDiagonal;<br />
						break;<br />
					case BackgroundStyles.ForwardDiagonalGradient:<br />
						lgm = LinearGradientMode.ForwardDiagonal;<br />
						break;<br />
					case BackgroundStyles.HorizontalGradient:<br />
						lgm = LinearGradientMode.Horizontal;<br />
						break;<br />
					default:<br />
					case BackgroundStyles.VerticalGradient:<br />
						lgm = LinearGradientMode.Vertical;<br />
						break;<br />
				}<br />
				using (LinearGradientBrush lgb = new LinearGradientBrush (rDisplay, GradientColor, BackColor, lgm))<br />
				{<br />
					if (this.Blend != null)<br />
						lgb.Blend = this.Blend;<br />
					e.Graphics.FillRectangle (lgb, rDisplay);<br />
				}<br />
			}<br />
<br />
			// Next draw borders...<br />
			drawBorder (e.Graphics);<br />
		}<br />
<br />
		protected virtual void drawBorder (Graphics fx)<br />
		{<br />
			fx.DrawRectangle (Pens.Silver, 2, 2, Width - 4, ActualHeight - 4);<br />
			<br />
			// Top border<br />
			fx.DrawLine (Pens.Silver, 0, 0, Width, 0);<br />
			fx.DrawLine (Pens.White, 0, 1, Width, 1);<br />
			fx.DrawLine (Pens.DarkGray, 3, 3, Width - 4, 3);<br />
			fx.DrawLine (Pens.DimGray, 4, 4, Width - 5, 4);<br />
<br />
			// Left border<br />
			fx.DrawLine (Pens.Silver, 0, 0, 0, ActualHeight);<br />
			fx.DrawLine (Pens.White, 1, 1, 1, ActualHeight);<br />
			fx.DrawLine (Pens.DarkGray, 3, 3, 3, ActualHeight - 4);<br />
			fx.DrawLine (Pens.DimGray, 4, 4, 4, ActualHeight - 5);<br />
<br />
			// Bottom border<br />
			fx.DrawLine (Pens.DarkGray, 1, ActualHeight - 1, Width - 1, ActualHeight - 1);<br />
			fx.DrawLine (Pens.White, 3, ActualHeight - 3, Width - 3, ActualHeight - 3);<br />
			fx.DrawLine (Pens.Silver, 4, ActualHeight - 4, Width - 4, ActualHeight - 4);<br />
<br />
			// Right border<br />
			fx.DrawLine (Pens.DarkGray, Width - 1, 1, Width - 1, ActualHeight - 1);<br />
			fx.DrawLine (Pens.White, Width - 3, 3, Width - 3, ActualHeight - 3);<br />
			fx.DrawLine (Pens.Silver, Width - 4, 4, Width - 4, ActualHeight - 4);<br />
		}<br />
<br />
		protected virtual void drawCloseButton (Graphics fx)<br />
		{<br />
			if (visualStylesEnabled())<br />
				drawThemeCloseButton (fx);<br />
			else<br />
				drawLegacyCloseButton (fx);<br />
		}<br />
<br />
		/// <summary><br />
		/// Draw a Windows XP style close button.<br />
		/// </summary><br />
		protected void drawThemeCloseButton (Graphics fx)<br />
		{<br />
			IntPtr hTheme = OpenThemeData (Handle, "Window");<br />
			if (hTheme == IntPtr.Zero)<br />
			{<br />
				drawLegacyCloseButton (fx);<br />
				return;<br />
			}<br />
			int stateId;<br />
			if (closePressed)<br />
				stateId = CBS_PUSHED;<br />
			else if (closeHot)<br />
				stateId = CBS_HOT;<br />
			else<br />
				stateId = CBS_NORMAL;<br />
			RECT reClose = new RECT (rClose);<br />
			RECT reClip = reClose; // should fx.VisibleClipBounds be used here?<br />
			IntPtr hDC = fx.GetHdc();<br />
			DrawThemeBackground (hTheme, hDC, WP_CLOSEBUTTON, stateId, ref reClose, ref reClip);<br />
			fx.ReleaseHdc (hDC);<br />
			CloseThemeData (hTheme);<br />
		}<br />
<br />
		/// <summary><br />
		/// Draw a Windows 95 style close button.<br />
		/// </summary><br />
		protected void drawLegacyCloseButton (Graphics fx)<br />
		{<br />
			ButtonState bState;<br />
			if (closePressed)<br />
				bState = ButtonState.Pushed;<br />
			else // the Windows 95 theme doesn't have a "hot" button<br />
				bState = ButtonState.Normal;<br />
			ControlPaint.DrawCaptionButton (fx, rClose, CaptionButton.Close, bState);<br />
		}<br />
<br />
		/// <summary><br />
		/// Determine whether or not XP Visual Styles are active.  Compatible with pre-UxTheme.dll versions of Windows.<br />
		/// </summary><br />
		protected bool visualStylesEnabled()<br />
		{<br />
			try<br />
			{<br />
				if (IsThemeActive() == 1)<br />
					return true;<br />
				else<br />
					return false;<br />
			}<br />
			catch (System.DllNotFoundException)  // pre-XP systems which don't have UxTheme.dll<br />
			{<br />
				return false;<br />
			}<br />
		}<br />
		#endregion<br />
<br />
		<br />
<br />
		#region Timers and EventHandlers<br />
		protected void viewTimer (object sender, System.EventArgs e)<br />
		{<br />
			switch (ClockState)<br />
			{<br />
				case ClockStates.Opening:<br />
					//JW: Note heightAdjustment<br />
					if (Top - 2 <= rScreen.Height - ActualHeight - heightAdjustment)<br />
					{<br />
						//JW: Note heightAdjustment<br />
						Top = rScreen.Height - ActualHeight - heightAdjustment;<br />
						Height = ActualHeight;<br />
						ClockState = ClockStates.Showing;<br />
						viewClock.Interval = WaitTime;<br />
					}<br />
					else<br />
					{<br />
						Top -= 2;<br />
						Height += 2;<br />
					}<br />
					break;<br />
<br />
				case ClockStates.Showing:<br />
					if (!WaitOnMouseOver || !rGlobDisplay.Contains (Cursor.Position))<br />
					{<br />
						viewClock.Interval = 1;<br />
						ClockState = ClockStates.Closing;<br />
					}<br />
					break;<br />
<br />
				case ClockStates.Closing:<br />
					Top += 2;<br />
					Height -= 2;<br />
					<br />
					//JW: Note heightAdjustment<br />
					if (Top >= rScreen.Height - heightAdjustment)<br />
					{<br />
						ClockState = ClockStates.None;<br />
						viewClock.Stop();<br />
						viewClock.Dispose();<br />
<br />
						//JW: Remove the item from our map<br />
						activeWindows.Remove(this);						 <br />
<br />
						Close();<br />
<br />
						//JW: Just in case...<br />
						this.Dispose(true);<br />
					}<br />
					break;<br />
			}<br />
		}<br />
<br />
		protected override void OnMouseMove (System.Windows.Forms.MouseEventArgs e)<br />
		{<br />
			if (Title != null && rGlobTitle.Contains (Cursor.Position) && !textPressed && !closePressed)<br />
			{<br />
				Cursor = Cursors.Hand;<br />
				titleHot = true;<br />
				textHot = false;  closeHot = false;<br />
				Invalidate();<br />
			}<br />
			else if (rGlobText.Contains (Cursor.Position) && !titlePressed && !closePressed)<br />
			{<br />
				Cursor = Cursors.Hand;<br />
				textHot = true;<br />
				titleHot = false;  closeHot = false;<br />
				Invalidate();<br />
			}<br />
			else if (rGlobClose.Contains (Cursor.Position) && !titlePressed && !textPressed)<br />
			{<br />
				Cursor = Cursors.Hand;<br />
				closeHot = true;<br />
				titleHot = false;  textHot = false;<br />
				Invalidate();<br />
			}<br />
			else if ((textHot || titleHot || closeHot) && (!titlePressed && !textPressed && !closePressed))<br />
			{<br />
				Cursor = Cursors.Default;<br />
				titleHot = false;  textHot = false;  closeHot = false;<br />
				Invalidate();<br />
			}<br />
			base.OnMouseMove (e);<br />
		}<br />
<br />
		protected override void OnMouseDown (System.Windows.Forms.MouseEventArgs e)<br />
		{<br />
			if (e.Button == MouseButtons.Left)<br />
			{<br />
				if (rGlobClose.Contains (Cursor.Position))<br />
				{<br />
					closePressed = true;<br />
					closeHot = false;<br />
					Invalidate();<br />
				}<br />
				else if (rGlobText.Contains (Cursor.Position))<br />
				{<br />
					textPressed = true;<br />
					Invalidate();<br />
				}<br />
				else if (Title != null && rGlobTitle.Contains (Cursor.Position))<br />
				{<br />
					titlePressed = true;<br />
					Invalidate();<br />
				}<br />
			}<br />
			base.OnMouseDown (e);<br />
		}<br />
<br />
		protected override void OnMouseUp (System.Windows.Forms.MouseEventArgs e)<br />
		{<br />
			if (e.Button == MouseButtons.Left)<br />
			{<br />
				if (closePressed)<br />
				{<br />
					Cursor = Cursors.Default;<br />
					closePressed = false;<br />
					closeHot = false;<br />
					Invalidate();<br />
					if (rGlobClose.Contains (Cursor.Position))<br />
						Close();<br />
				}<br />
				else if (textPressed)<br />
				{<br />
					Cursor = Cursors.Default;<br />
					textPressed = false;<br />
					textHot = false;<br />
					Invalidate();<br />
					if (rGlobText.Contains (Cursor.Position))<br />
					{<br />
						Close();<br />
						if (TextClicked != null)<br />
							TextClicked (this, new System.EventArgs());<br />
					}<br />
				}<br />
				else if (titlePressed)<br />
				{<br />
					Cursor = Cursors.Default;<br />
					titlePressed = false;<br />
					titleHot = false;<br />
					Invalidate();<br />
					if (rGlobTitle.Contains (Cursor.Position))<br />
					{<br />
						Close();<br />
						if (TitleClicked != null)<br />
							TitleClicked (this, new System.EventArgs());<br />
					}<br />
				}<br />
			}<br />
			base.OnMouseUp (e);<br />
		}<br />
		#endregion<br />
<br />
		#region P/Invoke<br />
		// DrawThemeBackground()<br />
		protected const Int32 WP_CLOSEBUTTON = 18;<br />
		protected const Int32 CBS_NORMAL = 1;<br />
		protected const Int32 CBS_HOT = 2;<br />
		protected const Int32 CBS_PUSHED = 3;<br />
		[StructLayout (LayoutKind.Explicit)]<br />
		protected struct RECT<br />
		{<br />
			[FieldOffset (0)] public Int32 Left;<br />
			[FieldOffset (4)] public Int32 Top;<br />
			[FieldOffset (8)] public Int32 Right;<br />
			[FieldOffset (12)] public Int32 Bottom;<br />
<br />
			public RECT (System.Drawing.Rectangle bounds)<br />
			{<br />
				Left = bounds.Left;<br />
				Top = bounds.Top;<br />
				Right = bounds.Right;<br />
				Bottom = bounds.Bottom;<br />
			}<br />
		}<br />
<br />
		// SetWindowPos()<br />
		protected const Int32 HWND_TOPMOST = -1;<br />
		protected const Int32 SWP_NOACTIVATE = 0x0010;<br />
<br />
		// ShowWindow()<br />
		protected const Int32 SW_SHOWNOACTIVATE = 4;<br />
<br />
		// UxTheme.dll<br />
		[DllImport ("UxTheme.dll")]<br />
		protected static extern Int32 IsThemeActive();<br />
		[DllImport ("UxTheme.dll")]<br />
		protected static extern IntPtr OpenThemeData (IntPtr hWnd, [MarshalAs (UnmanagedType.LPTStr)] string classList);<br />
		[DllImport ("UxTheme.dll")]<br />
		protected static extern void CloseThemeData (IntPtr hTheme);<br />
		[DllImport ("UxTheme.dll")]<br />
		protected static extern void DrawThemeBackground (IntPtr hTheme, IntPtr hDC, Int32 partId, Int32 stateId, ref RECT rect, ref RECT clipRect);<br />
<br />
		// user32.dll<br />
		[DllImport ("user32.dll")]<br />
		protected static extern bool ShowWindow (IntPtr hWnd, Int32 flags);<br />
		[DllImport ("user32.dll")]<br />
		protected static extern bool SetWindowPos (IntPtr hWnd, Int32 hWndInsertAfter, Int32 X, Int32 Y, Int32 cx, Int32 cy, uint uFlags);<br />
		#endregion<br />
	}<br />
}<br />

GeneralRe: Stacking windows code Pin
jdmwood29-Mar-06 0:16
jdmwood29-Mar-06 0:16 
GeneralRe: Stacking windows code Pin
bobishkindaguy11-Dec-06 12:51
bobishkindaguy11-Dec-06 12:51 
GeneralRe: Stacking windows code Pin
PooranPrasad8-Apr-07 21:54
PooranPrasad8-Apr-07 21:54 
GeneralRe: Stacking windows code Pin
n2jtx15-Apr-08 7:27
n2jtx15-Apr-08 7:27 
GeneralRe: Stacking windows code Pin
ronaldovn18-Mar-10 16:40
ronaldovn18-Mar-10 16:40 
GeneralRe: Stacking windows code Pin
ronaldovn18-Mar-10 17:55
ronaldovn18-Mar-10 17:55 
QuestionHow would you call this from a C# ASP.Net Web Application Pin
mranimal25-Feb-06 9:11
mranimal25-Feb-06 9:11 
AnswerRe: How would you call this from a C# ASP.Net Web Application Pin
Paulza25-Jan-07 23:37
Paulza25-Jan-07 23:37 
GeneralGood jumping off point! Pin
davegalligher26-Jan-06 10:06
davegalligher26-Jan-06 10:06 
GeneralNative Window Pin
handle1237-Dec-05 20:22
handle1237-Dec-05 20:22 
GeneralRe: Native Window Pin
Robert Misiak8-Dec-05 2:49
professionalRobert Misiak8-Dec-05 2:49 
GeneralRe: Native Window Pin
pjnaughter12-May-06 3:59
pjnaughter12-May-06 3:59 
GeneralVery Leaky Pin
Matware7-Nov-05 18:33
Matware7-Nov-05 18:33 
GeneralRe: Very Leaky Pin
Robert Misiak8-Dec-05 2:48
professionalRobert Misiak8-Dec-05 2:48 
GeneralRe: Very Leaky Pin
Member 469789318-Sep-08 8:46
Member 469789318-Sep-08 8:46 
GeneralImage Display Pin
Member 192988118-Aug-05 4:46
Member 192988118-Aug-05 4:46 
GeneralRe: Image Display Pin
MarkoStamcar30-Nov-05 12:54
MarkoStamcar30-Nov-05 12:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.