首頁 > 軟體

C# Winform實現自定義漂亮的通知效果

2022-08-29 14:02:35

前言

本文主要介紹其具體的實現思路(視訊僅有程式碼輸入,並無過程介紹等),同時,在原本實現的基礎上,進行了多處修改和優化,具體參見下面的內容。

優化調整

下面是對原始碼的修改、優化和調整:

  • 修改 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個訊息方塊時,發生替換的效果;

// 設定通知的數量
Form_Alert.AlertFormNum = 5;
Form_Alert.MoveEntry = false;// 不水平移動進入
  • 調整下圖示位置,垂直方向居中一些

水平移動進入的效果(預設):

  • 新增顯示時指定訊息字型的引數,有需要可直接修改顯示文字的字型。
/// <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())));

主要實現過程

  • 建立一個無邊框表單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);
        }
    }
}

到此這篇關於C# Winform實現自定義漂亮的通知效果的文章就介紹到這了,更多相關C# Winform通知效果內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com