• 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)

    其余逻辑实现保持不变。

    使用更简单,如下图示:

    出处:https://www.cnblogs.com/zuowj/p/6497142.html

  • 相关阅读:
    软件测试第四周--闰年问题的非法输入处理
    Edit Boxing三个盒子——等价类划分以及实现
    软件测试--等价类划分的基本概念及实际应用
    对软件测试工具的认识
    软件测试的流程及策略
    几种简单的软件测试模型
    软件开发中的白盒测试
    一种简单的软件测试工具——Visual Studio2010
    int Parse方法引发的异常
    两种软件测试框架——JUnit和NUnit
  • 原文地址:https://www.cnblogs.com/mq0036/p/16710313.html
Copyright © 2020-2023  润新知