自定义控件学习 《一》

自定义控件学习

一、 前言

  1. 什么是控件?
  2. 什么是组件?
  3. 如何创建与使用自定义控件

控件组件

  1. 本质上都是从类继承出的子类。组件派生于Component类,而控件派生于Control类,用户控件派生于UserControl.
  2. 表现上:组件Component是没有界面的,控件Control是有界面的。简单的说,组件是在设计器界面上看不到,控件在设计界面上是界面可见的。
  3. 关系说明:每一个可以重复使用并且可以与其他对象交互的对象都是组件,拥有UI界面的组件就是控件。就是说大家都是组件,但是有界面的组件就是控件。
  4. 控件的常见分类:复合,扩展和自定义三类。
    1. 复合控件:将多个已有控件组合成一个新的控件进行开发。
    2. 扩展控件:从已有的窗体控件中继承来的控件,是在原有基础上简单二次开发的控件。
    3. 自定义控件:继承自Control类,从头开发的控件。

见如下案例 首先是简单的组件(无UI界面):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FrmMain
{
    public partial class ComponentExample : Component
    {
        private int _id;
       
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
    }
}

img

编写一个简单的扩展控件案例(有UI界面)(代码基本相同,但是派生于Button类):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FrmMain
{
    public partial class ComponentExample : Button
    {
        private int _id;
       
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
    }
}

二、 使用简单的GDI绘制自定义控件

案例1 绘制一个led指示灯

需要设置属性如下:

  1. 外环宽度。2.外环间隙。3.内环间隙。4.颜色(五种)。5.当前值

img

先贴上代码,再补充相应的知识

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FrmMain
{
    public partial class ZLed : UserControl
    {
        public ZLed()
        {
            InitializeComponent();

            //设置控件样式 重绘图像固定加入
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.Selectable, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint, true);
        }

        #region Fields

        //画布
        private Graphics g;

        //画笔
        private Pen p;

        //画刷
        private SolidBrush sb;

        //宽度
        private int width;

        //高度
        private int height;


        private float outWidth = 4.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环宽度")]
        public float OutWidth
        {
            get { return outWidth; }
            set
            {
                if (value <= 0 || value > 0.1f * this.Width)
                {
                    return;
                }
                outWidth = value;
                this.Invalidate();
            }
        }


        private float outGap = 3.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环间隙")]
        public float OutGap
        {
            get { return outGap; }
            set
            {
                if (value <= 0 || value > 0.1f * this.Width || inGap <= value)
                {
                    return;
                }
                outGap = value;



                this.Invalidate();
            }
        }

        private float inGap = 8.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("内环间隙")]
        public float InGap
        {
            get { return inGap; }
            set
            {
                if (value <= 0 || value <= outGap)
                {
                    return;
                }
                inGap = value;
                this.Invalidate();
            }
        }


        private Color color1 = Color.Gray;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("第一种颜色")]
        public Color Color1
        {
            get { return color1; }
            set
            {
                color1 = value;
                this.Invalidate();
            }
        }



        private Color color2 = Color.LimeGreen;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("第二种颜色")]
        public Color Color2
        {
            get { return color2; }
            set
            {
                color2 = value;
                this.Invalidate();
            }
        }

        private Color color3 = Color.Red;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("第三种颜色")]
        public Color Color3
        {
            get { return color3; }
            set
            {
                color3 = value;
                this.Invalidate();
            }
        }

        private Color color4 = Color.DarkGoldenrod;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("第四种颜色")]
        public Color Color4
        {
            get { return color4; }
            set
            {
                color4 = value;
                this.Invalidate();
            }
        }

        private Color color5 = Color.Blue;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("第五种颜色")]
        public Color Color5
        {
            get { return color5; }
            set
            {
                color5 = value;
                this.Invalidate();
            }
        }

        private int currentValue = 0;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("当前值")]
        public int CurrentValue
        {
            get { return currentValue; }
            set
            {
                if (value > 4 || value < 0)
                {
                    return;
                }
                currentValue = value;
                this.Invalidate();
            }
        }


        #endregion

        #region Override
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            g = e.Graphics;

            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            this.width = this.Width;

            this.height = this.Height;

            //特殊情况处理
            if (inGap > 0.5f * this.width || inGap > 0.5f * this.height)
            {
                return;
            }

            Color currentColor = GetCurrentColor();

            //绘制外环

            p = new Pen(currentColor, outWidth);

            RectangleF rec = new RectangleF(outGap, outGap, this.width - 2 * outGap, this.height - 2 * outGap);

            g.DrawEllipse(p, rec);

            //绘制内圆

            sb = new SolidBrush(currentColor);

            rec = new RectangleF(inGap, inGap, this.width - 2 * inGap, this.height - 2 * inGap);

            g.FillEllipse(sb, rec);



        }
        #endregion

        private Color GetCurrentColor()
        {
            List<Color> ColorList = new List<Color>();
            ColorList.Add(color1);
            ColorList.Add(color2);
            ColorList.Add(color3);
            ColorList.Add(color4);
            ColorList.Add(color5);
            return ColorList[currentValue];
        }
    }
}

所需基础知识如下:

第一部分 Pen 画笔类,Graphics画板类与Brush填充类

1. Graphics画板

Graphics对象

使用Graphics可以将图像直接绘制到设备上。

上述代码中,在绘制外环中使用了该对象

            p = new Pen(currentColor, outWidth);

            RectangleF rec = new RectangleF(outGap, outGap, this.width - 2 * outGap, this.height - 2 * outGap);

            g.DrawEllipse(p, rec);

DrawEllipse方法中传入了画笔对象(设置了绘画的线条样式,传入颜色与线条宽度)与长方体对象(起始坐标,外接矩形的长与宽)。利用外接矩形来绘制圆

Graphics的其他方法如下:

  1. DrawLine(pen,10,15,111,222) // 绘制直线 起始坐标到终点坐标
  2. DrawRectangle(pen,10,15,111,222)//绘制矩形 起始坐标,宽度,长度

2. Pen 画笔类

主要属性如下(一般会和画板类一起使用):

  1. Color
  2. DashCap 短划线终点形状
  3. DashStyle(虚线样式)
  4. EndCap(线尾形状)
  5. StartCap(线头形状)
  6. Width(粗细)

3. Brush填充类

常见的使用类如下:

  1. SolidBrush:使用纯色进行填充
  2. HatchBrush:使用图案进行填充
  3. TextureBrush:可以使用纹理进行填充
  4. LinearGradientBrush :使用沿渐变混合的两种颜色进行绘制
  5. PathGradientBrush : 基于编程者定义的唯一路径,使用复杂的混合色渐变进行绘制
            sb = new SolidBrush(currentColor);
            rec = new RectangleF(inGap, inGap, this.width - 2 * inGap, this.height - 2 * inGap);
            g.FillEllipse(sb, rec);

第二部分 绘制图形美化

作用如下:消除锯齿,文本平滑化

			g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

第三部分 案例

案例二

案例2描述如下: 绘制一个可以改变样式滑块(一种是椭圆的,另外一种是圆角矩形的)

大致思路:先编写属性,然后编写重绘方法(重点是 GraphicsPath的使用)

样式大致如下:

代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FrmMain
{
    [DefaultEvent("CheckedChanged")]
    public partial class ZToggle : UserControl
    {
        public ZToggle()
        {
            InitializeComponent();
            //设置控件样式
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.Selectable, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.MouseDown += XToggle_MouseDown;
        }

        #region Fileds

        private Graphics g;

        private int width;

        private int height;


        public enum SwitchType
        {
            //椭圆
            Ellipse,

            //矩形
            Rectangle,
        }


        private bool _checked = false;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("是否选中")]
        public bool Checked
        {
            get { return _checked; }
            set
            {
                _checked = value;

                this.Invalidate();

                //触发一个事件
                this.CheckedChanged?.Invoke(this, null);
            }
        }

        private Color falseColor = Color.FromArgb(180, 180, 180);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("没有选中时的颜色")]
        public Color FalseColor
        {
            get { return falseColor; }
            set
            {
                falseColor = value;

                this.Invalidate();
            }
        }


        private Color trueColor = Color.FromArgb(73, 119, 232);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("选中时的颜色")]
        public Color TrueColor
        {
            get { return trueColor; }
            set
            {
                trueColor = value;

                this.Invalidate();
            }
        }


        private string falseText = "关闭";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("没有选中时的文本")]
        public string FalseText
        {
            get { return falseText; }
            set
            {
                falseText = value;

                this.Invalidate();
            }
        }

        private string trueText = "打开";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("选中时的文本")]
        public string TrueText
        {
            get { return trueText; }
            set
            {
                trueText = value;

                this.Invalidate();
            }
        }

        private SwitchType switchtype = SwitchType.Ellipse;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("切换样式")]
        public SwitchType Switchtype

        {
            get { return switchtype; }
            set
            {
                switchtype = value;
                this.Invalidate();
            }
        }


        private int circleDiameter = 10;
        [Browsable(true)]
        [Category("自定义属性")]
        [Description("小圆形直径")]
        public int CircleDiameter
        {
            get { return circleDiameter; }
            set
            {
                if (value >= this.Width || value >= this.Height || value <= 0)
                {
                    return;
                }

                circleDiameter = value;
                this.Invalidate();
            }
        }


        private int circlePosition = 10;
        [Browsable(true)]
        [Category("自定义属性")]
        [Description("小圆形位置")]
        public int CirclePosition
        {
            get { return circlePosition; }
            set
            {
                if (value >= this.Width || value >= this.Height || value <= 0)
                {
                    return;
                }

                circlePosition = value;
                this.Invalidate();
            }
        }


        private Color sliderColor = Color.White;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("滑块颜色")]
        public Color SliderColor
        {
            get { return sliderColor; }
            set
            {
                sliderColor = value;

                this.Invalidate();
            }
        }
        #endregion

        #region Event


        [Browsable(true)]
        [Category("自定义事件")]
        [Description("选中改变事件")]
        public event EventHandler CheckedChanged;

        private void XToggle_MouseDown(object sender, MouseEventArgs e)
        {
            Checked = !Checked;
        }


        #endregion

        #region Override
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            g = e.Graphics;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            this.width = this.Width;
            this.height = this.Height;

            //矩形样式绘制
            if (this.switchtype == SwitchType.Rectangle)
            {
                //确定填充色
                Color fillColor = this._checked ? trueColor : falseColor;

                //画带圆弧的矩形
                GraphicsPath path = new GraphicsPath();

                int diameter = 5;

                // 左上角 起始从0,0. 外接矩形的长度与宽度。开始从180旋转90度
                path.AddArc(0, 0, diameter, diameter, 180f, 90f);

                // 右上角
                path.AddArc(this.width - diameter, 0, diameter, diameter, 270f, 90f);

                // 右下角
                path.AddArc(this.width - diameter, this.height - diameter, diameter, diameter, 0f, 90f);

                // 左下角
                path.AddArc(0, this.height - diameter, diameter, diameter, 90f, 90f);

                g.FillPath(new SolidBrush(fillColor), path);

                string strText = this._checked ? trueText : falseText;

                if (_checked)
                {
                    path = new GraphicsPath();

                    int sliderwidth = this.height - 4;

                    path.AddArc(this.width - sliderwidth - 2, 2, diameter, diameter, 180f, 90f);

                    path.AddArc(this.width - diameter - 2, 2, diameter, diameter, 270f, 90f);

                    path.AddArc(this.width - diameter - 2, this.height - diameter - 2, diameter, diameter, 0f, 90f);

                    path.AddArc(this.width - sliderwidth - 2, this.height - diameter - 2, diameter, diameter, 90f, 90f);

                    g.FillPath(new SolidBrush(sliderColor), path);


                    if (string.IsNullOrEmpty(strText))
                    {
                        //绘制小圆形
                        g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(circlePosition, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
                    }
                    else
                    {
                        //绘制文本
                        Rectangle rec = new Rectangle(0, 0, this.width - sliderwidth - 2, this.height);

                        StringFormat sf = new StringFormat();
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;

                        g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);

                    }
                }
                else
                {
                    path = new GraphicsPath();

                    int sliderwidth = this.height - 4;

                    // 左上方
                    path.AddArc(2, 2, diameter, diameter, 180f, 90f);

                    // 右上方
                    path.AddArc(sliderwidth - diameter + 2, 2, diameter, diameter, 270f, 90f);

                    // 右下方
                    path.AddArc(sliderwidth - diameter + 2, sliderwidth - diameter + 2, diameter, diameter, 0f, 90f);

                    // 左下方
                    path.AddArc(2, sliderwidth - diameter + 2, diameter, diameter, 90f, 90f);

                    g.FillPath(new SolidBrush(sliderColor), path);


                    if (string.IsNullOrEmpty(strText))
                    {
                        //绘制小圆形
                        g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(this.width - circlePosition - circleDiameter, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));
                    }
                    else
                    {
                        //绘制文本
                        Rectangle rec = new Rectangle(sliderwidth + 2, 0, this.width - sliderwidth - 2, this.height);

                        StringFormat sf = new StringFormat();
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;

                        g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);

                    }


                }
            }

            //椭圆样式绘制
            else if (this.switchtype == SwitchType.Ellipse)
            {
                //确定填充色
                Color fillColor = this._checked ? trueColor : falseColor;

                //画带圆弧的矩形
                GraphicsPath path = new GraphicsPath();

                path.AddArc(1, 1, this.height - 2, this.height - 2, 90f, 180f);

                path.AddArc(this.width - (this.height - 2) - 1, 1, this.height - 2, this.height - 2, 270f, 180f);

                g.FillPath(new SolidBrush(fillColor), path);

                string strText = this._checked ? trueText : falseText;

                if (_checked)
                {
                    int circlewidth = this.height - 6;

                    g.FillEllipse(new SolidBrush(sliderColor), new Rectangle(this.width - circlewidth - 3, 3, circlewidth, circlewidth));

                    if (string.IsNullOrEmpty(strText))
                    {
                        //绘制小圆形
                        g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(circlePosition, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));

                    }
                    else
                    {
                        //绘制文本
                        Rectangle rec = new Rectangle(0, 0, this.width - circlewidth - 3, this.height);

                        StringFormat sf = new StringFormat();
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;

                        g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
                    }
                }
                else
                {
                    int circlewidth = this.height - 6;

                    g.FillEllipse(new SolidBrush(sliderColor), new Rectangle(3, 3, circlewidth, circlewidth));

                    if (string.IsNullOrEmpty(strText))
                    {
                        //绘制小圆形
                        g.DrawEllipse(new Pen(sliderColor, 2), new RectangleF(this.width - circlePosition - circleDiameter, (this.height - circleDiameter) * 0.5f, circleDiameter, circleDiameter));

                    }
                    else
                    {
                        //绘制文本
                        Rectangle rec = new Rectangle(circlewidth + 3, 0, this.width - circlewidth - 3, this.height);

                        StringFormat sf = new StringFormat();
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;

                        g.DrawString(strText, Font, new SolidBrush(sliderColor), rec, sf);
                    }
                }

            }
        }
        #endregion
       

    }
}

img
 //确定填充色
                Color fillColor = this._checked ? trueColor : falseColor;
                //画带圆弧的矩形
                GraphicsPath path = new GraphicsPath();
                int diameter = 5;
                // 左上角 起始从0,0. 外接矩形的长度与宽度。开始从180旋转90度
                path.AddArc(0, 0, diameter, diameter, 180f, 90f);
                // 右上角
                path.AddArc(this.width - diameter, 0, diameter, diameter, 270f, 90f);
                // 右下角
                path.AddArc(this.width - diameter, this.height - diameter, diameter, diameter, 0f, 90f);
                // 左下角
                path.AddArc(0, this.height - diameter, diameter, diameter, 90f, 90f);
                g.FillPath(new SolidBrush(fillColor), path);

path.AddArc 参数说明(我自己理解的是外接矩形的左上角作为起始点)。首先坐标轴方向是从x轴正半轴开始,顺时针旋转的。所以绘制图像是从180度开始到270度,旋转了90度的那段弧线,这里我用绿色的方框进行了勾勒。绘制右上角的图像。借用下网上的图片

img

待更新。
如有问题,欢迎讨论留言

posted @ 2023-03-06 01:30  聆听微风  阅读(152)  评论(0编辑  收藏  举报