Winform控件优化之继承Control重写实现Layer遮罩层

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天, 点击查看活动详情

通过继承Control控件类,进行重写,实现Layer效果的遮罩层,具体使用可直接看后面的介绍。

功能介绍和代码实现

主要功能如下:

  • 遮罩层的透明度 Alpha ,默认125。255表示不透明。
  • 设置遮罩层中心的图片 LayerImage ,默认无。通常为加载效果的gif图片。
  • 遮罩层中心的图片的大小, LayerImageWidth LayerImageHeight ,默认为图片大小。
  • 遮罩层上的控件 ControlOnLayer 。此处实现不严谨,通过此属性可以添加多个控件进入,通常一个即可。如果有控件,则中间的图片将不显示。
  • 遮罩层的颜色 LayerColor ,默认为Gray。
  • 推荐使用 Parent 设置其父控件。
  • 关于加载图片的背景透明:图片背景透明的效果和 base.BackColor 有关(即控件的背景,因此需要透明)。但并未做到真正透明底部的内容。

    因此,设置 base.BackColor 为透明色,且不允许外部修改 BackColor

        /// <summary>
        /// 自定义控件:半透明控件
        /// </summary>
         * [ToolboxBitmap(typeof(MyOpaqueLayer))]
         * 用于指定当把你做好的自定义控件添加到工具栏时,工具栏显示的图标。
         * 正确写法应该是
         * [ToolboxBitmap(typeof(XXXXControl),"xxx.bmp")]
         * 其中XXXXControl是你的自定义控件,"xxx.bmp"是你要用的图标名称。
        [ToolboxBitmap(typeof(CMLayer))]
        public class CMLayer : Control
            private int _alpha;//设置透明度 255表示不透明
            private PictureBox loadingPicBox;// 加载图片
            private Control controlOnLayer;//遮罩上的控件
            private Color layerColor;
            [Category("高级"), DefaultValue(125), Description("设置遮罩层透明度,默认125。255表示不透明")]
            public int Alpha
                    return _alpha;
                    _alpha = value;
                    this.Invalidate();
            /// <summary>
            /// 图片背景透明的效果和base.BackColor有关(控件背景,因此需要透明)。但并未做到真正透明底部的内容
            /// </summary>
            [Category("高级"), Description("遮罩层中间显示的图片,默认无。通常为加载效果的gif图片")]
            public Image LayerImage
                get => loadingPicBox?.Image;
                    if (value != null && loadingPicBox == null)
                        loadingPicBox = new PictureBox()
                            SizeMode = PictureBoxSizeMode.Zoom,
                            BackColor = Color.Transparent,
                            //Margin = new Padding(0)
                        loadingPicBox.Width = value.Width;
                        loadingPicBox.Height = value.Height;
                        loadingPicBox.Location = new Point((Width - loadingPicBox.Width) / 2, (Height - loadingPicBox.Height) / 2);
                        Controls.Add(loadingPicBox);
                    if (loadingPicBox != null)
                        loadingPicBox.Image = value;
                        if (controlOnLayer != null)
                            loadingPicBox.Visible = false;
            [Category("高级"), Description("遮罩层中间显示的图片的宽度,默认为图片大小")]
            public int LayerImageWidth { get { return loadingPicBox?.Width ?? 0; } set { if (loadingPicBox != null) loadingPicBox.Width = value; } }
            [Category("高级"), Description("遮罩层中间显示的图片的高度,默认为图片大小")]
            public int LayerImageHeight { get { return loadingPicBox?.Height ?? 0; } set { if (loadingPicBox != null) loadingPicBox.Height = value; } }
            [Category("高级"), Description("遮罩层中间显示的控件,默认无。如果有控件,则中间的图片将不显示")]
            public Control ControlOnLayer
                get => controlOnLayer; set
                    controlOnLayer = value;
                    controlOnLayer.Location = new Point((Width - controlOnLayer.Width) / 2, (Height - controlOnLayer.Height) / 2);
                    Controls.Add(controlOnLayer);
                    controlOnLayer.BringToFront(); // 非必须
                    if (loadingPicBox != null && loadingPicBox.Visible)
                        loadingPicBox.Visible = false;
            [Category("高级"), DefaultValue(typeof(Color), "Gray"), Description("遮罩层的颜色")]
            public Color LayerColor
                    return Color.FromArgb(_alpha, layerColor);
                    layerColor = value;
                    Invalidate();
            public new Control Parent
                    return base.Parent;
                    base.Parent = value;
                    if (value != null)
                        Dock = DockStyle.Fill;
                        BringToFront();
            [Browsable(false), Description("隐藏背景色的更改")]
            public override Color BackColor => base.BackColor;
            public CMLayer() : this(125)
            public CMLayer(int alpha)
                SetStyle(System.Windows.Forms.ControlStyles.Opaque | ControlStyles.SupportsTransparentBackColor, true);
                base.CreateControl();
                this._alpha = alpha;
                base.BackColor = Color.Transparent;
                LayerColor = Color.Gray;
                Dock = DockStyle.Fill;
                BringToFront();
            /// <summary>
            /// 自定义绘制背景
            /// </summary>
            /// <param name="e"></param>
            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
                base.OnPaint(e);
                SolidBrush backBrush = new SolidBrush(LayerColor);
                e.Graphics.FillRectangle(backBrush, ClientRectangle);
    

    遮罩层的使用

  • 创建添加到窗体并显示
  • var layer = new CMLayer();
    this.Controls.Add(layer); // Add 需调用BringToFront
    layer.BringToFront();
    layer.Show();
    
  • 推荐Parent赋值
  • layer.Parent = this;
    
    layer.LayerColor = Color.MediumPurple;
    
  • 遮罩层中间添加加载效果的图片
  • layer.LayerImage = LoadingImg.LoadingSimple;
    //layer.LayerImage = LoadingImg.LoadingStripBar;
    //layer.LayerImage = LoadingImg.LoadingStripBar2;
    layer.LayerColor = Color.CornflowerBlue;
    
  • ControlOnLayer遮罩层中间添加控件
  • 将一个ProgressBar控件添加到遮罩层

    using (var layer = new CMLayer())
        // 推荐Parent
        layer.Parent = this;
        // 显示操作进度
        using (var progressBar = new ProgressBar())
            #region 遮罩层上添加一个progressBar【ControlOnLayer属性的方式】
            progressBar.Style = ProgressBarStyle.Marquee;
            progressBar.Width = layer.Width - 10;
            layer.ControlOnLayer = progressBar;
            #endregion
            layer.LayerColor = Color.CornflowerBlue;
            layer.Show();
            // 处理耗时的操作,最好使用异步...
        layer.Hide();
    
  • layer.Controls.Add向遮罩层添加控件
  • #region 遮罩层上添加一个progressBar【layer.Controls.Add的方式】
    progressBar.Style = ProgressBarStyle.Marquee;
    progressBar.Width = layer.Width - 10;
    // 居中
    progressBar.Location = new Point((layer.Width - progressBar.Width) / 2, (layer.Height - progressBar.Height) / 2);
    layer.Controls.Add(progressBar);
    progressBar.BringToFront();
    #endregion
    
  • 无法在设计器中很好的使用。在窗体设计器中使用时,调整大小或移动、修改颜色时,总会使的遮罩层不透明。暂时无法解决。
  • 控件添加时注意层次,通过BringToFront()显示到最前方。
  • 未添加关闭按钮,可根据需要在遮罩层上添加关闭按钮。否则代码中要就保证会自动关闭。
  • 遮罩层的大小问题,控件只能位于窗体内,因此遮罩层控件无法遮盖整个窗体,遮罩层只显示在窗体内。
  • 通常开启遮罩层是为了等待耗时操作,因此,开启遮罩层后,最好禁止关闭。如果能够遮住整个窗体,那么就这遮住了顶部菜单栏的关闭按钮,也能实现禁止关闭,并且效果比较好。

  • C#实现Winform自定义半透明遮罩层
  •