Toasts – Rev 17

Subversion Repositories:
Rev:
// =====COPYRIGHT=====
// Code originally retrieved from http://www.vbforums.com/showthread.php?t=547778 - no license information supplied
// =====COPYRIGHT=====

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Media;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Toasts.Properties;

namespace Toasts
{
    public partial class ToastForm : Form
    {
        #region Static Fields and Constants

        private static readonly object OpenNotificationsLock = new object();

        private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>();

        #endregion

        #region Private Overrides

        protected override bool ShowWithoutActivation => true;

        protected override CreateParams CreateParams
        {
            get
            {
                var baseParams = base.CreateParams;

                const int WS_EX_NOACTIVATE = 0x08000000;
                const int WS_EX_TOOLWINDOW = 0x00000080;
                const int WS_EX_TOPMOST = 0x00000008;
                baseParams.ExStyle |= WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;

                return baseParams;
            }
        }

        #endregion

        #region Constructors, Destructors and Finalizers

        private ToastForm()
        {
            InitializeComponent();
            Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20));
            
            _screenWidth = Screen.PrimaryScreen.WorkingArea.Width;
            _screenHeight = Screen.PrimaryScreen.WorkingArea.Height;

            _formAnimator = new FormAnimator(this, FormAnimator.AnimationMethod.Slide, FormAnimator.AnimationDirection.Up,
                500);
            
            using (var memoryStream = new MemoryStream())
            {
                Resources.normal.CopyTo(memoryStream);

                memoryStream.Position = 0L;

                _sound = memoryStream.ToArray();
            }

            _toastTimer = new System.Timers.Timer();
            _toastTimer.Elapsed += ToastTimer_Elapsed;
            _toastTimer.Enabled = false;
        }

        /// <summary>
        /// Display a toast with a title, body and a logo indefinitely on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="logo">the toast logo</param>
        public ToastForm(string title, string body, Image logo) : this()
        {
            _toastTimer.Enabled = false;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;
        }

        /// <summary>
        /// Display a toast on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="duration">the duration in milliseconds to display the toast on screen for</param>
        /// <param name="logo">the toast logo</param>
        /// <param name="background">the background image for the toast</param>
        /// <param name="sound">a sound to play when the toast is displayed</param>
        /// <param name="animation">the form animation type <see cref="FormAnimator"/></param>
        /// <param name="direction">the form animation direction <see cref="FormAnimator"/></param>
        public ToastForm(string title, string body, int duration, Image logo, Image background, byte[] sound,
            FormAnimator.AnimationMethod animation, FormAnimator.AnimationDirection direction) : this()
        {
            _toastTimer.Enabled = true;
            _toastTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;
            BackgroundImage = background;

            _sound = sound;

            _formAnimator = new FormAnimator(this, animation, direction, 500);
        }

        /// <summary>
        /// Display a toast on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="duration">the duration in milliseconds to display the toast on screen for</param>
        /// <param name="logo">the toast logo</param>
        /// <param name="sound">a sound to play when the toast is displayed</param>
        /// <param name="animation">the form animation type <see cref="FormAnimator"/></param>
        /// <param name="direction">the form animation direction <see cref="FormAnimator"/></param>
        public ToastForm(string title, string body, int duration, Image logo, byte[] sound,
            FormAnimator.AnimationMethod animation, FormAnimator.AnimationDirection direction) : this()
        {
            _toastTimer.Enabled = true;
            _toastTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;

            _sound = sound;

            _formAnimator = new FormAnimator(this, animation, direction, 500);
        }

        /// <summary>
        /// Display a toast on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="duration">the duration in milliseconds to display the toast on screen for</param>
        /// <param name="logo">the toast logo</param>
        /// <param name="sound">a sound to play when the toast is displayed</param>
        public ToastForm(string title, string body, int duration, Image logo, byte[] sound) : this()
        {
            _toastTimer.Enabled = true;
            _toastTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;

            _sound = sound;
        }

        /// <summary>
        /// Display a toast on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="duration">the duration in milliseconds to display the toast on screen for</param>
        /// <param name="logo">the toast logo</param>
        public ToastForm(string title, string body, int duration, Image logo) : this()
        {
            _toastTimer.Enabled = true;
            _toastTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;
        }

        /// <summary>
        /// Display a toast on screen.
        /// </summary>
        /// <param name="title">the toast title</param>
        /// <param name="body">the toast body</param>
        /// <param name="duration">the duration in milliseconds to display the toast on screen for</param>
        /// <param name="logo">the toast logo</param>
        public ToastForm(string title, string body, int duration, Icon logo) : this()
        {
            _toastTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo.ToBitmap();
        }

        /// <summary>
        ///     Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && components != null)
            {
                if (_toastTimer != null)
                {
                    _toastTimer.Dispose();
                    _toastTimer = null;
                }

                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

        #region Private Delegates, Events, Enums, Properties, Indexers and Fields

        private readonly FormAnimator _formAnimator;

        private readonly byte[] _sound;

        private System.Timers.Timer _toastTimer;

        private readonly int _screenWidth;

        private readonly int _screenHeight;

        #endregion

        #region Event Handlers

        private void Notification_Load(object sender, EventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                using (var memoryStream = new MemoryStream(_sound))
                {
                    using (var player = new SoundPlayer(memoryStream))
                    {
                        player.Play();
                    }
                }
            });

            try
            {
                lock (OpenNotificationsLock)
                {
                    var notifications = OpenNotifications.ToArray();

                    foreach (var openForm in notifications)
                    {
                        if (openForm == null)
                        {
                            continue;
                        }

                        if (openForm.IsDisposed)
                        {
                            OpenNotifications.Remove(openForm);

                            continue;
                        }

                        if (openForm.IsHandleCreated != true)
                        {
                            continue;
                        }

                        // Move each open form upwards to make room for this one
                        var handle = Handle;
                        openForm.BeginInvoke(new MethodInvoker(() =>
                        {
                            try
                            {
                                if (openForm.Handle == handle)
                                {
                                    return;
                                }

                                openForm.Top -= Height;
                            }
                            catch
                            {
                                Debug.WriteLine("Error while moving forms up.");
                            }
                        }));
                    }

                    Invoke(new MethodInvoker(() =>
                    {
                        Location = new Point(_screenWidth - Width, _screenHeight - Height);
                    }));

                    OpenNotifications.Add(this);

                    // Only start the timer if it has been enabled in the constructor.
                    if (_toastTimer.Enabled)
                    {
                        _toastTimer.Start();
                    }
                }
            }
            catch
            {
                Debug.WriteLine("Error on form load event.");
            }
        }

        private void Notification_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                lock (OpenNotificationsLock)
                {
                    OpenNotifications.Remove(this);
                }
            }
            catch
            {
                Debug.WriteLine("Error in form closed event.");
            }
        }

        private void Notification_Shown(object sender, EventArgs e)
        {
            // Reverse animation direction for hiding.
            switch (_formAnimator.Direction)
            {
                case FormAnimator.AnimationDirection.Up:
                    _formAnimator.Direction = FormAnimator.AnimationDirection.Down;
                    break;
                case FormAnimator.AnimationDirection.Down:
                    _formAnimator.Direction = FormAnimator.AnimationDirection.Up;
                    break;
                case FormAnimator.AnimationDirection.Left:
                    _formAnimator.Direction = FormAnimator.AnimationDirection.Right;
                    break;
                case FormAnimator.AnimationDirection.Right:
                    _formAnimator.Direction = FormAnimator.AnimationDirection.Left;
                    break;
            }
        }

        private void ToastTimer_Elapsed(object sender, EventArgs e)
        {
            if (IsDisposed)
            {
                return;
            }

            try
            {
                _toastTimer.Stop();
                BeginInvoke(new MethodInvoker(() =>
                {
                    lock (OpenNotificationsLock)
                    {
                        Close();
                    }
                }));
            }
            catch
            {
                Debug.WriteLine("Error in timer elapsed event.");
            }
        }

        private void Toast_Click(object sender, EventArgs e)
        {
            try
            {
                lock (OpenNotificationsLock)
                {
                    Close();
                }
            }
            catch
            {
                Debug.WriteLine("Error in form click event.");
            }
        }

        #endregion
    }
}

Generated by GNU Enscript 1.6.5.90.