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