Toasts – Rev 8

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.Generic;
using System.Drawing;
using System.IO;
using System.Media;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;

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

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

        #endregion

        #region Public Methods

        /// <summary>
        ///     Displays the form
        /// </summary>
        /// <remarks>
        ///     Required to allow the form to determine the current foreground window before being displayed
        /// </remarks>
        public new void Show()
        {
            // Determine the current foreground window so it can be reactivated each time this form tries to get the focus
            _currentForegroundWindow = NativeMethods.GetForegroundWindow();

            base.Show();
        }

        #endregion

        #region Private Methods

        private static async Task<byte[]> LoadResource(string resource)
        {
            var assembly = Assembly.GetExecutingAssembly();

            using (var manifestResourceStream = assembly.GetManifestResourceStream(resource))
            {
                if (manifestResourceStream == null)
                {
                    return null;
                }

                var memoryStream = new MemoryStream();

                await manifestResourceStream.CopyToAsync(memoryStream);

                memoryStream.Position = 0L;

                return memoryStream.ToArray();
            }
        }

        #endregion

        #region Constructors, Destructors and Finalizers

                private ToastForm()
        {
            InitializeComponent();
        }
        
        /// <summary>
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="duration"></param>
        /// <param name="image"></param>
        /// <param name="animation"></param>
        /// <param name="direction"></param>
        public ToastForm(string title, string body, int duration, Image logo, Image background, byte[] sound,
            FormAnimator.AnimationMethod animation, FormAnimator.AnimationDirection direction) : this()
        {
            lifeTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;
            BackgroundImage = background;

            _sound = sound;

            _animator = new FormAnimator(this, animation, direction, 500);

            Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20));
        }

        public ToastForm(string title, string body, int duration, Image logo, byte[] sound,
            FormAnimator.AnimationMethod animation, FormAnimator.AnimationDirection direction) : this()
        {
            lifeTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;

            _sound = sound;

            _animator = new FormAnimator(this, animation, direction, 500);

            Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20));
        }

        public ToastForm(string title, string body, int duration, Image logo, byte[] sound) : this()
        {
            lifeTimer.Interval = duration;
            labelTitle.Text = title;
            labelBody.Text = body;
            imageBox.Image = logo;

            _sound = sound;

            _animator = new FormAnimator(this, FormAnimator.AnimationMethod.Slide, FormAnimator.AnimationDirection.Up,
                500);

            Region = Region.FromHrgn(NativeMethods.CreateRoundRectRgn(0, 0, Width - 5, Height - 5, 20, 20));
        }

        /// <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))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

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

        private readonly FormAnimator _animator;

        private bool _allowFocus;

        private IntPtr _currentForegroundWindow;

        private byte[] _sound;

        #endregion

        #region Event Handlers

        private async void Notification_Load(object sender, EventArgs e)
        {
            // Play the sound.
            if (_sound == null)
            {
                _sound = await LoadResource($"Toasts.Sounds.default.wav");
            }

            using (var memoryStream = new MemoryStream(_sound))
            {
                using (var player = new SoundPlayer(memoryStream))
                {
                    player.Play();
                }
            }

            // Display the form just above the system tray.
            Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - Width,
                Screen.PrimaryScreen.WorkingArea.Height - Height);

            // Move each open form upwards to make room for this one
            foreach (var openForm in OpenNotifications)
            {
                if (openForm.InvokeRequired)
                {
                    Action action = delegate { openForm.Top -= Height; };
                    openForm.Invoke(action);

                    continue;
                }

                openForm.Top -= Height;
            }

            OpenNotifications.Add(this);
            lifeTimer.Start();
        }

        private void Notification_Activated(object sender, EventArgs e)
        {
            // Prevent the form taking focus when it is initially shown
            if (!_allowFocus)
            {
                // Activate the window that previously had focus
                NativeMethods.SetForegroundWindow(_currentForegroundWindow);
            }
        }

        private void Notification_Shown(object sender, EventArgs e)
        {
            // Once the animation has completed the form can receive focus
            _allowFocus = true;

            // Close the form by sliding down.
            _animator.Duration = 0;
            _animator.Direction = FormAnimator.AnimationDirection.Down;
        }

        private void Notification_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Move down any open forms above this one
            foreach (var openForm in OpenNotifications)
            {
                if (openForm == this)
                    // Remaining forms are below this one
                    break;

                if (openForm.InvokeRequired)
                {
                    Action action = delegate { openForm.Top += Height; };
                    openForm.Invoke(action);

                    continue;
                }
            }

            OpenNotifications.Remove(this);
        }

        private void LifeTimer_Tick(object sender, EventArgs e)
        {
            Close();
        }

        private void Notification_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void LabelTitle_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void LabelRO_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion
    }
}

Generated by GNU Enscript 1.6.5.90.