自定义控件学习(2)

自定义控件学习 仪表盘

一、现有案例学习

案例1:

img

实际中修改 实时值显示 属性就能让仪表盘的指针进行变动

使用到的属性:

  • 外环左边颜色:左侧环用颜色显示正常

  • 外环右边颜色:右侧环用颜色显示异常。比如气压过高的报警颜色

    如果有三段颜色需求可以考虑把外环圆分为三段进行绘制,本例中绘制了两段

  • 刻度颜色:外侧圆环的刻度颜色

  • 左半部分的角度:0°-270°之间

  • 内环圆所占比例

  • 刻度圆所占比例

  • 实时数据显示高度所占比例

  • 仪表显示量程

  • 内环宽度

  • 外环宽度

  • 实时值显示

  • 中心圆半径

  • 显示前缀

  • 刻度字体

  • 显示字体

  • 是否显示实时值

  • 实时值显示颜色

属性部分:

  #region Fileds

        private Graphics g;

        private Pen p;

        private SolidBrush sb;

        private int width;

        private int height;

        private Color leftColor = Color.FromArgb(113, 152, 54);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环左边颜色")]
        public Color LeftColor
        {
            get { return leftColor; }
            set
            {
                leftColor = value;

                this.Invalidate();
            }
        }


        private Color rightColor = Color.FromArgb(187, 187, 187);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环右边颜色")]
        public Color RightColor
        {
            get { return rightColor; }
            set
            {
                rightColor = value;

                this.Invalidate();
            }
        }

        private Color textColor = Color.Black;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度颜色")]
        public Color TextColor
        {
            get { return textColor; }
            set
            {
                textColor = value;

                this.Invalidate();
            }
        }

        private float leftAngle = 168.75f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("左半部分的角度:0°-270°之间")]
        public float LeftAngle
        {
            get { return leftAngle; }
            set
            {

                if (value > 270.0f || value < 0.0f)
                {
                    return;
                }

                leftAngle = value;

                this.Invalidate();
            }
        }


        private float inScale = 0.5f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("内环圆所占比例:0 - 1 之间")]
        public float InScale
        {
            get { return inScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                inScale = value;

                this.Invalidate();
            }
        }



        private float outScale = 0.8f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度圆所占比例:0 - 1 之间")]
        public float OutScale
        {
            get { return outScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                outScale = value;

                this.Invalidate();
            }
        }

        private float textShowScale = 0.88f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时数据显示高度所占比例:0 - 1 之间,值越小越靠上")]
        public float TextShowScale
        {
            get { return textShowScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                textShowScale = value;

                this.Invalidate();
            }
        }



        private float range = 160.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("仪表显示量程")]
        public float Range
        {
            get { return range; }
            set
            {

                if (value <= 0.0f)
                {
                    return;
                }

                range = value;

                this.Invalidate();
            }
        }

        private int inThickness = 12;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("内环宽度")]
        public int InThickness
        {
            get { return inThickness; }
            set
            {

                if (value <= 0)
                {
                    return;
                }

                inThickness = value;

                this.Invalidate();
            }
        }


        private int outThickness = 5;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环宽度")]
        public int OutThickness
        {
            get { return outThickness; }
            set
            {

                if (value <= 0)
                {
                    return;
                }

                outThickness = value;

                this.Invalidate();
            }
        }

        private float currentValue = 100.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时值显示")]
        public float CurrentValue
        {
            get { return currentValue; }
            set
            {

                if (value <= 0.0f || value > range)
                {
                    return;
                }

                currentValue = value;

                this.Invalidate();
            }
        }

        private float centerRadius = 12.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("中心圆半径")]
        public float CenterRadius
        {
            get { return centerRadius; }
            set
            {
                if (value <= 0.0f)
                {
                    return;
                }

                centerRadius = value;

                this.Invalidate();
            }
        }

        private string textPrefix = "实际温度:";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示前缀")]
        public string TextPrefix
        {
            get { return textPrefix; }
            set
            {
                textPrefix = value;

                this.Invalidate();
            }
        }

        private string unit = "℃";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示前缀")]
        public string Unit
        {
            get { return unit; }
            set
            {
                unit = value;

                this.Invalidate();
            }
        }


        private Font scaleFont = new Font(new FontFamily("微软雅黑"), 8.0f);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度字体")]
        public Font ScaleFont
        {
            get { return scaleFont; }
            set
            {
                scaleFont = value;

                this.Invalidate();
            }
        }


        private Font textShowFont = new Font(new FontFamily("微软雅黑"), 10.0f);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示字体")]
        public Font TxtShowFont
        {
            get { return textShowFont; }
            set
            {
                textShowFont = value;

                this.Invalidate();
            }
        }

        private bool isTextShow = true;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("是否显示实时值")]
        public bool IsTextShow
        {
            get { return isTextShow; }
            set
            {
                isTextShow = value;

                this.Invalidate();
            }
        }

        private Color textShowColor = Color.Black;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时值显示颜色")]
        public Color TextShowColor
        {
            get { return textShowColor; }
            set
            {
                textShowColor = value;

                this.Invalidate();
            }
        }

        #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.width <= 20 || this.height <= 20)
            {
                return;
            }

            //获取圆心位置
            Point center = GetCenterPoint();

            //绘制外环

            g.RotateTransform(0.0f);

            p = new Pen(leftColor, outThickness);

            g.DrawArc(p, new Rectangle(10, 10, center.X * 2 - 20, center.Y * 2 - 20), -225.0f, leftAngle);

            p = new Pen(rightColor, outThickness);

            g.DrawArc(p, new Rectangle(10, 10, center.X * 2 - 20, center.Y * 2 - 20), -225.0f + leftAngle, 270.0f - leftAngle);

            g.TranslateTransform(center.X, center.Y);

            //画刻度

            g.RotateTransform(-135.0f);

            for (int i = 0; i < 9; i++)
            {
                if (33.75f * i > leftAngle)
                {
                    sb = new SolidBrush(rightColor);
                }
                else
                {
                    sb = new SolidBrush(leftColor);
                }

                g.FillRectangle(sb, new RectangleF(-2.0f, center.Y * (-1.0f) + 5.0f, 4.0f, 12.0f));

                g.RotateTransform(33.75f);
            }

            //绘制刻度值
            //33.75+270.0
            g.RotateTransform(-303.75f);


            //复杂一点、横着的刻度值

            //宽度为60,高度为20

            g.RotateTransform(135.0f);

            for (int i = 0; i < 9; i++)
            {

                float angle = -225.0f + 33.75f * i;

                double x1 = Math.Cos(angle * Math.PI / 180);

                double y1 = Math.Sin(angle * Math.PI / 180);

                float x = Convert.ToSingle(center.X * outScale * x1);

                float y = Convert.ToSingle(center.Y * outScale * y1);

                StringFormat sf = new StringFormat();

                if (i == 4)
                {
                    x = x - 30;
                    sf.Alignment = StringAlignment.Center;
                }
                else if (i > 4)
                {
                    x = x - 60;
                    sf.Alignment = StringAlignment.Far;
                }
                else if (i < 4)
                {
                    sf.Alignment = StringAlignment.Near;
                }
                RectangleF rec = new RectangleF(x, y, 60, 20);

                if (range % 8 == 0)
                {
                    g.DrawString((range / 8 * i).ToString(), scaleFont, new SolidBrush(textColor), rec, sf);
                }
                else
                {
                    g.DrawString((range / 8 * i).ToString("f1"), scaleFont, new SolidBrush(textColor), rec, sf);
                }
            }

            //画内圆

            g.FillEllipse(new SolidBrush(leftColor), new RectangleF(-centerRadius, -centerRadius, centerRadius * 2, centerRadius * 2));

            //画内圆实际值

            p = new Pen(leftColor, inThickness);

            float sweepangle = currentValue / range * 270.0f;

            g.DrawArc(p, new RectangleF(center.X * inScale * (-1.0f), center.Y * inScale * (-1.0f), center.X * 2 * inScale, center.Y * 2 * inScale), -225.0f, sweepangle);


            //画指针

            g.RotateTransform(-135.0f);

            g.RotateTransform(sweepangle);

            p = new Pen(leftColor, 3.0f);

            PointF endPoint = new PointF(-1.5f, (center.Y * inScale + inThickness * 0.5f) * (-1.0f));

            g.DrawLine(p, new PointF(0, 0), endPoint);

            //写标识数组
            if (isTextShow)
            {
                g.RotateTransform(sweepangle * (-1.0f));

                g.RotateTransform(135.0f);

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

                string val = textPrefix + currentValue.ToString() + " " + unit;

                RectangleF rec = new RectangleF(center.X * (-1.0f), this.height * textShowScale - center.Y, center.X * 2, this.height * (1.0f - textShowScale));

                g.DrawString(val, textShowFont, new SolidBrush(textShowColor), rec, sf);

            }


        }

        #endregion

        #region Private Methods

        private Point GetCenterPoint()
        {
            if (this.Height > this.Width)
            {
                return new Point(this.Width / 2, this.Width / 2);
            }
            else
            {
                return new Point(this.Height / 2, this.Height / 2);
            }
        }

        #endregion

思路原理:

  1. 找圆心:图像可能横向或者纵向放大。选择较短的边构成的正方形中心作为圆心。
  2. 绘制外环时候:把外环分为左半个和右半个圆环。
  3. 绘制刻度:使用很小的长方形来充当刻度线。实际绘制需要向左稍微偏移一点。
  4. 绘制刻度值:共有9个数值,其中左边4个,中间1个,右边4个。StringAlignment枚举类有3个枚举属性。左侧的使用Near,中间的使用Center,右侧的使用Far
Center 1 指定文本在布局矩形中居中对齐。
Far 2 指定文本远离布局矩形的原点位置对齐。 在左到右布局中,远端位置是右。 在右到左布局中,远端位置是左。
Near 0 指定文本靠近布局对齐。 在左到右布局中,近端位置是左。 在右到左布局中,近端位置是右。
  1. 绘制圆内实际值与指针:先用计算出实际值占量程的比例再乘以270°,然后绘制直线指向实际值的位置。绘制直线。
  2. RotateTransform。该方法是更改逆时针旋转起始角度。比如旧的样式中需要顺时针旋转270度的图形。当先使用g.RotateTransform(90)后,那起始的角度就变为90度,仅仅只需要旋转180度就好了。
  3. TranslateTransform(x,y)。平移圆心坐标到指定的(x,y)
  4. 其中所涉及到的实际绘图角度,我本人也是学得一知半解,每次需要多次调整才能绘制出大致的图形。个人感触,需要现有美工绘制出图形,然后使用工具测量出相应的数据,再去图形中进行尝试。

案例2

img

代码逻辑基本相同:

代码完整如下:

 public ZDialPlate()
        {
            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 Fileds

        private Graphics g;

        private Pen p;

        private SolidBrush sb;

        private int width;

        private int height;

        private Color leftColor = Color.FromArgb(113, 152, 54);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环左边颜色")]
        public Color LeftColor
        {
            get { return leftColor; }
            set
            {
                leftColor = value;

                this.Invalidate();
            }
        }


        private Color rightColor = Color.FromArgb(187, 187, 187);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环右边颜色")]
        public Color RightColor
        {
            get { return rightColor; }
            set
            {
                rightColor = value;

                this.Invalidate();
            }
        }

        private Color textColor = Color.Black;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度颜色")]
        public Color TextColor
        {
            get { return textColor; }
            set
            {
                textColor = value;

                this.Invalidate();
            }
        }

        private float leftAngle = 120f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("左半部分的角度:0°-180°之间")]
        public float LeftAngle
        {
            get { return leftAngle; }
            set
            {

                if (value > 180.0f || value < 0.0f)
                {
                    return;
                }

                leftAngle = value;

                this.Invalidate();
            }
        }


        private float inScale = 0.5f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("内环圆所占比例:0 - 1 之间")]
        public float InScale
        {
            get { return inScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                inScale = value;

                this.Invalidate();
            }
        }



        private float outScale = 0.8f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度圆所占比例:0 - 1 之间")]
        public float OutScale
        {
            get { return outScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                outScale = value;

                this.Invalidate();
            }
        }

        private float textShowScale = 0.88f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时数据显示高度所占比例:0 - 1 之间,值越小越靠上")]
        public float TextShowScale
        {
            get { return textShowScale; }
            set
            {

                if (value > 1.0f || value < 0.0f)
                {
                    return;
                }

                textShowScale = value;

                this.Invalidate();
            }
        }



        private float range = 180.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("仪表显示量程")]
        public float Range
        {
            get { return range; }
            set
            {

                if (value <= 0.0f)
                {
                    return;
                }

                range = value;

                this.Invalidate();
            }
        }

        private int inThickness = 12;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("内环宽度")]
        public int InThickness
        {
            get { return inThickness; }
            set
            {

                if (value <= 0)
                {
                    return;
                }

                inThickness = value;

                this.Invalidate();
            }
        }


        private int outThickness = 5;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("外环宽度")]
        public int OutThickness
        {
            get { return outThickness; }
            set
            {

                if (value <= 0)
                {
                    return;
                }

                outThickness = value;

                this.Invalidate();
            }
        }

        private float currentValue = 100.0f;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时值显示")]
        public float CurrentValue
        {
            get { return currentValue; }
            set
            {

                if (value <= 0.0f || value > range)
                {
                    return;
                }

                currentValue = value;

                this.Invalidate();
            }
        }


        private string textPrefix = "实际温度:";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示前缀")]
        public string TextPrefix
        {
            get { return textPrefix; }
            set
            {
                textPrefix = value;

                this.Invalidate();
            }
        }

        private string unit = "℃";

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示前缀")]
        public string Unit
        {
            get { return unit; }
            set
            {
                unit = value;

                this.Invalidate();
            }
        }


        private Font scaleFont = new Font(new FontFamily("微软雅黑"), 8.0f);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("刻度字体")]
        public Font ScaleFont
        {
            get { return scaleFont; }
            set
            {
                scaleFont = value;

                this.Invalidate();
            }
        }


        private Font textShowFont = new Font(new FontFamily("微软雅黑"), 10.0f);

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("显示字体")]
        public Font TxtShowFont
        {
            get { return textShowFont; }
            set
            {
                textShowFont = value;

                this.Invalidate();
            }
        }

        private bool isTextShow = true;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("是否显示实时值")]
        public bool IsTextShow
        {
            get { return isTextShow; }
            set
            {
                isTextShow = value;

                this.Invalidate();
            }
        }

        private Color textShowColor = Color.Black;

        [Browsable(true)]
        [Category("自定义属性")]
        [Description("实时值显示颜色")]
        public Color TextShowColor
        {
            get { return textShowColor; }
            set
            {
                textShowColor = value;

                this.Invalidate();
            }
        }

        #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.width <= 20 || this.height <= 20)
            {
                return;
            }

            if (this.height < this.width * 0.5f)
            {
                return;
            }

            //画外环
            p = new Pen(leftColor, outThickness);

            g.DrawArc(p, new RectangleF(10, 10, this.width - 20, this.width - 20), -180.0f, leftAngle);

            p = new Pen(rightColor, outThickness);

            g.DrawArc(p, new RectangleF(10, 10, this.width - 20, this.width - 20), -180.0f + leftAngle, 180.0f - leftAngle);

            //画刻度

            g.TranslateTransform(this.width * 0.5f, this.width * 0.5f);

            g.RotateTransform(-90.0f);

            for (int i = 0; i < 7; i++)
            {
                if (30 * i > leftAngle)
                {
                    sb = new SolidBrush(rightColor);
                }
                else
                {
                    sb = new SolidBrush(leftColor);
                }
                g.FillRectangle(sb, new RectangleF(-3.0f, (this.width * 0.5f - 10.0f + outThickness * 0.5f + 2.0f) * (-1.0f), 6.0f, outThickness + 4.0f));

                g.RotateTransform(30.0f);
            }

            //画刻度值

            g.RotateTransform(-210.0f);

            g.RotateTransform(90.0f);

            for (int i = 0; i < 7; i++)
            {
                float angle = -180.0f + 30.0f * i;

                double x1 = Math.Cos(angle * Math.PI / 180);

                double y1 = Math.Sin(angle * Math.PI / 180);

                float x = Convert.ToSingle(this.width * outScale * 0.5f * x1);

                float y = Convert.ToSingle(this.width * outScale * 0.5f * y1);

                StringFormat sf = new StringFormat();

                if (i == 3)
                {
                    x = x - 30;
                    sf.Alignment = StringAlignment.Center;
                }
                else if (i > 3)
                {
                    x = x - 60;
                    sf.Alignment = StringAlignment.Far;
                }
                else if (i < 3)
                {
                    sf.Alignment = StringAlignment.Near;
                }

                RectangleF rec = new RectangleF(x, y, 60, 20);

                sb = new SolidBrush(textColor);

                if (range % 6 == 0)
                {
                    g.DrawString((range / 6 * i).ToString(), scaleFont, sb, rec, sf);
                }
                else
                {
                    g.DrawString((range / 6 * i).ToString("f1"), scaleFont, sb, rec, sf);
                }

            }

            //画实际值圆弧

            p = new Pen(leftColor, inThickness);

            float sweepangle = currentValue / range * 180.0f;

            float xx = this.width * 0.5f * inScale * (-1.0f);

            float yy = this.width * 0.5f * inScale * (-1.0f);

            g.DrawArc(p, new RectangleF(xx, yy, this.width * inScale, this.width * inScale), -180.0f, sweepangle);

            //绘制TextShow

            if (isTextShow)
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;

                RectangleF rec = new RectangleF(this.width * (-0.5f), this.height * textShowScale - 0.5f * this.width, this.width, this.height * (1.0f - textShowScale));

                string val = textPrefix + currentValue.ToString() + " " + unit;

                g.DrawString(val, textShowFont, new SolidBrush(textShowColor), rec, sf);
            }


        }
        #endregion
posted @ 2023-03-06 17:31  聆听微风  阅读(105)  评论(0编辑  收藏  举报