I have created NotificationForm to replace MessageBox, The notifications appear above each other, and wait 5 sec to disappear. Whenever a notification disappears the above ones go down to their new positions.
The only problem I got is when another thread creates a new notification, I got error of Cross-thread. I tried Invoke and HandleCreate and it didn't solve the problem.
Here is the code:
NotificationAlertForm
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using School.Properties;
namespace School.Forms
{
public partial class NotificationAlert : Form
{
public enum enmAction
{
wait,
start,
close
}
public enum enmType
{
Success,
Location,
Error,
Database
}
private static readonly List<NotificationAlert> activeAlerts = new List<NotificationAlert>();
private enmAction action;
private int x, y;
public NotificationAlert()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Interval = 1;
action = enmAction.close;
}
private void timer1_Tick(object sender, EventArgs e)
{
switch (action)
{
case enmAction.wait:
timer1.Interval = 5000;
action = enmAction.close;
break;
case enmAction.start:
timer1.Interval = 1;
Opacity += 0.1;
if (x < Location.X)
{
Left--;
}
else
{
if (Opacity == 1.0) action = enmAction.wait;
}
break;
case enmAction.close:
var newY = 0;
// Remove the current alert from the list of active alerts
activeAlerts.Remove(this);
// Start a timer to gradually update the positions of the remaining alerts
var positionTimer = new Timer();
positionTimer.Interval = 250;
positionTimer.Tick += (ssender, args) =>
{
// Update the position of each remaining alert
var offset = 0;
foreach (var alert in activeAlerts)
{
alert.Invoke((Action)delegate ()
{
newY = Screen.PrimaryScreen.WorkingArea.Height - alert.Height * (offset + 1) -
5 * offset;
if (alert.y != newY)
{
// Update the y-coordinate of the alert gradually in small steps
var step = Math.Sign(newY - alert.y) * 5;
if (Math.Abs(newY - alert.y) < Math.Abs(step))
alert.y = newY;
else
alert.y += step;
alert.Location = new Point(alert.x, alert.y);
}
offset++;
});
}
// Stop the timer when all positions are updated
if (activeAlerts.Count == 0 || activeAlerts.All(a => a.y == newY)) positionTimer.Stop();
};
positionTimer.Start();
// Fade out and close the current alert
timer1.Interval = 1;
Opacity -= 0.1;
Left -= 3;
if (Opacity == 0.0) Close();
break;
}
}
public void showAlert(string msg, enmType type)
{
// Add the new alert to the list of active alerts
activeAlerts.Add(this);
// Adjust the positions of all the active alerts
var offset = 0;
foreach (var alert in activeAlerts)
{
alert.y = Screen.PrimaryScreen.WorkingArea.Height - alert.Height * (offset + 1) - 5 * offset;
if (!alert.IsHandleCreated)
{
alert.CreateHandle();
}
alert.Invoke((Action)delegate ()
{
alert.Location = new Point(alert.x, alert.y);
offset++;
});
}
Opacity = 0.0;
StartPosition = FormStartPosition.Manual;
string fname;
for (var i = 1; i < 10; i++)
{
fname = "alert" + i;
var frm = (NotificationAlert)Application.OpenForms[fname];
if (frm == null)
{
Name = fname;
x = Screen.PrimaryScreen.WorkingArea.Width - Width + 15;
y = Screen.PrimaryScreen.WorkingArea.Height - Height * i - 5 * i;
Location = new Point(x, y);
break;
}
}
x = Screen.PrimaryScreen.WorkingArea.Width - Width - 5;
switch (type)
{
case enmType.Success:
pictureBox1.Image = Resources.icons8_ok_45px_1;
BackColor = Color.RoyalBlue;
break;
case enmType.Error:
pictureBox1.Image = Resources.icons8_error_45px_1;
BackColor = Color.DarkRed;
break;
case enmType.Database:
pictureBox1.Image = Resources.icons8_database_view_45px;
BackColor = Color.SeaGreen;
break;
case enmType.Location:
pictureBox1.Image = Resources.icons8_location_45px;
BackColor = Color.OrangeRed;
break;
}
msg_text.Text = msg;
TopMost = true;
Show();
action = enmAction.start;
timer1.Interval = 1;
timer1.Start();
}
}
}
NotificationAlertClass:
public static class NotificationAlertClass
{
public static void MessageSentSuccess(string msg)
{
var frm = new NotificationAlert();
frm.showAlert(msg, NotificationAlert.enmType.Success);
}
public static void LocationSentSuccess(string msg)
{
var frm = new NotificationAlert();
frm.showAlert(msg, NotificationAlert.enmType.Location);
}
public static void Error(string msg)
{
var frm = new NotificationAlert();
frm.showAlert(msg, NotificationAlert.enmType.Error);
}
public static void DatabaseSuccess(string msg)
{
var frm = new NotificationAlert();
frm.showAlert(msg, NotificationAlert.enmType.Database);
}
}
Tried MethodInvoke, it says that form handle must be created first, so I tried createHandle and still not works,
The only problem appears when another thread calls the class to start new Notificaiton