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


之前先后發表過:《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)

其余邏輯實現保持不變。

使用更簡單,如下圖示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM