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
<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 />
public class NotifyWindow : System.Windows.Forms.Form<br />
{<br />
<br />
static System.Collections.ArrayList activeWindows = new System.Collections.ArrayList();<br />
<br />
public int MaxActiveWindows= 6;
<br />
#region Public Variables<br />
public string Title;<br />
public System.Drawing.Font TitleFont;<br />
public System.Drawing.Font HoverFont;<br />
public System.Drawing.Font TitleHoverFont;<br />
public BackgroundStyles BackgroundStyle;<br />
public System.Drawing.Drawing2D.Blend Blend;<br />
public System.Drawing.StringFormat StringFormat;<br />
public bool WaitOnMouseOver;<br />
public event System.EventHandler TextClicked;<br />
public event System.EventHandler TitleClicked;<br />
public System.Drawing.Color TitleColor;<br />
public System.Drawing.Color TextColor;<br />
public System.Drawing.Color GradientColor;<br />
public System.Drawing.Color PressedColor;<br />
public int WaitTime;<br />
public int ActualHeight;<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 />
public NotifyWindow (string title, string text) : this() { Title = title; Text = text; }<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);
SetStyle (ControlStyles.DoubleBuffer, true);<br />
<br />
ShowInTaskbar = false;<br />
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;<br />
StartPosition = System.Windows.Forms.FormStartPosition.Manual;<br />
<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 />
public void SetDimensions (int width, int height)<br />
{<br />
ActualWidth = width;<br />
ActualHeight = height;<br />
}<br />
<br />
<br />
private int heightAdjustment = 0;<br />
<br />
public void Notify()<br />
{<br />
if (activeWindows.Count > MaxActiveWindows)<br />
{<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 />
heightAdjustment = activeWindows.Count * this.ActualHeight;<br />
Top -= heightAdjustment;<br />
<br />
activeWindows.Add(this);<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 />
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 />
ShowWindow (Handle, SW_SHOWNOACTIVATE);<br />
<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 />
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 />
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 />
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 />
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 />
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 />
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 />
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 />
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;
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 />
protected void drawLegacyCloseButton (Graphics fx)<br />
{<br />
ButtonState bState;<br />
if (closePressed)<br />
bState = ButtonState.Pushed;<br />
else
bState = ButtonState.Normal;<br />
ControlPaint.DrawCaptionButton (fx, rClose, CaptionButton.Close, bState);<br />
}<br />
<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)
{<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 />
if (Top - 2 <= rScreen.Height - ActualHeight - heightAdjustment)<br />
{<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 />
if (Top >= rScreen.Height - heightAdjustment)<br />
{<br />
ClockState = ClockStates.None;<br />
viewClock.Stop();<br />
viewClock.Dispose();<br />
<br />
activeWindows.Remove(this); <br />
<br />
Close();<br />
<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 />
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 />
protected const Int32 HWND_TOPMOST = -1;<br />
protected const Int32 SWP_NOACTIVATE = 0x0010;<br />
<br />
protected const Int32 SW_SHOWNOACTIVATE = 4;<br />
<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 />
[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 />
|