Toasts – Blame information for rev 34

Subversion Repositories:
Rev:
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  
34 office 5 using Markdig.Extensions.Tables;
1 office 6 using System;
15 office 7 using System.Collections.Concurrent;
16 office 8 using System.Collections.Generic;
9 using System.Diagnostics;
1 office 10 using System.Drawing;
8 office 11 using System.IO;
15 office 12 using System.Linq;
8 office 13 using System.Media;
34 office 14 using System.Runtime.InteropServices;
16 office 15 using System.Threading;
16 using System.Threading.Tasks;
1 office 17 using System.Windows.Forms;
34 office 18 using TheArtOfDev.HtmlRenderer.WinForms;
11 office 19 using Toasts.Properties;
34 office 20 using static System.Windows.Forms.VisualStyles.VisualStyleElement;
1 office 21  
22 namespace Toasts
23 {
2 office 24 public partial class ToastForm : Form
1 office 25 {
24 office 26 #region Public Fields and Properties
25 office 27  
24 office 28 public bool EnableChime { get; set; } = true;
29  
30 public int LingerTime { get; set; } = 5000;
31  
32 public FormAnimator.AnimationMethod AnimationMethod { get; set; } = FormAnimator.AnimationMethod.Slide;
33  
34 public FormAnimator.AnimationDirection AnimationDirection { get; set; } = FormAnimator.AnimationDirection.Up;
35  
36 public int AnimationDuration { get; set; } = 500;
37  
26 office 38 public byte[] Chime
39 {
40 get => _chime;
41 set
42 {
43 if (value is null)
44 {
45 return;
46 }
24 office 47  
26 office 48 _chime = value;
49 }
50 }
51  
24 office 52 /// <summary>
53 ///
54 /// </summary>
55 /// <remarks>clone the image for safety</remarks>
56 public Image Image
57 {
34 office 58 get
59 {
60 if (imageBox.Image == null)
61 {
62 return null;
63 }
64 return new Bitmap(imageBox.Image);
65 }
33 office 66 set => imageBox.Image = value;
24 office 67 }
68  
69 #endregion
70  
1 office 71 #region Static Fields and Constants
72  
16 office 73 private static readonly object OpenNotificationsLock = new object();
1 office 74  
16 office 75 private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>();
76  
1 office 77 #endregion
78  
12 office 79 #region Private Overrides
80  
81 protected override bool ShowWithoutActivation => true;
82 protected override CreateParams CreateParams
83 {
84 get
85 {
34 office 86  
12 office 87 var baseParams = base.CreateParams;
34 office 88  
12 office 89 const int WS_EX_NOACTIVATE = 0x08000000;
90 const int WS_EX_TOOLWINDOW = 0x00000080;
91 const int WS_EX_TOPMOST = 0x00000008;
92 baseParams.ExStyle |= WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
34 office 93  
12 office 94 return baseParams;
95 }
96 }
97  
98 #endregion
99  
1 office 100 #region Constructors, Destructors and Finalizers
101  
11 office 102 private ToastForm()
7 office 103 {
104 InitializeComponent();
34 office 105 // round rectangles
106 //Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20));
107  
16 office 108 _screenWidth = Screen.PrimaryScreen.WorkingArea.Width;
109 _screenHeight = Screen.PrimaryScreen.WorkingArea.Height;
15 office 110  
24 office 111 _formAnimator = new FormAnimator(this, AnimationMethod, AnimationDirection, AnimationDuration);
16 office 112  
113 using (var memoryStream = new MemoryStream())
15 office 114 {
16 office 115 Resources.normal.CopyTo(memoryStream);
116  
117 memoryStream.Position = 0L;
118  
24 office 119 Chime = memoryStream.ToArray();
15 office 120 }
121  
18 office 122 _showFormTaskCompletionSource = new TaskCompletionSource<object>();
123  
25 office 124 _toastTimer = new System.Timers.Timer { Enabled = false, AutoReset = false, Interval = LingerTime };
15 office 125 _toastTimer.Elapsed += ToastTimer_Elapsed;
7 office 126 }
11 office 127  
1 office 128 /// <summary>
15 office 129 /// Display a toast with a title, body and a logo indefinitely on screen.
1 office 130 /// </summary>
15 office 131 /// <param name="title">the toast title</param>
132 /// <param name="body">the toast body</param>
24 office 133 public ToastForm(string title, string body) : this()
15 office 134 {
135 labelTitle.Text = title;
27 office 136 htmlPanel1.Text = body;
15 office 137 }
138  
139 /// <summary>
11 office 140 /// Clean up any resources being used.
7 office 141 /// </summary>
142 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
143 protected override void Dispose(bool disposing)
144 {
16 office 145 if (disposing && components != null)
33 office 146 {
147 components.Dispose();
148 }
149  
150 if (_toastTimer != null)
16 office 151 {
33 office 152 _toastTimer.Dispose();
153 _toastTimer = null;
154 }
16 office 155  
33 office 156 if (Image != null)
157 {
24 office 158 Image.Dispose();
33 office 159 Image = null;
16 office 160 }
33 office 161  
7 office 162 base.Dispose(disposing);
163 }
164  
1 office 165 #endregion
166  
167 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
168  
16 office 169 private readonly FormAnimator _formAnimator;
1 office 170  
16 office 171 private System.Timers.Timer _toastTimer;
1 office 172  
16 office 173 private readonly int _screenWidth;
174  
175 private readonly int _screenHeight;
21 office 176  
18 office 177 private readonly TaskCompletionSource<object> _showFormTaskCompletionSource;
16 office 178  
26 office 179 private byte[] _chime;
180  
1 office 181 #endregion
182  
183 #region Event Handlers
184  
25 office 185 private void ToastForm_Load(object sender, EventArgs e)
1 office 186 {
24 office 187 if (EnableChime)
16 office 188 {
24 office 189 Task.Factory.StartNew(() =>
9 office 190 {
24 office 191 using (var memoryStream = new MemoryStream(Chime))
16 office 192 {
24 office 193 using (var player = new SoundPlayer(memoryStream))
194 {
195 player.Play();
196 }
16 office 197 }
24 office 198 });
199 }
8 office 200  
16 office 201 try
8 office 202 {
16 office 203 lock (OpenNotificationsLock)
8 office 204 {
34 office 205 Invoke(new MethodInvoker(() =>
206 {
207 // if not image is provided, just collapse the panel for the extra space
208 if (imageBox.Image == null)
209 {
210 splitContainer1.Panel1Collapsed = true;
211 }
212  
213 // compute notification height from body
214 var maxWidth = tableLayoutPanel4.Width;
215 htmlPanel1.MaximumSize = new Size(maxWidth, Screen.PrimaryScreen.WorkingArea.Height);
216 using (var m_Bitmap = new Bitmap(64, 64))
217 {
218 PointF point = new PointF(0, 0);
219 SizeF maxSize = new SizeF(maxWidth, Screen.PrimaryScreen.WorkingArea.Height);
220 using (var graphics = Graphics.FromImage(m_Bitmap))
221 {
222 var size = HtmlRender.Render(graphics, htmlPanel1.Text, point, maxSize);
223 // total height = height of text fitting in rectangle + the height of the title on top + one extra line for the last line wrap
224 Height = (int)Math.Round(size.Height) + labelTitle.Height + (int)Math.Round(htmlPanel1.Font.GetHeight());
225 }
226 }
227 }));
228  
16 office 229 var notifications = OpenNotifications.ToArray();
8 office 230  
16 office 231 foreach (var openForm in notifications)
15 office 232 {
16 office 233 if (openForm == null)
234 {
235 continue;
236 }
7 office 237  
16 office 238 if (openForm.IsDisposed)
239 {
240 OpenNotifications.Remove(openForm);
15 office 241  
16 office 242 continue;
243 }
1 office 244  
16 office 245 if (openForm.IsHandleCreated != true)
246 {
247 continue;
248 }
4 office 249  
16 office 250 // Move each open form upwards to make room for this one
251 var handle = Handle;
252 openForm.BeginInvoke(new MethodInvoker(() =>
253 {
254 try
255 {
256 if (openForm.Handle == handle)
257 {
258 return;
259 }
1 office 260  
16 office 261 openForm.Top -= Height;
262 }
263 catch
264 {
265 Debug.WriteLine("Error while moving forms up.");
266 }
267 }));
268 }
15 office 269  
16 office 270 Invoke(new MethodInvoker(() =>
271 {
34 office 272 // set the location of the toast
16 office 273 Location = new Point(_screenWidth - Width, _screenHeight - Height);
274 }));
275  
276 OpenNotifications.Add(this);
277  
25 office 278 _toastTimer.Enabled = true;
24 office 279 _toastTimer.Start();
16 office 280 }
281 }
282 catch
15 office 283 {
16 office 284 Debug.WriteLine("Error on form load event.");
15 office 285 }
1 office 286 }
287  
25 office 288 private void ToastForm_FormClosed(object sender, FormClosedEventArgs e)
1 office 289 {
16 office 290 try
1 office 291 {
16 office 292 lock (OpenNotificationsLock)
7 office 293 {
16 office 294 OpenNotifications.Remove(this);
7 office 295 }
16 office 296 }
297 catch
298 {
299 Debug.WriteLine("Error in form closed event.");
300 }
301 }
15 office 302  
25 office 303 private void ToastForm_Shown(object sender, EventArgs e)
16 office 304 {
305 // Reverse animation direction for hiding.
306 switch (_formAnimator.Direction)
307 {
308 case FormAnimator.AnimationDirection.Up:
309 _formAnimator.Direction = FormAnimator.AnimationDirection.Down;
310 break;
311 case FormAnimator.AnimationDirection.Down:
312 _formAnimator.Direction = FormAnimator.AnimationDirection.Up;
313 break;
314 case FormAnimator.AnimationDirection.Left:
315 _formAnimator.Direction = FormAnimator.AnimationDirection.Right;
316 break;
317 case FormAnimator.AnimationDirection.Right:
318 _formAnimator.Direction = FormAnimator.AnimationDirection.Left;
319 break;
1 office 320 }
34 office 321  
18 office 322 _showFormTaskCompletionSource.TrySetResult(new { });
1 office 323 }
324  
18 office 325 private async void ToastTimer_Elapsed(object sender, EventArgs e)
1 office 326 {
18 office 327 await _showFormTaskCompletionSource.Task;
328  
16 office 329 if (IsDisposed)
15 office 330 {
331 return;
332 }
1 office 333  
16 office 334 try
335 {
336 _toastTimer.Stop();
337 BeginInvoke(new MethodInvoker(() =>
338 {
339 lock (OpenNotificationsLock)
340 {
341 Close();
342 }
343 }));
344 }
345 catch
346 {
347 Debug.WriteLine("Error in timer elapsed event.");
348 }
1 office 349 }
350  
15 office 351 private void Toast_Click(object sender, EventArgs e)
1 office 352 {
16 office 353 try
354 {
21 office 355 BeginInvoke(new MethodInvoker(() =>
16 office 356 {
21 office 357 lock (OpenNotificationsLock)
358 {
359 Close();
360 }
361 }));
16 office 362 }
363 catch
364 {
365 Debug.WriteLine("Error in form click event.");
366 }
1 office 367 }
368  
369 #endregion
370 }
371 }