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)

其余逻辑实现保持不变。

使用更简单,如下图示:

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/6497142.html   ,如需转载请自行联系原作者

相关文章
|
C# 容器
Winform控件优化之TabControl控件的美化和功能扩展
在基本的TabControl控件使用和功能之上,可以尝试对其进行美化和功能扩展,比如动态删除或添加tab、绘制图标按钮及鼠标hover时的背景变化、Tab从右向左布局的优化处理等。最重要...
2099 0
Winform控件优化之TabControl控件的美化和功能扩展
|
12月前
|
C# C++ Windows
3.只使用代码创建WPF应用程序
3.只使用代码创建WPF应用程序
81 0
|
自然语言处理 编译器 C#
【WPF】实现动态切换语言(国际化)以及动态换肤功能
以下内容,手把手从搭建到最终实现,完成多语言切换以及换装功能。
324 0
【WPF】实现动态切换语言(国际化)以及动态换肤功能
|
JavaScript 前端开发 API
了解如何构建 Metro 样式的应用程序
Windows 提供两组用于构建 Metro 风格应用的 API:Windows 运行时和 Windows JavaScript 库。但是Windows JavaScript 库和大众的javascript不兼容,也就是说不是一回事 参考url http://msdn.
647 0
|
C# Windows
利用WPF创建含多种交互特性的无边框窗体
原文:利用WPF创建含多种交互特性的无边框窗体 咳咳,标题一口气读下来确实有点累,让我先解释一下。另外文章底部有演示程序的下载。
1194 0
|
C#
WPF 嵌入Winform GDI 、 开启AllowsTransparenc问题
原文:WPF 嵌入Winform GDI 、 开启AllowsTransparenc问题 此文章可以解决2至少2个问题: 1.开启AllowsTransparenc造成的GDI+组件不显示问题 2.WPF 组件无法覆盖嵌入WPF窗口的任何第三方GDI+组件上层   方案1:自制双层 原理:用一个新的窗口来承载GDI+组件,实现 父窗口 拖动、缩放、最小化、最大化 的联动 事件。
1216 0
|
JavaScript 前端开发 C++
COM组件开发实践(七)---多线程ActiveX控件和自动调整ActiveX控件大小(上)
声明:本文代码基于CodeProject的文章《A Complete ActiveX Web Control Tutorial》修改而来,因此同样遵循Code Project Open License (CPOL)。
842 0