Smart Notifier for Executables | Toast for .NET

0
66

Introduction

The aim of this library is to provide a new and simple solution to represent the notifier message of an application, instead of using the classic MessageBox.

MessageBox is a very powerful message notifier, but it shows a lack of style and practice. A lot of customers require a UI that is in line with today’s market.

Another aim is to get a notification that is easy to update in its content: sometimes, it is necessary to put a MessageBox in a loop, to get (for instance) a fast error report. This often causes a waterfall of message notifications. In this case, we need a notification that can stay opened, on top, in a comfortable position and with a updatable content, thus it is possible to update an hypothetical error counter.

The features

  • Up to four simple toast type with respective colors:
    • Info
    • Error
    • Warning
    • Confirm
  • Draggable Notifier;
  • DialogStyle Notifier;
  • Positionable Notifier;
  • Message with the same content/title are now handled as multi-notes: you will see an update counter in the title of the note;
  • Full Screen faded background (experimental);
  • Added SimpleLogger: an custom logger to VS Console or File.

Using the Code

To use this code, simply insert a reference to the Notifier.dll library to your project:


using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;


using Notify;

Then, to create a note, simply:

Notifier.Show("Note text", Notifier.Type.INFO, "TitleBar");

You have to indicate the text of the note, the type and the title. Also, it is possible to use a simple call, with only the text (“Info” as the default type).

You can choose between 4 styles:

  1. Warning
  2. Ok
  3. Error
  4. Info

preview

The notifications are stacked to the right bottom corner of the active screen.

In the above image you can see a key-feature of this notification library: you can now handle the same note as one note with a counter updates.

Also the creation call return the ID of the note. We can use this ID to change the notification content:

short ID = Notifier.Show("Note text", Notifier.Type.INFO, "TitleBar");

Then, it is possible to use this ID to refer to the opened notification to update its content:

Notifier.Update(ID, "New Note text", Notifier.Type.OK, "New TitleBar");

Briefly:


short ID = Notifier.Show("Hello World");


Notifier.Update(ID, "Hello Mars");

It’s also introduced the “Close All” function: to access it open the notification menu by pressing the arrow icon left to the close icon:

In the opened menu, select “Close All” to close all the opened notification; this will also reset the ID counter.

Watching the Code

The Notifier.dll library is made of a Windows Form and its resource file. In the resource file are stored the background image of the note and the icons.

GLOBALS

Let’s start from the GLOBALS part:

    
    public enum BackDialogStyle {None, FadedScreen, FadedApplication }

 #region GLOBALS
        
        public enum Type { INFO, WARNING, ERROR, OK }
        
        
        class PosXY{
            internal int X;
            internal int Y;
            
            internal Point firstPoint;
            internal bool mouseIsDown = false;

            public PosXY(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }
        static List<PosXY> posXY = new List<PosXY>();

        
        static List<Toast> toasts = new List<Toast>();
        
        static short ID = 0x00;

        
        private String description = "";
        private String title = "Toast";
        private PosXY myPos;
        Type type = Type.INFO;
        
        
        bool isDialog;
        BackDialogStyle backDialogStyle = BackDialogStyle.None;
        private Form myCallerApp;
    #endregion

That part includes the helper class used to save the position of the note and its information.

The…

static List<PosXY> posXY = new List<PosXY>(); static List<Notifier> notifications = new List<Notifier>(); static short ID = 0x00;

…is used to store the positions of all the notifications, because every time a note is created, it is necessary to check the available position to show it in an empty space. To save the notifications, we use a static container and a static ID counter to be sure that each Notifier as its ID.

CREATE & DRAW

In the OnLoad method, called on the form creation:

BackColor = Color.Blue;
TransparencyKey = Color.Blue;
FormBorderStyle = FormBorderStyle.None;

We set the transparency of the form, so we can use a custom background to achieve a better smooth effect and a transparent border.

this.Tag = "__Toast|"+ ID.ToString("X4");

This TAG is used to identify the notification type of the form. It is helpful for the “Close All” operation.

To set the content of the note, this function is used:

 private void setToast(string _desc, Type noteType, string _title)
        {
            
            titleText.Text = _title;
            labelDesc.Text = _desc;
            date.Text = DateTime.Now + "";

            #region ADJUST COLORS
            Color Hover = Color.FromArgb(0, 0, 0);
            Color Leave = Color.FromArgb(0, 0, 0);

            switch (noteType)
            {
                case Type.ERROR:
                    icon.Image = global::Notify.Properties.Resources.ko;
                    Leave = Color.FromArgb(200, 60, 70);
                    Hover = Color.FromArgb(240, 80, 90);
                    break;
                case Type.INFO:
                    icon.Image = global::Notify.Properties.Resources.info;
                    Leave = Color.FromArgb(90, 140, 230);
                    Hover = Color.FromArgb(110, 160, 250);
                    break;
                case Type.WARNING:
                    icon.Image = global::Notify.Properties.Resources.warning;
                    Leave = Color.FromArgb(200, 200, 80);
                    Hover = Color.FromArgb(220, 220, 80);
                    break;
                case Type.OK:
                    icon.Image = global::Notify.Properties.Resources.ok;
                    Leave = Color.FromArgb(80, 200, 130);
                    Hover = Color.FromArgb(80, 240, 130);
                    break;
            }

            
            buttonClose.BackColor = Leave;
            buttonMenu.BackColor = Leave;
            titleText.BackColor = Leave;

            
            this.buttonClose.MouseHover += (s, e) =>
            {
                this.buttonClose.BackColor = Hover;
                this.buttonMenu.BackColor = Hover;
                this.titleText.BackColor = Hover;
            };
            this.buttonMenu.MouseHover += (s, e) =>
            {
                this.buttonMenu.BackColor = Hover;
                this.buttonClose.BackColor = Hover;
                this.titleText.BackColor = Hover;
            }; this.titleText.MouseHover += (s, e) =>
            {
                this.buttonMenu.BackColor = Hover;
                this.buttonClose.BackColor = Hover;
                this.titleText.BackColor = Hover;
            };

            
            this.buttonClose.MouseLeave += (s, e) =>
            {
                this.buttonClose.BackColor = Leave;
                this.buttonMenu.BackColor = Leave;
                this.titleText.BackColor = Leave;
            };
            this.buttonMenu.MouseLeave += (s, e) =>
            {
                this.buttonMenu.BackColor = Leave;
                this.buttonClose.BackColor = Leave;
                this.titleText.BackColor = Leave;
            };
            this.titleText.MouseLeave += (s, e) =>
            {
                this.buttonMenu.BackColor = Leave;
                this.buttonClose.BackColor = Leave;
                this.titleText.BackColor = Leave;
            };

            if (isDialog)
            {
                
                Button ok_button = new Button();
                ok_button.FlatStyle = FlatStyle.Flat;
                ok_button.BackColor = Leave;
                ok_button.ForeColor = Color.White;
                this.Size = new Size(this.Size.Width, this.Size.Height + 50);
                ok_button.Size = new Size(80, 40);
                ok_button.Location = new Point(this.Size.Height /2 + 40, this.Size.Height - 50);
                ok_button.Text = "OK";
                ok_button.Click += onClickButtonOK;
                this.Controls.Add(ok_button);
            }
            #endregion

            #region ADJUST LOCATION
            Rectangle rec = Screen.GetWorkingArea(Screen.PrimaryScreen.Bounds);

            
            
            int maxNot = rec.Height / this.Height;
            int x_Pos = rec.Width - this.Width;

            
            int x_Shift = 25;                   
            int n_columns = 0;
            int n_max_columns = rec.Width / x_Shift;
            bool add = false;
            if (!isDialog)
            {
                myPos = new PosXY(x_Pos, rec.Height - (this.Height * 1));

                while (!add)
                {
                    for (int n_not = 1; n_not <= maxNot; n_not++)
                    {
                        myPos = new PosXY(x_Pos, rec.Height - (this.Height * n_not));

                        if (!posXYContains(myPos))
                        {
                            add = true; break;
                        }

                        
                        if (n_not == maxNot)
                        {
                            n_columns++;
                            n_not = 0;
                            x_Pos = rec.Width - this.Width - x_Shift * n_columns;
                        }

                        
                        if (n_columns >= n_max_columns)
                        {
                            add = true; break;
                        }
                    }
                }
            }
            else
            {
                
                switch (backDialogStyle)
                {
                    case BackDialogStyle.FadedScreen:
                    case BackDialogStyle.None:
                        
                        int X = 0, Y = 0;
                        X = (rec.Width - this.Width) / 2;
                        Y = (rec.Height - this.Height) / 2;
                        myPos = new PosXY(X, Y);
                        break;
                    case BackDialogStyle.FadedApplication:
                        
                        int px = myCallerApp.Location.X + myCallerApp.Size.Width / 2;
                        int py = myCallerApp.Location.Y + myCallerApp.Size.Height / 2;
                        px = px - this.Width / 2;
                        py = py - this.Height / 2;
                        myPos = new PosXY(px, py);
                        break;
                }
            }

            
            this.Location = new Point(myPos.X, myPos.Y);

            posXY.Add(myPos);
            #endregion
        }

That simply set all the notification elements with the desired content. It is also used in the update part. There are 2 mains sections:

  1. ADJUST COLOR
  2. ADJUST LOCATIONS

Those are introduced for the dialog style toast. In the dialog style mode the toast will have a button inside the form to close the toast. Optionaly it will have a faded black background (witch is currently not possible with a simple messageBox).

In the OnLoad, we find a place for our notification:

Rectangle rec = Screen.GetWorkingArea(this); int maxNot = rec.Height / this.Height;
int x_Pos = rec.Width - this.Width; int x_Shift = 25; int n_columns = 0;
int n_max_columns = rec.Width / x_Shift; myPos = new PosXY(x_Pos, rec.Height - (this.Height * 1)); bool add = false;
while (!add)
{ for (int n_not = 1; n_not <= maxNot; n_not++) { myPos = new PosXY(x_Pos, rec.Height - (this.Height * n_not)); if (!posXYContains(myPos) ) { add = true; break; } if (n_not == maxNot) { n_columns++; n_not = 0; x_Pos = rec.Width - this.Width - x_Shift * n_columns; } if (n_columns >= n_max_columns) { add = true; break; } }
} this.Location = new Point(myPos.X, myPos.Y);
posXY.Add(myPos);

The last part gets a valid position in the screen for the note.

  1. First, we get the screen area and calculate the available grid dimension (row and columns).
  2. Then, we start the position cycle: if a column is full, we shift the notification across the X axis of the screen by a custom value (no notifications overlay).
  3. If the entire screen is full, then create the notifications in the last used place.

In the end, we save the notification position: it’s a list used in the cycle above.

Let’s see the interesting part, the Update function.

UPDATE

To update the notification, as previously said, we need its ID.

With the note ID, it is simple to find the needed notification and update it:

public static void Update(short ID, string p1, Type noteType, string p2)
{ for (int i = 0; i < notifications.Count; i++) { if (notifications[i].Tag != null && notifications[i].Tag.Equals("__Notifier|" + ID.ToString("X4"))) { Notifier myNote = (Notifier)notifications[i]; myNote.setNotification(p1, noteType, p2); } }
}

We cycle in the notifications list and check for the ID set in the TAG properties.

Simple Logger

To get more power regarding the notification to the user it is possible to use the included logger, called SimpleLogger.

A very basilar logger for .NET. It is possible to choose a filename for the log and the level of logging message.

In your class include the logger:

Logger logger = new Logger();

…or specify a filename:

Logger logger = new Logger("myManager.log");

OPTIONAL: Set the log level (Remember: CRITICAL < ERROR < WARNING < INFO < VERBOSE) so if you set the level to WARNING, you will have in the log the CRITICAL, ERROR and WARNING if you set the level to CRITICAL, you will have only CRITICAL in the log Default value is VERBOSE.

logger.setLoggingLevel(Logger.LEVEL.INFO);

Use it:

logger.log(Logger.LEVEL.ERROR, e.StackTrace);

Points of Interest

Some of the future developments are as follows:

  • Temporary note: autoclose of the note after sometimes
  • Dialog style note: complete version
  • Configurable position of the note (X, Y, Corner of the screen)
  • Complete multiscreen support

History

  • 25/08/2016: v1.0 – First version release
  • 01/09/2016: v1.1 – Changed the caller style in a static way, added the Close All function and the notification ID: it is possible now to recall and change the content of a opened notification
  • 10/07/2017: v1.2 – Added the features as showDialog, draggable Notifier. Improved the graphics part, update counters of the same note. Added the SimpleLogger;

LEAVE A REPLY