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)
其余邏輯實現保持不變。
使用更簡單,如下圖示:
Winform應用程序實現通用遮罩層二