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

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

添加 str img 控件 text 邏輯 mar chang sha

之前先後發表過:《Winform應用程序實現通用遮罩層》、《Winform應用程序實現通用消息窗口》,這兩款遮罩層其實都是基於彈出窗口的,今天為大家分享一個比較簡單但界面相對友好的另一種實現方案,廢話不多說,直接進入主題。

一、實現思路(解決問題順序):

透明遮罩:

1.實現可設置透明的Panel控件(MaskPanel);

2.Panel控件(MaskPanel)能夠覆蓋父容器(一般是當前窗體form對象)客戶區區域(即:與父容器客戶區區域大小相同),並處於最上層,保證父容器上的任何控件都被蓋住並保證不可用;

3.Panel控件(MaskPanel)必需實現隨著父容器大小的改變而改變;

4.Panel控件(MaskPanel)上可呈現以表示正在加載的動圖或者文字,並且居中;

異步:

實現的方法有很多,比如異步委托、Task等,而這是在winform項目中,此次就直接使用BackgroundWorker

二、關鍵解決方案:

1.可設置透明控件:通過自定義控件,並重寫CreateParams(其中: cp.ExStyle |= 0x00000020;)、OnPaint(其中:labelBorderPen、labelBackColorBrush的Color=Color.FromArgb(_alpha, this.BackColor))兩個方法即可;

2.能夠覆蓋父容器客戶區區域:this.Size = this.Parent.ClientSize;this.Left = 0;this.Top = 0;

3.隨著父容器大小的改變而改變:this.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;

4.呈現以表示正在加載的動圖或者文字,並且居中:

添加PictureBox,設置Image為loading.gif動圖,SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; Point Location = new Point(this.Location.X + (this.Width - pictureBox_Loading.Width) / 2, this.Location.Y + (this.Height - pictureBox_Loading.Height) / 2);//居中

好了,最後貼出實現的源代碼:

MaskPanel:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 public partial class MaskPanel : Control { private System.ComponentModel.Container components = new System.ComponentModel.Container(); private bool _isTransparent = true;//是否透明 [Category("透明"), Description("是否使用透明,默認為True")] public bool IsTransparent { get { return _isTransparent; } set { _isTransparent = value; } } private int _alpha = 125;//設置透明度 [Category("透明"), Description("設置透明度")] public int Alpha { get { return _alpha; } set { _alpha = value; } } public MaskPanel(Control parent) : this(parent, 125) { } /// <summary> /// 初始化加載控件 /// </summary> /// <param name="Alpha"透明度</param> public MaskPanel(Control parent, int alpha) { SetStyle(ControlStyles.Opaque, true);//設置背景透明 base.CreateControl(); _alpha = alpha; parent.Controls.Add(this); this.Parent = parent; this.Size = this.Parent.ClientSize; this.Left = 0; this.Top = 0; this.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; this.BringToFront(); PictureBox pictureBox_Loading = new PictureBox(); pictureBox_Loading.BackColor = System.Drawing.Color.Transparent; pictureBox_Loading.Image = Properties.Resources.loading; pictureBox_Loading.Name = "pictureBox_Loading"; pictureBox_Loading.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; Point Location = new Point(this.Location.X + (this.Width - pictureBox_Loading.Width) / 2, this.Location.Y + (this.Height - pictureBox_Loading.Height) / 2);//居中 pictureBox_Loading.Location = Location; pictureBox_Loading.Anchor = AnchorStyles.None; this.Controls.Add(pictureBox_Loading); this.Visible = false; } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x00000020; // 開啟 WS_EX_TRANSPARENT,使控件支持透明 return cp; } } protected override void OnPaint(PaintEventArgs pe) { Pen labelBorderPen; SolidBrush labelBackColorBrush; if (_isTransparent) { Color cl = Color.FromArgb(_alpha, this.BackColor); labelBorderPen = new Pen(cl, 0); labelBackColorBrush = new SolidBrush(cl); } else { labelBorderPen = new Pen(this.BackColor, 0); labelBackColorBrush = new SolidBrush(this.BackColor); } base.OnPaint(pe); pe.Graphics.DrawRectangle(labelBorderPen, 0, 0, this.Width, this.Height); pe.Graphics.FillRectangle(labelBackColorBrush, 0, 0, this.Width, this.Height); } protected override void Dispose(bool disposing) { if (disposing) { if (!((components == null))) { components.Dispose(); } } base.Dispose(disposing); } }

為了實現通用,同時保證所有的窗體都有異步執行並顯示遮罩效果,故此處采用定義一個窗體基類:FormBase,裏面定義一個受保護的DoWorkAsync方法, 代碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public partial class FormBase : Form { public FormBase() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterParent; } /// <summary> /// 多線程異步後臺處理某些耗時的數據,不會卡死界面 /// </summary> /// <param name="workFunc">Func委托,包裝耗時處理(不含UI界面處理),示例:(o)=>{ 具體耗時邏輯; return 處理的結果數據 }</param> /// <param name="funcArg">Func委托參數,用於跨線程傳遞給耗時處理邏輯所需要的對象,示例:String對象、JObject對象或DataTable等任何一個值</param> /// <param name="workCompleted">Action委托,包裝耗時處理完成後,下步操作(一般是更新界面的數據或UI控件),示列:(r)=>{ datagirdview1.DataSource=r; }</param> protected void DoWorkAsync(Func<object, object> workFunc, object funcArg = null, Action<object> workCompleted = null) { var bgWorkder = new BackgroundWorker(); //Form loadingForm = null; Control loadingPan = null; bgWorkder.WorkerReportsProgress = true; bgWorkder.ProgressChanged += (s, arg) => { if (arg.ProgressPercentage > 1) return; #region Panel模式 var result = this.Controls.Find("loadingPan", true); if (result == null || result.Length <= 0) { loadingPan = new MaskPanel(this) { Name = "loadingPan" }; } else { loadingPan = result[0]; } loadingPan.BringToFront(); loadingPan.Visible = true; #endregion }; bgWorkder.RunWorkerCompleted += (s, arg) => { #region Panel模式 if (loadingPan != null) { loadingPan.Visible = false; } #endregion bgWorkder.Dispose(); if (workCompleted != null) { workCompleted(arg.Result); } }; bgWorkder.DoWork += (s, arg) => { bgWorkder.ReportProgress(1); var result = workFunc(arg.Argument); arg.Result = result; bgWorkder.ReportProgress(100); }; bgWorkder.RunWorkerAsync(funcArg); } }

使用示例如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private void button1_Click(object sender, EventArgs e) { int startNo = 20; button1.Enabled = false; this.DoWorkAsync((o) => //耗時邏輯處理(此處不能操作UI控件,因為是在異步中) { int result = 0; for (int i = 1; i <= Convert.ToInt32(o); i++) { result += i; Thread.Sleep(500); } return result; }, startNo, (r) => //顯示結果(此處用於對上面結果的處理,比如顯示到界面上) { label1.Text = r.ToString(); button1.Enabled = true; }); }

效果圖就不貼出來了,大家可以COPY上面的所有代碼,即可測試出效果。

2017年3月15日優化補充:

為了提高異步加載編碼的方便,特優化了DoWorkAsync方法,將返回值由object改為dynamic,這樣就比較方便,直接返回,直接使用

方法簽名如下:

protected void DoWorkAsync(Func<object, dynamic> workFunc, object funcArg = null, Action<dynamic> workCompleted = null)

其余邏輯實現保持不變。

使用更簡單,如下圖示:

技術分享

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