Toasts – Diff between revs 52 and 53
?pathlinks?
Rev 52 | Rev 53 | |||
---|---|---|---|---|
Line 11... | Line 11... | |||
11 | using System.IO; |
11 | using System.IO; |
|
12 | using System.Linq; |
12 | using System.Linq; |
|
13 | using System.Media; |
13 | using System.Media; |
|
14 | using System.Net.Http; |
14 | using System.Net.Http; |
|
15 | using System.Runtime.InteropServices; |
15 | using System.Runtime.InteropServices; |
|
- | 16 | using System.Runtime.Remoting.Messaging; |
||
- | 17 | using System.Runtime.Serialization; |
||
16 | using System.Threading; |
18 | using System.Threading; |
|
17 | using System.Threading.Tasks; |
19 | using System.Threading.Tasks; |
|
18 | using System.Windows.Forms; |
20 | using System.Windows.Forms; |
|
- | 21 | using System.Xml.Linq; |
||
19 | using TheArtOfDev.HtmlRenderer.WinForms; |
22 | using TheArtOfDev.HtmlRenderer.WinForms; |
|
20 | using Toasts.Properties; |
23 | using Toasts.Properties; |
|
- | 24 | using Toasts.Utilities; |
||
21 | |
25 | |
|
22 | namespace Toasts |
26 | namespace Toasts |
|
23 | { |
27 | { |
|
24 | public partial class ToastForm : Form |
28 | public partial class ToastForm : Form |
|
25 | { |
29 | { |
|
26 | #region Public Fields and Properties |
30 | #region Public Fields and Properties |
|
27 | |
- | ||
28 | public string UserAgent { get; set; } = string.Empty; |
31 | public string UserAgent { get; set; } = string.Empty; |
|
Line 29... | Line 32... | |||
29 | |
32 | |
|
Line 30... | Line 33... | |||
30 | public bool EnableChime { get; set; } = true; |
33 | public bool EnableChime { get; set; } = true; |
|
Line 80... | Line 83... | |||
80 | /// |
83 | /// |
|
81 | /// </summary> |
84 | /// </summary> |
|
82 | /// <remarks>clone the image for safety</remarks> |
85 | /// <remarks>clone the image for safety</remarks> |
|
83 | public Image Image |
86 | public Image Image |
|
84 | { |
87 | { |
|
- | 88 | get => _image; |
||
85 | get |
89 | set |
|
86 | { |
90 | { |
|
87 | try |
91 | if (value == _image) |
|
88 | { |
92 | { |
|
89 | |
- | ||
90 | return new Bitmap(imageBox.Image); |
- | ||
91 | } |
- | ||
92 | catch |
- | ||
93 | { |
- | ||
94 | return null; |
93 | return; |
|
95 | } |
94 | } |
|
- | 95 | |
||
- | 96 | _image = new Bitmap(value); |
||
96 | } |
97 | } |
|
97 | set => imageBox.Image = value; |
- | ||
98 | } |
98 | } |
|
Line 99... | Line 99... | |||
99 | |
99 | |
|
Line 100... | Line 100... | |||
100 | #endregion |
100 | #endregion |
|
Line 101... | Line 101... | |||
101 | |
101 | |
|
Line 102... | Line 102... | |||
102 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
102 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
|
- | 103 | |
||
- | 104 | private static readonly object OpenNotificationsLock = new object(); |
||
Line 103... | Line 105... | |||
103 | |
105 | |
|
Line 104... | Line 106... | |||
104 | private static readonly object OpenNotificationsLock = new object(); |
106 | private static readonly List<ToastForm> OpenNotifications = new List<ToastForm>(); |
|
Line -... | Line 107... | |||
- | 107 | |
||
- | 108 | private TaskCompletionSource<object> _handleCreatedTaskCompletionSource; |
||
105 | |
109 | |
|
Line 106... | Line 110... | |||
106 | private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>(); |
110 | private bool _toastDetached; |
|
Line 107... | Line 111... | |||
107 | |
111 | |
|
- | 112 | private object _toastDetachedLock = new object(); |
||
- | 113 | |
||
Line 108... | Line 114... | |||
108 | private bool _toastDetached; |
114 | private Image _image; |
|
Line 109... | Line 115... | |||
109 | |
115 | |
|
Line 110... | Line 116... | |||
110 | private object _toastDetachedLock = new object(); |
116 | private bool _mouseDown; |
|
Line 111... | Line 117... | |||
111 | |
117 | |
|
Line 112... | Line -... | |||
112 | private bool _mouseDown; |
- | ||
113 | |
- | ||
114 | private Point _lastLocation; |
118 | private Point _lastLocation; |
|
Line 115... | Line 119... | |||
115 | |
119 | |
|
Line 116... | Line 120... | |||
116 | private static HttpClient httpClient = new HttpClient(); |
120 | private static HttpClient _httpClient = new HttpClient(); |
|
Line 155... | Line 159... | |||
155 | #region Constructors, Destructors and Finalizers |
159 | #region Constructors, Destructors and Finalizers |
|
Line 156... | Line 160... | |||
156 | |
160 | |
|
157 | private ToastForm() |
161 | private ToastForm() |
|
158 | { |
162 | { |
|
- | 163 | InitializeComponent(); |
||
159 | InitializeComponent(); |
164 | |
|
- | 165 | _handleCreatedTaskCompletionSource = new TaskCompletionSource<object>(); |
||
160 | // round rectangles |
166 | |
|
Line 161... | Line 167... | |||
161 | //Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20)); |
167 | HandleCreated += ToastForm_HandleCreated; |
|
162 | |
168 | |
|
163 | _screenWidth = Screen.PrimaryScreen.WorkingArea.Width; |
- | ||
Line -... | Line 169... | |||
- | 169 | _screenWidth = Screen.PrimaryScreen.WorkingArea.Width; |
||
164 | _screenHeight = Screen.PrimaryScreen.WorkingArea.Height; |
170 | _screenHeight = Screen.PrimaryScreen.WorkingArea.Height; |
|
Line -... | Line 171... | |||
- | 171 | |
||
165 | _showFormTaskCompletionSource = new TaskCompletionSource<object>(); |
172 | _formAnimator = new FormAnimator(this, AnimationMethod, AnimationDirection, AnimationDuration); |
|
166 | |
173 | } |
|
167 | } |
174 | |
|
168 | |
175 | |
|
169 | /// <summary> |
176 | /// <summary> |
|
Line 183... | Line 190... | |||
183 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
190 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
|
184 | protected override void Dispose(bool disposing) |
191 | protected override void Dispose(bool disposing) |
|
185 | { |
192 | { |
|
186 | if (disposing && components != null) |
193 | if (disposing && components != null) |
|
187 | { |
194 | { |
|
188 | components.Dispose(); |
195 | HandleCreated -= ToastForm_HandleCreated; |
|
189 | } |
- | ||
Line 190... | Line 196... | |||
190 | |
196 | |
|
191 | if (_toastTimer != null) |
197 | if (_toastTimer != null) |
|
192 | { |
- | ||
193 | _toastTimer.Stop(); |
198 | { |
|
194 | _toastTimer.Dispose(); |
199 | _toastTimer.Dispose(); |
|
195 | _toastTimer = null; |
200 | _toastTimer = null; |
|
Line 196... | Line 201... | |||
196 | } |
201 | } |
|
197 | |
202 | |
|
198 | if (Image != null) |
203 | if (_image != null) |
|
199 | { |
204 | { |
|
- | 205 | _image.Dispose(); |
||
- | 206 | _image = null; |
||
- | 207 | } |
||
200 | Image.Dispose(); |
208 | |
|
Line 201... | Line 209... | |||
201 | Image = null; |
209 | components.Dispose(); |
|
202 | } |
210 | } |
|
Line 203... | Line 211... | |||
203 | |
211 | |
|
Line 204... | Line 212... | |||
204 | base.Dispose(disposing); |
212 | base.Dispose(disposing); |
|
Line -... | Line 213... | |||
- | 213 | } |
||
- | 214 | |
||
- | 215 | #endregion |
||
- | 216 | |
||
- | 217 | #region Event Handlers |
||
- | 218 | |
||
- | 219 | private void ToastForm_HandleCreated(object sender, EventArgs e) |
||
- | 220 | { |
||
- | 221 | _handleCreatedTaskCompletionSource.TrySetResult(new { }); |
||
- | 222 | } |
||
- | 223 | |
||
- | 224 | private void Toast_MouseEnter(object sender, EventArgs e) |
||
- | 225 | { |
||
- | 226 | if (_toastTimer == null) |
||
- | 227 | { |
||
- | 228 | return; |
||
- | 229 | } |
||
- | 230 | |
||
- | 231 | _toastTimer.Stop(); |
||
- | 232 | } |
||
- | 233 | |
||
- | 234 | private void Toast_MouseLeave(object sender, EventArgs e) |
||
- | 235 | { |
||
- | 236 | if (_toastTimer == null) |
||
- | 237 | { |
||
205 | } |
238 | return; |
|
206 | |
239 | } |
|
207 | #endregion |
240 | |
|
208 | |
241 | _toastTimer.Start(); |
|
209 | #region Event Handlers |
242 | } |
|
Line 220... | Line 253... | |||
220 | } |
253 | } |
|
221 | } |
254 | } |
|
Line 222... | Line 255... | |||
222 | |
255 | |
|
223 | private void panel2_Click(object sender, EventArgs e) |
256 | private void panel2_Click(object sender, EventArgs e) |
|
224 | { |
257 | { |
|
225 | try |
258 | lock (OpenNotificationsLock) |
|
226 | { |
259 | { |
|
227 | BeginInvoke(new MethodInvoker(() => |
260 | this.InvokeIfRequired(form => |
|
228 | { |
- | ||
229 | lock (OpenNotificationsLock) |
- | ||
230 | { |
261 | { |
|
231 | Close(); |
- | ||
232 | } |
262 | form.Close(); |
|
233 | })); |
- | ||
234 | } |
- | ||
235 | catch |
- | ||
236 | { |
- | ||
237 | Debug.WriteLine("Error in form click event."); |
263 | }); |
|
238 | } |
264 | } |
|
Line 239... | Line 265... | |||
239 | } |
265 | } |
|
240 | |
266 | |
|
Line 249... | Line 275... | |||
249 | |
275 | |
|
250 | BringToFront(); |
276 | BringToFront(); |
|
251 | } |
277 | } |
|
Line 252... | Line 278... | |||
252 | } |
278 | } |
|
253 | |
279 | |
|
254 | private void ToastForm_Load(object sender, EventArgs e) |
- | ||
255 | { |
- | ||
256 | // set up the timer when the notification should be removed |
- | ||
257 | _toastTimer = new System.Timers.Timer { Enabled = false, AutoReset = false, Interval = LingerTime }; |
- | ||
258 | _toastTimer.Elapsed += ToastTimer_Elapsed; |
280 | private async void ToastForm_Load(object sender, EventArgs e) |
|
259 | |
281 | { |
|
260 | if (EnableChime) |
282 | if (EnableChime) |
|
261 | { |
283 | { |
|
262 | Task.Factory.StartNew(() => |
284 | try |
|
263 | { |
285 | { |
|
264 | try |
286 | using (var memoryStream = new MemoryStream(Chime)) |
|
265 | { |
287 | { |
|
266 | using (var memoryStream = new MemoryStream(Chime)) |
- | ||
267 | { |
- | ||
268 | using (var player = new SoundPlayer(memoryStream)) |
288 | using (var player = new SoundPlayer(memoryStream)) |
|
269 | { |
- | ||
270 | player.Play(); |
289 | { |
|
271 | } |
290 | player.Play(); |
|
- | 291 | } |
||
272 | } |
292 | } |
|
273 | } |
293 | } |
|
274 | catch |
294 | catch |
|
275 | { |
- | ||
276 | Debug.WriteLine("Sound file could not be played."); |
295 | { |
|
277 | } |
296 | Debug.WriteLine("Sound file could not be played."); |
|
Line 278... | Line -... | |||
278 | }); |
- | ||
279 | } |
- | ||
280 | |
297 | } |
|
281 | try |
- | ||
282 | { |
- | ||
283 | lock (OpenNotificationsLock) |
- | ||
284 | { |
- | ||
285 | // if not image is provided, just collapse the panel for the extra space |
- | ||
286 | if (imageBox.Image == null) |
- | ||
Line -... | Line 298... | |||
- | 298 | } |
||
- | 299 | |
||
- | 300 | await _handleCreatedTaskCompletionSource.Task; |
||
- | 301 | |
||
- | 302 | // if no image is provided, collapse the panel for the extra space |
||
287 | { |
303 | // otherwise display the image |
|
288 | splitContainer1.Panel1Collapsed = true; |
304 | switch (_image == null) |
|
- | 305 | { |
||
289 | } |
306 | case true: |
|
290 | |
307 | splitContainer1.Panel1Collapsed = true; |
|
Line 291... | Line -... | |||
291 | Invoke(new MethodInvoker(() => |
- | ||
292 | { |
308 | break; |
|
293 | // compute notification height from body |
- | ||
294 | var maxWidth = tableLayoutPanel4.Width; |
- | ||
295 | |
- | ||
296 | switch (ContentType) |
- | ||
297 | { |
- | ||
298 | case "text/markdown": |
- | ||
299 | var htmlDocument = new HtmlAgilityPack.HtmlDocument(); |
- | ||
300 | var panelText = htmlPanel1.Text; |
- | ||
301 | if (!string.IsNullOrEmpty(panelText)) |
- | ||
302 | { |
- | ||
303 | htmlDocument.LoadHtml(panelText); |
- | ||
304 | if (htmlDocument.DocumentNode != null && htmlDocument.DocumentNode.Descendants().Any()) |
- | ||
305 | { |
- | ||
306 | var imgNodes = htmlDocument.DocumentNode.SelectNodes("//img"); |
- | ||
307 | if (imgNodes != null && imgNodes.Any()) |
- | ||
308 | { |
- | ||
309 | foreach (var node in imgNodes) |
- | ||
Line 310... | Line -... | |||
310 | { |
- | ||
311 | node.SetAttributeValue("style", $"max-width: {maxWidth}px"); |
- | ||
312 | } |
309 | default: |
|
313 | } |
310 | imageBox.BackgroundImage = _image; |
|
314 | } |
- | ||
315 | |
- | ||
Line -... | Line 311... | |||
- | 311 | break; |
||
- | 312 | |
||
- | 313 | } |
||
316 | htmlPanel1.Text = htmlDocument.DocumentNode.WriteTo(); |
314 | |
|
- | 315 | // compute notification height from body |
||
- | 316 | var maxWidth = tableLayoutPanel4.Width; |
||
- | 317 | |
||
- | 318 | switch (ContentType) |
||
317 | } |
319 | { |
|
318 | break; |
320 | case "text/markdown": |
|
319 | default: |
321 | var htmlDocument = new HtmlAgilityPack.HtmlDocument(); |
|
320 | break; |
322 | var panelText = htmlPanel1.Text; |
|
321 | } |
323 | if (!string.IsNullOrEmpty(panelText)) |
|
- | 324 | { |
||
- | 325 | htmlDocument.LoadHtml(panelText); |
||
- | 326 | if (htmlDocument.DocumentNode != null && htmlDocument.DocumentNode.Descendants().Any()) |
||
322 | |
327 | { |
|
323 | //var maxSize = new Size(maxWidth, Screen.PrimaryScreen.WorkingArea.Height); |
328 | var imgNodes = htmlDocument.DocumentNode.SelectNodes("//img"); |
|
324 | using (var image = HtmlRender.RenderToImage(htmlPanel1.Text, maxWidth, 0, Color.Empty)) |
329 | if (imgNodes != null && imgNodes.Any()) |
|
325 | { |
- | ||
326 | var height = image.Height + labelTitle.Height; |
- | ||
327 | if(height > Height) |
- | ||
328 | { |
- | ||
329 | Height = height; |
- | ||
330 | } |
- | ||
331 | } |
- | ||
332 | })); |
- | ||
333 | |
- | ||
334 | if (EnablePin) |
- | ||
335 | { |
- | ||
336 | Invoke(new MethodInvoker(() => |
- | ||
337 | { |
- | ||
338 | // set the location of the toast |
- | ||
339 | Location = new Point(PinPoint.X, PinPoint.Y); |
- | ||
Line 340... | Line 330... | |||
340 | })); |
330 | { |
|
341 | |
331 | foreach (var node in imgNodes) |
|
- | 332 | { |
||
- | 333 | node.SetAttributeValue("style", $"max-width: {maxWidth}px"); |
||
- | 334 | } |
||
- | 335 | } |
||
Line -... | Line 336... | |||
- | 336 | } |
||
- | 337 | |
||
342 | // remove top-most |
338 | htmlPanel1.Text = htmlDocument.DocumentNode.WriteTo(); |
|
- | 339 | } |
||
- | 340 | break; |
||
- | 341 | default: |
||
- | 342 | break; |
||
- | 343 | } |
||
Line -... | Line 344... | |||
- | 344 | |
||
- | 345 | using (var image = HtmlRender.RenderToImage(htmlPanel1.Text, maxWidth, 0, Color.Empty)) |
||
343 | SetWindowPos(Handle, -2, 0, 0, 0, 0, 0x0001 | 0x0002); |
346 | { |
|
- | 347 | var height = image.Height + labelTitle.Height; |
||
- | 348 | if (height > Height) |
||
344 | |
349 | { |
|
345 | _formAnimator.Method = AnimationMethodDetached; |
- | ||
346 | _formAnimator.Direction = AnimationDirectionDetached; |
- | ||
347 | |
350 | Height = height; |
|
348 | return; |
- | ||
349 | } |
- | ||
350 | |
351 | } |
|
351 | var notifications = OpenNotifications.ToArray(); |
352 | } |
|
352 | |
- | ||
353 | foreach (var openForm in notifications) |
- | ||
354 | { |
- | ||
355 | if (openForm == null) |
353 | |
|
Line 356... | Line -... | |||
356 | { |
- | ||
357 | continue; |
354 | if (EnablePin) |
|
358 | } |
355 | { |
|
359 | |
- | ||
Line 360... | Line -... | |||
360 | if (openForm.IsDisposed) |
- | ||
361 | { |
356 | lock (OpenNotificationsLock) |
|
362 | OpenNotifications.Remove(openForm); |
357 | { |
|
363 | |
- | ||
364 | continue; |
- | ||
365 | } |
- | ||
366 | |
- | ||
367 | if (openForm.IsHandleCreated != true) |
- | ||
368 | { |
- | ||
369 | continue; |
- | ||
Line 370... | Line 358... | |||
370 | } |
358 | this.InvokeIfRequired(form => |
|
- | 359 | { |
||
Line 371... | Line 360... | |||
371 | |
360 | // set the location of the toast |
|
372 | // Move each open form upwards to make room for this one |
- | ||
373 | var handle = Handle; |
361 | form.Location = new Point(PinPoint.X, PinPoint.Y); |
|
374 | openForm.BeginInvoke(new MethodInvoker(() => |
362 | }); |
|
375 | { |
- | ||
376 | try |
- | ||
377 | { |
363 | } |
|
378 | if (openForm.Handle == handle) |
364 | |
|
379 | { |
- | ||
380 | return; |
- | ||
381 | } |
- | ||
382 | |
- | ||
383 | openForm.Top -= Height; |
365 | // remove top-most |
|
384 | |
366 | SetWindowPos(Handle, -2, 0, 0, 0, 0, 0x0001 | 0x0002); |
|
385 | // do not render offscreen |
- | ||
386 | if (!IsFormOnScreen(openForm)) |
367 | |
|
387 | { |
368 | _formAnimator.Method = AnimationMethodDetached; |
|
Line 388... | Line 369... | |||
388 | OpenNotifications.Remove(openForm); |
369 | _formAnimator.Direction = AnimationDirectionDetached; |
|
389 | openForm.Close(); |
370 | |
|
- | 371 | return; |
||
- | 372 | } |
||
- | 373 | |
||
- | 374 | lock (OpenNotificationsLock) |
||
390 | if (!openForm.IsDisposed) |
375 | { |
|
391 | { |
376 | foreach (var openForm in new List<ToastForm>(OpenNotifications)) |
|
- | 377 | { |
||
- | 378 | if (openForm == null || openForm.IsDisposed) |
||
- | 379 | { |
||
392 | openForm.Dispose(); |
380 | OpenNotifications.Remove(openForm); |
|
- | 381 | continue; |
||
Line 393... | Line 382... | |||
393 | } |
382 | } |
|
- | 383 | |
||
Line 394... | Line 384... | |||
394 | } |
384 | openForm.InvokeIfRequired(form => |
|
395 | } |
385 | { |
|
396 | catch |
386 | foreach (Screen screen in Screen.AllScreens) |
|
397 | { |
- | ||
398 | Debug.WriteLine("Error while moving forms up."); |
- | ||
399 | } |
- | ||
400 | })); |
- | ||
401 | } |
387 | { |
|
- | 388 | if (!screen.WorkingArea.Contains(new Rectangle(form.Left, form.Top - Height, form.Width, form.Height))) |
||
- | 389 | { |
||
- | 390 | form.Close(); |
||
- | 391 | OpenNotifications.Remove(form); |
||
- | 392 | } |
||
402 | |
393 | } |
|
Line 403... | Line 394... | |||
403 | Invoke(new MethodInvoker(() => |
394 | form.Top -= Height; |
|
404 | { |
395 | }); |
|
405 | // set the location of the toast |
396 | } |
|
406 | Location = new Point(_screenWidth - Width, _screenHeight - Height); |
397 | |
|
- | 398 | // set the location of the toast |
||
- | 399 | Location = new Point(_screenWidth - Width, _screenHeight - Height); |
||
- | 400 | |
||
- | 401 | OpenNotifications.Add(this); |
||
407 | })); |
402 | // show the form |
|
- | 403 | //Opacity = 1; |
||
- | 404 | } |
||
408 | |
405 | |
|
Line 409... | Line 406... | |||
409 | OpenNotifications.Add(this); |
406 | // set up the timer when the notification should be removed |
|
410 | |
407 | _toastTimer = new System.Timers.Timer { Enabled = true, AutoReset = false, Interval = LingerTime }; |
|
411 | _toastTimer.Enabled = true; |
408 | _toastTimer.Elapsed += ToastTimer_Elapsed; |
|
412 | _toastTimer.Start(); |
409 | _toastTimer.Start(); |
|
413 | } |
410 | } |
|
414 | } |
411 | |
|
415 | catch |
412 | private async void HtmlPanel1_ImageLoad(object sender, TheArtOfDev.HtmlRenderer.Core.Entities.HtmlImageLoadEventArgs e) |
|
Line 444... | Line 441... | |||
444 | } |
441 | } |
|
445 | } |
442 | } |
|
Line 446... | Line 443... | |||
446 | |
443 | |
|
447 | private void ToastForm_FormClosed(object sender, FormClosedEventArgs e) |
444 | private void ToastForm_FormClosed(object sender, FormClosedEventArgs e) |
|
448 | { |
445 | { |
|
449 | try |
446 | lock (OpenNotificationsLock) |
|
450 | { |
- | ||
451 | lock (OpenNotificationsLock) |
- | ||
452 | { |
447 | { |
|
453 | OpenNotifications.Remove(this); |
- | ||
454 | } |
- | ||
455 | } |
- | ||
456 | catch |
- | ||
457 | { |
- | ||
458 | Debug.WriteLine("Error in form closed event."); |
448 | OpenNotifications.Remove(this); |
|
459 | } |
449 | } |
|
Line 460... | Line 450... | |||
460 | } |
450 | } |
|
461 | |
451 | |
|
- | 452 | private async void ToastForm_Shown(object sender, EventArgs e) |
||
462 | private void ToastForm_Shown(object sender, EventArgs e) |
453 | { |
|
Line 463... | Line 454... | |||
463 | { |
454 | // wait for window creation |
|
464 | _formAnimator = new FormAnimator(this, AnimationMethod, AnimationDirection, AnimationDuration); |
455 | await _handleCreatedTaskCompletionSource.Task; |
|
465 | |
456 | |
|
466 | // Reverse animation direction for hiding. |
457 | // Reverse animation direction for hiding. |
|
Line 478... | Line 469... | |||
478 | case FormAnimator.AnimationDirection.Right: |
469 | case FormAnimator.AnimationDirection.Right: |
|
479 | _formAnimator.Direction = FormAnimator.AnimationDirection.Left; |
470 | _formAnimator.Direction = FormAnimator.AnimationDirection.Left; |
|
480 | break; |
471 | break; |
|
481 | } |
472 | } |
|
Line 482... | Line -... | |||
482 | |
- | ||
- | 473 | |
||
483 | _showFormTaskCompletionSource.TrySetResult(new { }); |
474 | |
|
Line 484... | Line 475... | |||
484 | } |
475 | } |
|
485 | |
476 | |
|
486 | private async void ToastTimer_Elapsed(object sender, EventArgs e) |
- | ||
487 | { |
- | ||
488 | await _showFormTaskCompletionSource.Task; |
477 | private void ToastTimer_Elapsed(object sender, EventArgs e) |
|
489 | |
478 | { |
|
490 | if (IsDisposed) |
479 | if (IsDisposed) |
|
491 | { |
480 | { |
|
Line 502... | Line 491... | |||
502 | |
491 | |
|
503 | try |
492 | try |
|
504 | { |
493 | { |
|
Line 505... | Line 494... | |||
505 | _toastTimer.Stop(); |
494 | _toastTimer.Stop(); |
|
506 | |
495 | |
|
507 | BeginInvoke(new MethodInvoker(() => |
496 | lock (OpenNotificationsLock) |
|
508 | { |
497 | { |
|
509 | lock (OpenNotificationsLock) |
498 | this.InvokeIfRequired(form => |
|
510 | { |
499 | { |
|
511 | Close(); |
500 | form.Close(); |
|
512 | } |
501 | }); |
|
513 | })); |
502 | } |
|
514 | } |
503 | } |
|
515 | catch |
504 | catch |
|
516 | { |
505 | { |
|
Line 546... | Line 535... | |||
546 | _toastDetached = true; |
535 | _toastDetached = true; |
|
Line 547... | Line 536... | |||
547 | |
536 | |
|
548 | _toastTimer.Elapsed -= ToastTimer_Elapsed; |
537 | _toastTimer.Elapsed -= ToastTimer_Elapsed; |
|
549 | _toastTimer.Stop(); |
538 | _toastTimer.Stop(); |
|
- | 539 | _toastTimer.Dispose(); |
||
Line 550... | Line 540... | |||
550 | _toastTimer.Dispose(); |
540 | _toastTimer = null; |
|
551 | |
541 | |
|
552 | BeginInvoke(new MethodInvoker(() => |
- | ||
553 | { |
- | ||
554 | lock (OpenNotificationsLock) |
- | ||
555 | { |
- | ||
556 | if (OpenNotifications.Contains(this)) |
542 | lock (OpenNotificationsLock) |
|
557 | { |
- | ||
558 | OpenNotifications.Remove(this); |
- | ||
559 | } |
543 | { |
|
Line 560... | Line 544... | |||
560 | } |
544 | OpenNotifications.Remove(this); |
|
561 | })); |
545 | } |
|
Line 562... | Line 546... | |||
562 | |
546 | |
|
Line 576... | Line 560... | |||
576 | |
560 | |
|
Line 577... | Line 561... | |||
577 | #endregion |
561 | #endregion |
|
Line 578... | Line -... | |||
578 | |
- | ||
579 | #region Private Methods |
- | ||
580 | |
- | ||
581 | /// <summary> |
- | ||
582 | /// Checks if a form is contained within any of the screens. |
- | ||
583 | /// </summary> |
- | ||
584 | /// <param name="form">the form to check</param> |
- | ||
585 | /// <remarks>https://stackoverflow.com/questions/987018/determining-if-a-form-is-completely-off-screen</remarks> |
- | ||
586 | /// <returns>false if the form is entirely or partially offscreen</returns> |
- | ||
587 | public bool IsFormOnScreen(Form form) |
- | ||
588 | { |
- | ||
589 | foreach (Screen screen in Screen.AllScreens) |
- | ||
590 | { |
- | ||
591 | Rectangle formRectangle = new Rectangle(form.Left, form.Top, form.Width, form.Height); |
- | ||
592 | |
- | ||
593 | if (screen.WorkingArea.Contains(formRectangle)) |
- | ||
594 | { |
- | ||
595 | return true; |
- | ||
596 | } |
- | ||
597 | } |
- | ||
598 | |
- | ||
599 | return false; |
562 | |
|
600 | } |
- | ||
601 | |
- | ||
602 | #endregion |
- | ||
603 | |
- | ||
604 | private void Toast_MouseEnter(object sender, EventArgs e) |
- | ||
605 | { |
- | ||
606 | _toastTimer.Stop(); |
- | ||
607 | } |
- | ||
608 | |
- | ||
609 | private void Toast_MouseLeave(object sender, EventArgs e) |
- | ||
610 | { |
563 | #region Private Methods |
|
611 | _toastTimer.Start(); |
564 | |
|
612 | } |
565 | #endregion |