1. 程式人生 > >Winform應用程序實現通用遮罩層

Winform應用程序實現通用遮罩層

圖片 ted containe completed ini spa mst per 動圖

Winform應用程序實現通用遮罩層

在WEB上,我們在需要進行大數據或復雜邏輯處理時,由於耗時較長,一般我們會在處理過程中的頁面上顯示一個半透明的遮罩層,上面放個圖標或提示:正在處理中...等字樣,這樣用戶體驗就比較好了,然而如果在Winform客戶端程序,通常遮罩層的處理就顯得不那麽簡單或不那麽好看,而我今天要說明的是,我實現的這個Winform通用遮罩層,卻可以實現類似WEB上的遮罩層,既可以透明,而且還可以顯示動態圖片以及文字,那如何實現的呢,我現在一一講解。

首先要明確我們要實現的效果:透明+動態圖標+文字

透明:這個簡單,只需要將窗體的Opacity設為100%以下的值就可以了,這裏我采用85%;

動態圖標:這個相對復雜一些,因為Winform目前沒有現成的支持直接顯示動圖的控件,但幸好有一個組件ImageAnimator支持逐幀動畫,我們只需要將圖片綁定到ImageAnimator的Animate方法上(即:ImageAnimator.Animate(m_Image,EventHandler委托);),然後重寫窗體的OnPaint即可,具體的代碼實現見下面公布的源碼。

文字:這個簡單,放在一個Label控件即可

還有為了能夠讓圖標與文字在相對的位置(即不論大小)保持居中,我這裏采用了一個TableLayoutPanel,分成兩行,上行放置Label,並設為居中,下行放置Panel,提供繪制動圖的區域。

完整代碼實現如下(部份代碼參考網絡上它人的文章):

技術分享
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace TEMS
{
    public partial class FrmProcessing : Form
    {
        private static Image m_Image = null;

        private EventHandler evtHandler = null;

        private ParameterizedThreadStart workAction = null;
        private object workActionArg = null;

        private Thread workThread = null;

        public string Message
        {
            get
            {
                return lbMessage.Text;
            }
            set
            {
                lbMessage.Text = value;
            }
        }

        public bool WorkCompleted = false;

        public Exception WorkException
        { get; private set; }

        public void SetWorkAction(ParameterizedThreadStart workAction, object arg)
        {
            this.workAction = workAction;
            this.workActionArg = arg;
        }

        public FrmProcessing(string msg)
        {
            InitializeComponent();
            this.Message = msg;
        }

        protected override  void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (m_Image != null)
            {
                //獲得當前gif動畫下一步要渲染的幀。
                UpdateImage();

                //將獲得的當前gif動畫需要渲染的幀顯示在界面上的某個位置。
                int x = (int)(panImage.ClientRectangle.Width - m_Image.Width) / 2;
                int y = 0;
                //e.Graphics.DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height));
                panImage.CreateGraphics().DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height));
            }
            if (this.WorkCompleted)
            {
                this.Close();
            }
        }


        private void FrmProcessing_Load(object sender, EventArgs e)
        {
            if (this.Owner != null)
            {
                this.StartPosition = FormStartPosition.Manual;
                this.Location = new Point(this.Owner.Left, this.Owner.Top);
                //MessageBox.Show(string.Format("X={0},Y={1}", this.Owner.Left, this.Owner.Top));
                this.Width = this.Owner.Width;
                this.Height = this.Owner.Height;
            }
            else
            {
                Rectangle screenRect = Screen.PrimaryScreen.WorkingArea;
                this.Location = new Point((screenRect.Width - this.Width) / 2, (screenRect.Height - this.Height) / 2);
            }

            //為委托關聯一個處理方法
            evtHandler = new EventHandler(OnImageAnimate);

            if (m_Image == null)
            {
                Assembly assy = Assembly.GetExecutingAssembly();
                //獲取要加載的gif動畫文件
                m_Image = Image.FromStream(assy.GetManifestResourceStream(assy.GetName().Name + ".Resources.loading2.gif"));
            }
            //調用開始動畫方法
            BeginAnimate();
        }


        //開始動畫方法

        private void BeginAnimate()
        {
            if (m_Image != null)
            {
                //當gif動畫每隔一定時間後,都會變換一幀,那麽就會觸發一事件,該方法就是將當前image每變換一幀時,都會調用當前這個委托所關聯的方法。
                ImageAnimator.Animate(m_Image, evtHandler);
            }
        }

        //委托所關聯的方法

        private void OnImageAnimate(Object sender, EventArgs e)
        {
            //該方法中,只是使得當前這個winform重繪,然後去調用該winform的OnPaint()方法進行重繪)
            this.Invalidate();
        }

        //獲得當前gif動畫的下一步需要渲染的幀,當下一步任何對當前gif動畫的操作都是對該幀進行操作)

        private void UpdateImage()
        {
            ImageAnimator.UpdateFrames(m_Image);
        }

        //關閉顯示動畫,該方法可以在winform關閉時,或者某個按鈕的觸發事件中進行調用,以停止渲染當前gif動畫。

        private void StopAnimate()
        {
            m_Image = null;
            ImageAnimator.StopAnimate(m_Image, evtHandler);
        }

        private void FrmProcessing_Shown(object sender, EventArgs e)
        {
            if (this.workAction != null)
            {
                workThread = new Thread(ExecWorkAction);
                workThread.IsBackground = true;
                workThread.Start();
            }
        }

        private void ExecWorkAction()
        {
            try
            {
                var workTask = new Task((arg) =>
                                {
                                    this.workAction(arg);
                                },
                            this.workActionArg);

                workTask.Start();
                Task.WaitAll(workTask);
            }
            catch (Exception ex)
            {
                this.WorkException = ex;
            }
            finally
            {
                this.WorkCompleted = true;
            }

        }
    }
}
技術分享

以下是自動生成的代碼:

技術分享 技術分享
namespace TEMS
{
    partial class FrmProcessing
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <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);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.lbMessage = new System.Windows.Forms.Label();
            this.panImage = new System.Windows.Forms.Panel();
            this.tableLayoutPanel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 1;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.Controls.Add(this.lbMessage, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.panImage, 0, 1);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 2;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(582, 318);
            this.tableLayoutPanel1.TabIndex = 1;
            // 
            // lbMessage
            // 
            this.lbMessage.BackColor = System.Drawing.Color.Transparent;
            this.lbMessage.Dock = System.Windows.Forms.DockStyle.Fill;
            this.lbMessage.Font = new System.Drawing.Font("微軟雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
            this.lbMessage.Location = new System.Drawing.Point(3, 0);
            this.lbMessage.Name = "lbMessage";
            this.lbMessage.Padding = new System.Windows.Forms.Padding(0, 0, 0, 30);
            this.lbMessage.Size = new System.Drawing.Size(576, 159);
            this.lbMessage.TabIndex = 1;
            this.lbMessage.Text = "lbMessage\r\nadsfadsf";
            this.lbMessage.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
            // 
            // panImage
            // 
            this.panImage.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panImage.Location = new System.Drawing.Point(3, 162);
            this.panImage.Name = "panImage";
            this.panImage.Size = new System.Drawing.Size(576, 153);
            this.panImage.TabIndex = 2;
            // 
            // FrmProcessing
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.Control;
            this.ClientSize = new System.Drawing.Size(582, 318);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "FrmProcessing";
            this.Opacity = 0.85D;
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.Text = "FrmProcessing";
            this.Load += new System.EventHandler(this.FrmProcessing_Load);
            this.Shown += new System.EventHandler(this.FrmProcessing_Shown);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private System.Windows.Forms.Label lbMessage;
        private System.Windows.Forms.Panel panImage;


    }
}
技術分享

代碼中SetWorkAction方法是用來設置異步需要處理的委托方法,在窗體顯示出來後(FrmProcessing_Shown),創建新線程,用以處理耗時的邏輯代碼段,其中有一個WorkCompleted屬性,這個主要是表明處理耗時的邏輯代碼已完成(不論是否報錯),在窗體重繪時(OnPaint),會持續判斷該值是否為true,若為true則關閉當前窗口。

另之所以沒重寫Panel的OnPaint方法,原因是雖然可以顯示動圖,但由於局部重繪,造成動圖出現閃屏,所以仍需要采用窗體重繪

為了便於通用,我還定義了一個通用方法,專門用來顯示遮罩層窗體,方法定義如下:

技術分享
    public static class Common
    {

        public static void ShowProcessing(string msg, Form owner, ParameterizedThreadStart work, object workArg = null)
        {
            FrmProcessing processingForm = new FrmProcessing(msg);
            dynamic expObj = new ExpandoObject();
            expObj.Form = processingForm;
            expObj.WorkArg = workArg;
            processingForm.SetWorkAction(work, expObj);
            processingForm.ShowDialog(owner);
            if (processingForm.WorkException != null)
            {
                throw processingForm.WorkException;
            }
        }

        
    }
技術分享

現在使用就很簡單了,如下:

Common.ShowProcessing("正在處理中,請稍候...", this, (obj) =>
{
     //這裏寫處理耗時的代碼,代碼處理完成則自動關閉該窗口
},null);

使用效果如下:

技術分享

Winform應用程序實現通用遮罩層