C# Winform实现自定义漂亮的通知效果

作者:代码迷途??????? 时间:2021-08-10 08:15:29 

前言

本文主要介绍其具体的实现思路(视频仅有代码输入,并无过程介绍等),同时,在原本实现的基础上,进行了多处修改和优化,具体参见下面的内容。

优化调整

下面是对源代码的修改、优化和调整:

  • 修改 lblMsg(Label) 的 AutoSize 为false,尽可能多占通知窗体区域,Anchor跟随窗体变换,文字左侧垂直居中,用于显示可能更多的消息.

  • 设定action、timer1默认值,Name、Opacity、StartPosition(Manual)在构造函数中指定,这样就不用每次创建通知窗体时进行赋值了。ShowInTaskbar = false;通知窗体不在任务栏显示。

  • 将原有代码中定时器时间间隔调整到100毫秒,原设置为1,时间太短人眼看不出区别,白白浪费计算。

  • ShowNotice()改为静态方法,直接通过Form_Alert.ShowNotice(msg, msgType);调用显示窗体,不用new创建对象再调用。

  • AlertFormNum静态属性设置最多显示的通知数量,默认尽可能多的占满垂直屏幕,手动设置数量不能低于1或超出屏幕。

  • ShowTime静态属性设置完全显示后通知的显示时间,单位毫秒;也可以扩展渐变显示和消失的时间。

  • MoveEntry静态属性设置消息框是否水平移动进入,默认true。通过设置初始的消息框位置,即可实现水平移动进入。

  • 实现消息框占满后,新的消息框替换最近消失的通知的功能。原实现中最多只能显示10个通知框,当再多时不会显示(丢失掉),只有腾出位置(通知消失后)才能显示新的,现在已经优化为超出的通知框会替换掉旧通知,不会丢失。

下图为示例,后半段显示的内容是设置最多显示5个消息框时,发生替换的效果;

C# Winform实现自定义漂亮的通知效果

// 设置通知的数量
Form_Alert.AlertFormNum = 5;
Form_Alert.MoveEntry = false;// 不水平移动进入
  • 调整下图标位置,垂直方向居中一些

水平移动进入的效果(默认):

C# Winform实现自定义漂亮的通知效果

  • 添加显示时指定消息字体的参数,有需要可直接修改显示文字的字体。

/// <summary>
/// 设置完x、y之后执行初始化启动。设置位置、消息类型、显示、倒计时
/// </summary>
/// <param name="msg"></param>
/// <param name="msgType"></param>
/// <param name="msgFont">字体,默认不指定即可</param>
private void InitStart(string msg, MsgType msgType, Font msgFont = null)
{
   // ...
}

调用并显示自定义通知

新建项目NotificationCustom,完成通知框的调用显示

Form_Alert.ShowNotice("这是一条成功的消息", MsgType.Success);

Form_Alert.ShowNotice("警告!警告的消息", MsgType.Warning);

Form_Alert.ShowNotice("发生了错误,禁止!", MsgType.Error);

Form_Alert.ShowNotice("一条普通的信息记录", MsgType.Info);

或者显示时指定字体(下面为随机字体)

Form_Alert.ShowNotice("这是一条成功的消息", MsgType.Success, new Font(FontFamily.Families[random.Next(0, FontFamily.Families.Length)], (float)(10.0+10.0*random.NextDouble())));

C# Winform实现自定义漂亮的通知效果

主要实现过程

  • 创建一个无边框窗体Form_Alert,添加Label(lblMsg)显示通知消息,添加一个表示关闭的图片(PictureBox)。

  • 设置窗体StartPosition = FormStartPosition.Manual;,后面用于设置其初始位置为指定的屏幕右下角

  • 通过不同的背景颜色、不同的图片(icon,PictureBox)代表不同的消息类型(MsgType)

  • 定时器中通过定时时间完成消息窗的显示(透明度变化)、显示一定时间、关闭(逐渐透明)整个流程:定义消息窗体不同的操作(NotificationFormAction),start表示开始显示,显示窗体并在定时器中处理透明、移入的显示过程,完全显示后改变操作状态为wait;设置消息窗体显示等待的时间,操作状态变为close,定时时间之后再次执行定时器进入close处理;close过程中定时器执行变得透明、移出,完全透明后关闭定时器、关闭窗体。

  • 点击关闭按钮图标,窗体状态变为close,定时时间改为close的间隔100

  • 每次定时器执行函数的结尾记录下次执行的时间,用于判断当两个窗体的状态相同时,剩余执行时间为多少,判断哪个窗体最先消失,用于完成后面的消息通知太多时,新旧消息框的替换【不严谨,尤其在逐渐的显示和关闭过程中,有着多次的定时器循环,如果想要完全严格,可以考虑计算消息窗体最终消失的时间(消息框的状态,循环执行的剩余次数,每次的间隔时间综合计算)】

  • ShowNotice()静态方法显示消息框,直接传递要显示的消息和消息类型即可,分为Success,Warning,Error,Info四类,通过指定的 AlertFormNum 消息框数量(或默认数量),循环依次显示消息框,并启动定时器处理消息框的窗体状态:渐变显示(透明度)、显示一定时间(ShowTime)、渐变消失。循环中通过Application.OpenForms[fname]获取通知框窗体,如果没有获取到则创建新窗体,并执行显示,结束整个显示处理;在循环中记录已有窗体中最先消失的窗体;如果全部循环完,则说明所有数量的通知框都存在,则完成对最先消失的窗体的替换并显示新的消息窗体。

代码实现

修改后全部代码不到200行,如下,主要部分已经进行注释:

namespace CustomAlertBoxDemo
{
   public enum NotificationFormAction
   {
       start,
       wait,
       close
   }
   public enum MsgType
   {
       Success,
       Warning,
       Error,
       Info
   }
   public partial class Form_Alert : Form
   {
       /// <summary>
       /// 通知窗体的数量,默认为垂直屏幕几乎占满的数量
       /// </summary>
       private static int alertFormNum = Screen.PrimaryScreen.WorkingArea.Height / (75 + 5); // 75为窗体高度,如果调整窗体高度,记得修改此处
       /// <summary>
       /// 通知窗体的数量,默认为垂直屏幕几乎占满的数量,手动修改的数量不能超出屏幕和低于1,否则设置无效
       /// </summary>
       public static int AlertFormNum
       {
           get => alertFormNum;
           set
           {
               if (value <= Screen.PrimaryScreen.WorkingArea.Height / (75 + 5) && value > 0)
               {
                   alertFormNum = value;
               }
           }
       }
       /// <summary>
       /// 自定义通知的显示时间,单位为毫秒,默认为3分钟,之后开始消失。可根据需要修改
       /// </summary>
       public static int ShowTime { get; set; } = 3000;
       /// <summary>
       /// 是否移动进入,默认true
       /// </summary>
       public static bool MoveEntry { get; set; } = true;
       /// <summary>
       /// 创建通知窗体
       /// </summary>
       /// <param name="name">窗体名称,必须指定</param>
       public Form_Alert(string name)
       {
           InitializeComponent();
           Name = name;
           this.Opacity = 0.0;
           ShowInTaskbar = false;
           StartPosition = FormStartPosition.Manual;
       }
       private NotificationFormAction action = NotificationFormAction.start;
       /// <summary>
       /// 当前消息框的标准位置
       /// </summary>
       private int x, y;
       private void timer1_Tick(object sender, EventArgs e)
       {
           switch (this.action)
           {
               case NotificationFormAction.wait:
                   timer1.Interval = ShowTime;
                   action = NotificationFormAction.close;
                   break;
               case NotificationFormAction.start:
                   this.timer1.Interval = 100;
                   this.Opacity += 0.1;
                   if (this.x < this.Location.X)
                   {
                       this.Left-=20; // 移动快点
                   }
                   else
                   {
                       if (this.Opacity == 1.0)
                       {
                           action = NotificationFormAction.wait;
                       }
                   }
                   break;
               case NotificationFormAction.close:
                   timer1.Interval = 100;
                   this.Opacity -= 0.1;
                   this.Left -= 20;
                   if (base.Opacity == 0.0)
                   {
                       timer1.Stop();
                       base.Close();
                   }
                   break;
           }
           // tag记录下次执行的时间,用于后续的替换
           timer1.Tag = DateTime.Now.AddMilliseconds(timer1.Interval);
       }
       private void pictureBox2_Click(object sender, EventArgs e)
       {
           timer1.Interval = 100;
           action = NotificationFormAction.close;
       }
       /// <summary>
       /// 设置完x、y之后执行初始化启动。设置位置、消息类型、显示、倒计时
       /// </summary>
       /// <param name="msg"></param>
       /// <param name="msgType"></param>
       private void InitStart(string msg, MsgType msgType)
       {
           //this.Location = new Point(frm.x, frm.y);
           this.Location = new Point(x + (MoveEntry?Width / 2:0), y);
           switch (msgType)
           {
               case MsgType.Success:
                   pictureBox1.Image = Resources.success;
                   BackColor = Color.SeaGreen;
                   break;
               case MsgType.Error:
                   pictureBox1.Image = Resources.error;
                   BackColor = Color.DarkRed;
                   break;
               case MsgType.Info:
                   pictureBox1.Image = Resources.info;
                   BackColor = Color.RoyalBlue;
                   break;
               case MsgType.Warning:
                   pictureBox1.Image = Resources.warning;
                   BackColor = Color.DarkOrange;
                   break;
           }
           lblMsg.Text = msg;
           Show();
           timer1.Start();
       }
       public static void ShowNotice(string msg, MsgType msgType)
       {
           Form_Alert willDisappearFrm = null;
           for (int i = 1; i < alertFormNum+1; i++)
           {
               string fname = "alert" + i.ToString();
               Form_Alert frm = (Form_Alert)Application.OpenForms[fname];
               if (frm == null)
               {
                   frm = new Form_Alert(fname);
                   frm.x = Screen.PrimaryScreen.WorkingArea.Width - frm.Width - 5;
                   frm.y = Screen.PrimaryScreen.WorkingArea.Height - frm.Height * i - 5 * i;
                   // 设置完x、y之后执行初始化启动
                   frm.InitStart(msg, msgType);
                   return;
               }
               else
               {
                   if (willDisappearFrm == null)
                   {
                       willDisappearFrm = frm;
                   }
                   else
                   {
                       if (willDisappearFrm.action < frm.action)
                       {
                           willDisappearFrm = frm;
                       }
                       else if (willDisappearFrm.action == frm.action)
                       {
                           // 不考虑一次没执行的情况
                           if (willDisappearFrm.timer1.Tag!=null&& frm.timer1.Tag != null)
                           {
                               if (willDisappearFrm.timer1.Tag == null)
                               {
                                   willDisappearFrm = frm;
                               }
                               else if(frm.timer1.Tag != null)
                               {
                                   if ((DateTime)willDisappearFrm.timer1.Tag > (DateTime)frm.timer1.Tag)
                                   {
                                       willDisappearFrm = frm;
                                   }
                               }
                           }
                       }
                   }
               }
           }
           // 当前最早要消失的窗体willDisappearFrm被替换
           var newfrm = new Form_Alert(willDisappearFrm.Name);
           newfrm.x = Screen.PrimaryScreen.WorkingArea.Width - newfrm.Width - 5;
           newfrm.y = willDisappearFrm.Location.Y;
           // 必须立即替换name
           var totalNum = 0;
           foreach (Form form in Application.OpenForms)
           {
               if (form is Form_Alert)
               {
                   totalNum += 1;
               }
           }
           willDisappearFrm.Name = $"Form_Alert{totalNum + 1}";
           willDisappearFrm.pictureBox2_Click(null, null);
           // 设置完x、y之后执行初始化启动
           newfrm.InitStart(msg, msgType);
       }
   }
}

来源:https://juejin.cn/post/7135284656752033828

标签:C#,Winform,通知,效果
0
投稿

猜你喜欢

  • 使用Maven配置Spring的方法步骤

    2023-02-05 18:37:46
  • Java类和对象的设计原理

    2023-10-06 07:00:32
  • Java用for循环Map详细解析

    2021-06-08 08:23:03
  • 详解jvm对象的创建和分配

    2022-01-27 13:39:41
  • Java的后台文件夹下文件的遍历完整代码

    2023-09-18 12:32:19
  • Spring中的注解之@Override和@Autowired

    2022-08-07 19:56:47
  • Android自定义View实现水波纹引导动画

    2021-07-02 05:42:25
  • Android 图片处理缩放功能

    2023-09-29 13:59:54
  • Java中如何动态创建接口的实现方法

    2023-11-25 15:13:02
  • Java 使用Filter实现用户自动登陆

    2022-10-13 13:32:47
  • 在启动后台 jar包时,使用指定的 application.yml操作

    2023-01-08 20:10:12
  • JAVA格式化时间日期的简单实例

    2022-10-06 09:14:58
  • Java中Date日期时间类具体使用

    2022-04-11 23:18:13
  • java开发微信公众号支付

    2021-10-24 16:02:40
  • Android之ArcSlidingHelper制作圆弧滑动效果

    2021-07-23 03:10:24
  • JDBC查询Map转对象实现过程详解

    2021-08-30 07:17:40
  • java实现连连看游戏课程设计

    2023-10-30 13:18:37
  • springboot2.x 接入阿里云市场短信发送的实现

    2023-09-20 23:03:57
  • Android自定义View实现自动转圈效果

    2021-11-11 21:01:48
  • Android基础教程数据存储之文件存储

    2023-08-05 18:18:10
  • asp之家 软件编程 m.aspxhome.com