Toasts – Blame information for rev 24
?pathlinks?
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | office | 1 | // =====COPYRIGHT===== |
| 2 | // Code originally retrieved from http://www.vbforums.com/showthread.php?t=547778 - no license information supplied |
||
| 3 | // =====COPYRIGHT===== |
||
| 4 | |||
| 5 | using System; |
||
| 15 | office | 6 | using System.Collections.Concurrent; |
| 16 | office | 7 | using System.Collections.Generic; |
| 8 | using System.Diagnostics; |
||
| 1 | office | 9 | using System.Drawing; |
| 8 | office | 10 | using System.IO; |
| 15 | office | 11 | using System.Linq; |
| 8 | office | 12 | using System.Media; |
| 16 | office | 13 | using System.Threading; |
| 14 | using System.Threading.Tasks; |
||
| 1 | office | 15 | using System.Windows.Forms; |
| 11 | office | 16 | using Toasts.Properties; |
| 1 | office | 17 | |
| 18 | namespace Toasts |
||
| 19 | { |
||
| 2 | office | 20 | public partial class ToastForm : Form |
| 1 | office | 21 | { |
| 24 | office | 22 | #region Public Fields and Properties |
| 23 | public bool EnableChime { get; set; } = true; |
||
| 24 | |||
| 25 | public int LingerTime { get; set; } = 5000; |
||
| 26 | |||
| 27 | public FormAnimator.AnimationMethod AnimationMethod { get; set; } = FormAnimator.AnimationMethod.Slide; |
||
| 28 | |||
| 29 | public FormAnimator.AnimationDirection AnimationDirection { get; set; } = FormAnimator.AnimationDirection.Up; |
||
| 30 | |||
| 31 | public int AnimationDuration { get; set; } = 500; |
||
| 32 | |||
| 33 | public byte[] Chime { get; set; } |
||
| 34 | |||
| 35 | /// <summary> |
||
| 36 | /// |
||
| 37 | /// </summary> |
||
| 38 | /// <remarks>clone the image for safety</remarks> |
||
| 39 | public Image Image |
||
| 40 | { |
||
| 41 | get => new Bitmap(imageBox.Image); |
||
| 42 | set => imageBox.Image = new Bitmap(value); |
||
| 43 | } |
||
| 44 | |||
| 45 | #endregion |
||
| 46 | |||
| 1 | office | 47 | #region Static Fields and Constants |
| 48 | |||
| 16 | office | 49 | private static readonly object OpenNotificationsLock = new object(); |
| 1 | office | 50 | |
| 16 | office | 51 | private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>(); |
| 52 | |||
| 1 | office | 53 | #endregion |
| 54 | |||
| 12 | office | 55 | #region Private Overrides |
| 56 | |||
| 57 | protected override bool ShowWithoutActivation => true; |
||
| 58 | |||
| 59 | protected override CreateParams CreateParams |
||
| 60 | { |
||
| 61 | get |
||
| 62 | { |
||
| 63 | var baseParams = base.CreateParams; |
||
| 64 | |||
| 65 | const int WS_EX_NOACTIVATE = 0x08000000; |
||
| 66 | const int WS_EX_TOOLWINDOW = 0x00000080; |
||
| 67 | const int WS_EX_TOPMOST = 0x00000008; |
||
| 68 | baseParams.ExStyle |= WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST; |
||
| 69 | |||
| 70 | return baseParams; |
||
| 71 | } |
||
| 72 | } |
||
| 73 | |||
| 74 | #endregion |
||
| 75 | |||
| 1 | office | 76 | #region Constructors, Destructors and Finalizers |
| 77 | |||
| 11 | office | 78 | private ToastForm() |
| 7 | office | 79 | { |
| 80 | InitializeComponent(); |
||
| 16 | office | 81 | Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20)); |
| 82 | |||
| 83 | _screenWidth = Screen.PrimaryScreen.WorkingArea.Width; |
||
| 84 | _screenHeight = Screen.PrimaryScreen.WorkingArea.Height; |
||
| 15 | office | 85 | |
| 24 | office | 86 | _formAnimator = new FormAnimator(this, AnimationMethod, AnimationDirection, AnimationDuration); |
| 16 | office | 87 | |
| 88 | using (var memoryStream = new MemoryStream()) |
||
| 15 | office | 89 | { |
| 16 | office | 90 | Resources.normal.CopyTo(memoryStream); |
| 91 | |||
| 92 | memoryStream.Position = 0L; |
||
| 93 | |||
| 24 | office | 94 | Chime = memoryStream.ToArray(); |
| 15 | office | 95 | } |
| 96 | |||
| 18 | office | 97 | _showFormTaskCompletionSource = new TaskCompletionSource<object>(); |
| 98 | |||
| 24 | office | 99 | _toastTimer = new System.Timers.Timer { Enabled = true, AutoReset = false, Interval = LingerTime }; |
| 15 | office | 100 | _toastTimer.Elapsed += ToastTimer_Elapsed; |
| 7 | office | 101 | } |
| 11 | office | 102 | |
| 1 | office | 103 | /// <summary> |
| 15 | office | 104 | /// Display a toast with a title, body and a logo indefinitely on screen. |
| 1 | office | 105 | /// </summary> |
| 15 | office | 106 | /// <param name="title">the toast title</param> |
| 107 | /// <param name="body">the toast body</param> |
||
| 108 | /// <param name="logo">the toast logo</param> |
||
| 24 | office | 109 | public ToastForm(string title, string body) : this() |
| 15 | office | 110 | { |
| 111 | labelTitle.Text = title; |
||
| 112 | labelBody.Text = body; |
||
| 113 | } |
||
| 114 | |||
| 115 | /// <summary> |
||
| 11 | office | 116 | /// Clean up any resources being used. |
| 7 | office | 117 | /// </summary> |
| 118 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
||
| 119 | protected override void Dispose(bool disposing) |
||
| 120 | { |
||
| 16 | office | 121 | if (disposing && components != null) |
| 122 | { |
||
| 123 | if (_toastTimer != null) |
||
| 124 | { |
||
| 125 | _toastTimer.Dispose(); |
||
| 126 | _toastTimer = null; |
||
| 127 | } |
||
| 128 | |||
| 24 | office | 129 | Image.Dispose(); |
| 16 | office | 130 | components.Dispose(); |
| 131 | } |
||
| 7 | office | 132 | base.Dispose(disposing); |
| 133 | } |
||
| 134 | |||
| 1 | office | 135 | #endregion |
| 136 | |||
| 137 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
||
| 138 | |||
| 16 | office | 139 | private readonly FormAnimator _formAnimator; |
| 1 | office | 140 | |
| 16 | office | 141 | private System.Timers.Timer _toastTimer; |
| 1 | office | 142 | |
| 16 | office | 143 | private readonly int _screenWidth; |
| 144 | |||
| 145 | private readonly int _screenHeight; |
||
| 21 | office | 146 | |
| 18 | office | 147 | private readonly TaskCompletionSource<object> _showFormTaskCompletionSource; |
| 16 | office | 148 | |
| 1 | office | 149 | #endregion |
| 150 | |||
| 151 | #region Event Handlers |
||
| 152 | |||
| 9 | office | 153 | private void Notification_Load(object sender, EventArgs e) |
| 1 | office | 154 | { |
| 24 | office | 155 | if (EnableChime) |
| 16 | office | 156 | { |
| 24 | office | 157 | Task.Factory.StartNew(() => |
| 9 | office | 158 | { |
| 24 | office | 159 | using (var memoryStream = new MemoryStream(Chime)) |
| 16 | office | 160 | { |
| 24 | office | 161 | using (var player = new SoundPlayer(memoryStream)) |
| 162 | { |
||
| 163 | player.Play(); |
||
| 164 | } |
||
| 16 | office | 165 | } |
| 24 | office | 166 | }); |
| 167 | } |
||
| 8 | office | 168 | |
| 16 | office | 169 | try |
| 8 | office | 170 | { |
| 16 | office | 171 | lock (OpenNotificationsLock) |
| 8 | office | 172 | { |
| 16 | office | 173 | var notifications = OpenNotifications.ToArray(); |
| 8 | office | 174 | |
| 16 | office | 175 | foreach (var openForm in notifications) |
| 15 | office | 176 | { |
| 16 | office | 177 | if (openForm == null) |
| 178 | { |
||
| 179 | continue; |
||
| 180 | } |
||
| 7 | office | 181 | |
| 16 | office | 182 | if (openForm.IsDisposed) |
| 183 | { |
||
| 184 | OpenNotifications.Remove(openForm); |
||
| 15 | office | 185 | |
| 16 | office | 186 | continue; |
| 187 | } |
||
| 1 | office | 188 | |
| 16 | office | 189 | if (openForm.IsHandleCreated != true) |
| 190 | { |
||
| 191 | continue; |
||
| 192 | } |
||
| 4 | office | 193 | |
| 16 | office | 194 | // Move each open form upwards to make room for this one |
| 195 | var handle = Handle; |
||
| 196 | openForm.BeginInvoke(new MethodInvoker(() => |
||
| 197 | { |
||
| 198 | try |
||
| 199 | { |
||
| 200 | if (openForm.Handle == handle) |
||
| 201 | { |
||
| 202 | return; |
||
| 203 | } |
||
| 1 | office | 204 | |
| 16 | office | 205 | openForm.Top -= Height; |
| 206 | } |
||
| 207 | catch |
||
| 208 | { |
||
| 209 | Debug.WriteLine("Error while moving forms up."); |
||
| 210 | } |
||
| 211 | })); |
||
| 212 | } |
||
| 15 | office | 213 | |
| 16 | office | 214 | Invoke(new MethodInvoker(() => |
| 215 | { |
||
| 216 | Location = new Point(_screenWidth - Width, _screenHeight - Height); |
||
| 217 | })); |
||
| 218 | |||
| 219 | OpenNotifications.Add(this); |
||
| 220 | |||
| 24 | office | 221 | _toastTimer.Start(); |
| 16 | office | 222 | } |
| 223 | } |
||
| 224 | catch |
||
| 15 | office | 225 | { |
| 16 | office | 226 | Debug.WriteLine("Error on form load event."); |
| 15 | office | 227 | } |
| 1 | office | 228 | } |
| 229 | |||
| 230 | private void Notification_FormClosed(object sender, FormClosedEventArgs e) |
||
| 231 | { |
||
| 16 | office | 232 | try |
| 1 | office | 233 | { |
| 16 | office | 234 | lock (OpenNotificationsLock) |
| 7 | office | 235 | { |
| 16 | office | 236 | OpenNotifications.Remove(this); |
| 7 | office | 237 | } |
| 16 | office | 238 | } |
| 239 | catch |
||
| 240 | { |
||
| 241 | Debug.WriteLine("Error in form closed event."); |
||
| 242 | } |
||
| 243 | } |
||
| 15 | office | 244 | |
| 16 | office | 245 | private void Notification_Shown(object sender, EventArgs e) |
| 246 | { |
||
| 247 | // Reverse animation direction for hiding. |
||
| 248 | switch (_formAnimator.Direction) |
||
| 249 | { |
||
| 250 | case FormAnimator.AnimationDirection.Up: |
||
| 251 | _formAnimator.Direction = FormAnimator.AnimationDirection.Down; |
||
| 252 | break; |
||
| 253 | case FormAnimator.AnimationDirection.Down: |
||
| 254 | _formAnimator.Direction = FormAnimator.AnimationDirection.Up; |
||
| 255 | break; |
||
| 256 | case FormAnimator.AnimationDirection.Left: |
||
| 257 | _formAnimator.Direction = FormAnimator.AnimationDirection.Right; |
||
| 258 | break; |
||
| 259 | case FormAnimator.AnimationDirection.Right: |
||
| 260 | _formAnimator.Direction = FormAnimator.AnimationDirection.Left; |
||
| 261 | break; |
||
| 1 | office | 262 | } |
| 18 | office | 263 | |
| 264 | _showFormTaskCompletionSource.TrySetResult(new { }); |
||
| 1 | office | 265 | } |
| 266 | |||
| 18 | office | 267 | private async void ToastTimer_Elapsed(object sender, EventArgs e) |
| 1 | office | 268 | { |
| 18 | office | 269 | await _showFormTaskCompletionSource.Task; |
| 270 | |||
| 16 | office | 271 | if (IsDisposed) |
| 15 | office | 272 | { |
| 273 | return; |
||
| 274 | } |
||
| 1 | office | 275 | |
| 16 | office | 276 | try |
| 277 | { |
||
| 278 | _toastTimer.Stop(); |
||
| 279 | BeginInvoke(new MethodInvoker(() => |
||
| 280 | { |
||
| 281 | lock (OpenNotificationsLock) |
||
| 282 | { |
||
| 283 | Close(); |
||
| 284 | } |
||
| 285 | })); |
||
| 286 | } |
||
| 287 | catch |
||
| 288 | { |
||
| 289 | Debug.WriteLine("Error in timer elapsed event."); |
||
| 290 | } |
||
| 1 | office | 291 | } |
| 292 | |||
| 15 | office | 293 | private void Toast_Click(object sender, EventArgs e) |
| 1 | office | 294 | { |
| 16 | office | 295 | try |
| 296 | { |
||
| 21 | office | 297 | BeginInvoke(new MethodInvoker(() => |
| 16 | office | 298 | { |
| 21 | office | 299 | lock (OpenNotificationsLock) |
| 300 | { |
||
| 301 | Close(); |
||
| 302 | } |
||
| 303 | })); |
||
| 16 | office | 304 | } |
| 305 | catch |
||
| 306 | { |
||
| 307 | Debug.WriteLine("Error in form click event."); |
||
| 308 | } |
||
| 1 | office | 309 | } |
| 310 | |||
| 311 | #endregion |
||
| 312 | } |
||
| 313 | } |