• (七十二)c#Winform自定义控件-雷达图-HZHControls


    官网

    http://www.hzhcontrols.com

    前提

    入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章。

    GitHub:https://github.com/kwwwvagaa/NetWinformControl

    码云:https://gitee.com/kwwwvagaa/net_winform_custom_control.git

    如果觉得写的还行,请点个 star 支持一下吧

    欢迎前来交流探讨: 企鹅群568015492 企鹅群568015492

    麻烦博客下方点个【推荐】,谢谢

    NuGet

    Install-Package HZH_Controls

    目录

    https://www.cnblogs.com/bfyx/p/11364884.html

    用处及效果

    准备工作

    GDI+画的,不会的可以先百度了解下

    开始

    添加一个类UCRadarChart ,继承 UserControl

    添加一些控制属性

      1  /// <summary>
      2         /// The split count
      3         /// </summary>
      4         private int splitCount = 5;
      5         /// <summary>
      6         /// Gets or sets the split count.
      7         /// </summary>
      8         /// <value>The split count.</value>
      9         [Browsable(true)]
     10         [Category("自定义")]
     11         [Description("获取或设置分隔份数")]
     12         public int SplitCount
     13         {
     14             get { return splitCount; }
     15             set
     16             {
     17                 splitCount = value;
     18                 Invalidate();
     19             }
     20         }
     21 
     22         /// <summary>
     23         /// The split odd color
     24         /// </summary>
     25         private Color splitOddColor = Color.White;
     26         /// <summary>
     27         /// 分隔奇数栏背景色
     28         /// </summary>
     29         /// <value>The color of the split odd.</value>
     30         [Browsable(true)]
     31         [Category("自定义")]
     32         [Description("获取或设置分隔奇数栏背景色")]
     33         public Color SplitOddColor
     34         {
     35             get { return splitOddColor; }
     36             set
     37             {
     38                 splitOddColor = value;
     39                 Invalidate();
     40             }
     41         }
     42         /// <summary>
     43         /// The split even color
     44         /// </summary>
     45         private Color splitEvenColor = Color.FromArgb(232, 232, 232);
     46         /// <summary>
     47         /// 分隔偶数栏背景色
     48         /// </summary>
     49         /// <value>The color of the split even.</value>
     50         [Browsable(true)]
     51         [Category("自定义")]
     52         [Description("获取或设置分隔偶数栏背景色")]
     53         public Color SplitEvenColor
     54         {
     55             get { return splitEvenColor; }
     56             set { splitEvenColor = value; }
     57         }
     58 
     59         /// <summary>
     60         /// The line color
     61         /// </summary>
     62         private Color lineColor = Color.FromArgb(153, 153, 153);
     63         /// <summary>
     64         /// Gets or sets the color of the line.
     65         /// </summary>
     66         /// <value>The color of the line.</value>
     67         [Browsable(true)]
     68         [Category("自定义")]
     69         [Description("获取或设置线条色")]
     70         public Color LineColor
     71         {
     72             get { return lineColor; }
     73             set
     74             {
     75                 lineColor = value;
     76                 Invalidate();
     77             }
     78         }
     79 
     80         /// <summary>
     81         /// The radar positions
     82         /// </summary>
     83         private RadarPosition[] radarPositions;
     84         /// <summary>
     85         /// 节点列表,至少需要3个
     86         /// </summary>
     87         /// <value>The radar positions.</value>
     88         [Browsable(true)]
     89         [Category("自定义")]
     90         [Description("获取或设置节点,至少需要3个")]
     91         public RadarPosition[] RadarPositions
     92         {
     93             get { return radarPositions; }
     94             set
     95             {
     96                 radarPositions = value;
     97                 Invalidate();
     98             }
     99         }
    100 
    101         /// <summary>
    102         /// The title
    103         /// </summary>
    104         private string title;
    105         /// <summary>
    106         /// 标题
    107         /// </summary>
    108         /// <value>The title.</value>
    109         [Browsable(true)]
    110         [Category("自定义")]
    111         [Description("获取或设置标题")]
    112         public string Title
    113         {
    114             get { return title; }
    115             set
    116             {
    117                 title = value;
    118                 ResetTitleSize();
    119                 Invalidate();
    120             }
    121         }
    122 
    123         /// <summary>
    124         /// The title font
    125         /// </summary>
    126         private Font titleFont = new Font("微软雅黑", 12);
    127         /// <summary>
    128         /// Gets or sets the title font.
    129         /// </summary>
    130         /// <value>The title font.</value>
    131         [Browsable(true)]
    132         [Category("自定义")]
    133         [Description("获取或设置标题字体")]
    134         public Font TitleFont
    135         {
    136             get { return titleFont; }
    137             set
    138             {
    139                 titleFont = value;
    140                 ResetTitleSize();
    141                 Invalidate();
    142             }
    143         }
    144 
    145         /// <summary>
    146         /// The title color
    147         /// </summary>
    148         private Color titleColor = Color.Black;
    149         /// <summary>
    150         /// Gets or sets the color of the title.
    151         /// </summary>
    152         /// <value>The color of the title.</value>
    153         [Browsable(true)]
    154         [Category("自定义")]
    155         [Description("获取或设置标题文本颜色")]
    156         public Color TitleColor
    157         {
    158             get { return titleColor; }
    159             set
    160             {
    161                 titleColor = value;
    162                 Invalidate();
    163             }
    164         }
    165 
    166         /// <summary>
    167         /// The lines
    168         /// </summary>
    169         private RadarLine[] lines;
    170         /// <summary>
    171         /// Gets or sets the lines.
    172         /// </summary>
    173         /// <value>The lines.</value>
    174         [Browsable(true)]
    175         [Category("自定义")]
    176         [Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")]
    177         public RadarLine[] Lines
    178         {
    179             get { return lines; }
    180             set
    181             {
    182                 lines = value;
    183                 Invalidate();
    184             }
    185         }
    186 
    187 
    188         /// <summary>
    189         /// The title size
    190         /// </summary>
    191         SizeF titleSize = SizeF.Empty;
    192         /// <summary>
    193         /// The m rect working
    194         /// </summary>
    195         private RectangleF m_rectWorking = Rectangle.Empty;
    196         /// <summary>
    197         /// The line value type size
    198         /// </summary>
    199         SizeF lineValueTypeSize = SizeF.Empty;
    200         /// <summary>
    201         /// The int line value COM count
    202         /// </summary>
    203         int intLineValueComCount = 0;
    204         /// <summary>
    205         /// The int line value row count
    206         /// </summary>
    207         int intLineValueRowCount = 0;

    属性改变时处理工作区域

     1  /// <summary>
     2         /// Handles the SizeChanged event of the UCRadarChart control.
     3         /// </summary>
     4         /// <param name="sender">The source of the event.</param>
     5         /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
     6         void UCRadarChart_SizeChanged(object sender, EventArgs e)
     7         {
     8             ResetWorkingRect();
     9         }
    10 
    11         /// <summary>
    12         /// Resets the working rect.
    13         /// </summary>
    14         private void ResetWorkingRect()
    15         {
    16             if (lines != null && lines.Length > 0)
    17             {
    18                 using (Graphics g = this.CreateGraphics())
    19                 {
    20                     foreach (var item in lines)
    21                     {
    22                         var s = g.MeasureString(item.Name, Font);
    23                         if (s.Width > lineValueTypeSize.Width)
    24                             lineValueTypeSize = s;
    25                     }
    26                 }
    27             }
    28             var lineTypePanelHeight = 0f;
    29             if (lineValueTypeSize != SizeF.Empty)
    30             {
    31                 intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + 25));
    32 
    33                 intLineValueRowCount = lines.Length / intLineValueComCount;
    34                 if (lines.Length % intLineValueComCount != 0)
    35                 {
    36                     intLineValueRowCount++;
    37                 }
    38                 lineTypePanelHeight = (lineValueTypeSize.Height + 10) * intLineValueRowCount;
    39             }
    40             var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
    41             var rectWorking = new RectangleF((this.Width - min) / 2 + 10, titleSize.Height + lineTypePanelHeight + 10, min - 10, min - 10);
    42             //处理文字
    43             float fltSplitAngle = 360F / radarPositions.Length;
    44             float fltRadiusWidth = rectWorking.Width / 2;
    45             float minX = rectWorking.Left;
    46             float maxX = rectWorking.Right;
    47             float minY = rectWorking.Top;
    48             float maxY = rectWorking.Bottom;
    49             using (Graphics g = this.CreateGraphics())
    50             {
    51                 PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / 2, rectWorking.Top + rectWorking.Height / 2);
    52                 for (int i = 0; i < radarPositions.Length; i++)
    53                 {
    54                     float fltAngle = 270 + fltSplitAngle * i;
    55                     fltAngle = fltAngle % 360;
    56                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
    57                     var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
    58                     if (_point.X < centrePoint.X)//
    59                     {
    60                         if (_point.X - _txtSize.Width < minX)
    61                         {
    62                             minX = rectWorking.Left + _txtSize.Width;
    63                         }
    64                     }
    65                     else//
    66                     {
    67                         if (_point.X + _txtSize.Width > maxX)
    68                         {
    69                             maxX = rectWorking.Right - _txtSize.Width;
    70                         }
    71                     }
    72                     if (_point.Y < centrePoint.Y)//
    73                     {
    74                         if (_point.Y - _txtSize.Height < minY)
    75                         {
    76                             minY = rectWorking.Top + _txtSize.Height;
    77                         }
    78                     }
    79                     else//
    80                     {
    81                         if (_point.Y + _txtSize.Height > maxY)
    82                         {
    83                             maxY = rectWorking.Bottom - _txtSize.Height;
    84                         }
    85                     }
    86                 }
    87             }
    88 
    89             min = Math.Min(maxX - minX, maxY - minY);
    90             m_rectWorking = new RectangleF(minX, minY, min, min);
    91         }

    重绘

      1 protected override void OnPaint(PaintEventArgs e)
      2         {
      3             base.OnPaint(e);
      4             var g = e.Graphics;
      5             g.SetGDIHigh();
      6 
      7             if (!string.IsNullOrEmpty(title))
      8             {
      9                 g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / 2, m_rectWorking.Top - titleSize.Height - 10 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
     10             }
     11 
     12             if (radarPositions.Length <= 2)
     13             {
     14                 g.DrawString("至少需要3个顶点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
     15                 return;
     16             }
     17 
     18             var y = m_rectWorking.Top - 20 - (intLineValueRowCount * (10 + lineValueTypeSize.Height));
     19 
     20             for (int i = 0; i < intLineValueRowCount; i++)
     21             {
     22                 var x = 0f;
     23                 int intCount = intLineValueComCount;
     24                 if (i == intLineValueRowCount - 1)
     25                 {
     26                     intCount = lines.Length % intLineValueComCount;
     27 
     28                 }
     29                 x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + 25)) / 2;
     30 
     31                 for (int j = 0; j < intCount; j++)
     32                 {
     33                     g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
     34                     g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + 25) * j + 20, y + lineValueTypeSize.Height * i));
     35                 }
     36             }
     37 
     38             float fltSplitAngle = 360F / radarPositions.Length;
     39             float fltRadiusWidth = m_rectWorking.Width / 2;
     40             float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
     41             PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Height / 2);
     42 
     43             List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
     44             //分割点
     45             for (int i = 0; i < radarPositions.Length; i++)
     46             {
     47                 float fltAngle = 270 + fltSplitAngle * i;
     48                 fltAngle = fltAngle % 360;
     49                 for (int j = 0; j < splitCount; j++)
     50                 {
     51                     if (i == 0)
     52                     {
     53                         lstRingPoints.Add(new List<PointF>());
     54                     }
     55                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
     56                     lstRingPoints[j].Add(_point);
     57                 }
     58             }
     59 
     60             for (int i = 0; i < lstRingPoints.Count; i++)
     61             {
     62                 var ring = lstRingPoints[i];
     63                 GraphicsPath path = new GraphicsPath();
     64                 path.AddLines(ring.ToArray());
     65                 if ((lstRingPoints.Count - i) % 2 == 0)
     66                 {
     67                     g.FillPath(new SolidBrush(splitEvenColor), path);
     68                 }
     69                 else
     70                 {
     71                     g.FillPath(new SolidBrush(splitOddColor), path);
     72                 }
     73             }
     74 
     75             //画环
     76             foreach (var ring in lstRingPoints)
     77             {
     78                 ring.Add(ring[0]);
     79                 g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
     80             }
     81             //分割线
     82             foreach (var item in lstRingPoints[0])
     83             {
     84                 g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
     85             }
     86 
     87             //
     88             for (int i = 0; i < lines.Length; i++)
     89             {
     90                 var line = lines[i];
     91                 if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
     92                     continue;
     93                 if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
     94                     line.LineColor = ControlHelper.Colors[i + 13];
     95                 List<PointF> ps = new List<PointF>();
     96                 for (int j = 0; j < radarPositions.Length; j++)
     97                 {
     98                     float fltAngle = 270 + fltSplitAngle * j;
     99                     fltAngle = fltAngle % 360;
    100                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
    101                     ps.Add(_point);
    102                 }
    103                 ps.Add(ps[0]);
    104                 if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
    105                 {
    106                     GraphicsPath path = new GraphicsPath();
    107                     path.AddLines(ps.ToArray());
    108                     g.FillPath(new SolidBrush(line.FillColor.Value), path);
    109                 }
    110                 g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), 2), ps.ToArray());
    111 
    112                 for (int j = 0; j < radarPositions.Length; j++)
    113                 {
    114                     var item = ps[j];
    115                     g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
    116                     g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
    117                     if (line.ShowValueText)
    118                     {
    119                         var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
    120                         g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / 2, item.Y - valueSize.Height - 5));
    121                     }
    122                 }
    123             }
    124 
    125             //文本
    126 
    127             for (int i = 0; i < radarPositions.Length; i++)
    128             {
    129                 PointF point = lstRingPoints[0][i];
    130                 var txtSize = g.MeasureString(radarPositions[i].Text, Font);
    131 
    132                 if (point.X == centrePoint.X)
    133                 {
    134                     if (point.Y > centrePoint.Y)
    135                     {
    136                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
    137                     }
    138                     else
    139                     {
    140                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y - 10 - txtSize.Height));
    141                     }
    142                 }
    143                 else if (point.Y == centrePoint.Y)
    144                 {
    145                     if (point.X < centrePoint.X)
    146                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
    147                     else
    148                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - txtSize.Height / 2));
    149                 }
    150                 else if (point.X < centrePoint.X)//
    151                 {
    152                     if (point.Y < centrePoint.Y)//左上
    153                     {
    154                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - 10 + txtSize.Height / 2));
    155                     }
    156                     else//左下
    157                     {
    158                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y + 10 - txtSize.Height / 2));
    159                     }
    160                 }
    161                 else
    162                 {
    163                     if (point.Y < centrePoint.Y)//右上
    164                     {
    165                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
    166                     }
    167                     else//右下
    168                     {
    169                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y + 10 - txtSize.Height / 2));
    170                     }
    171                 }
    172             }
    173 
    174         }

    辅助函数

     1  #region 根据中心点、角度、半径计算圆边坐标点    English:Calculating the coordinate points of circular edge according to the center point, angle and radius
     2         /// <summary>
     3         /// 功能描述:根据中心点、角度、半径计算圆边坐标点    English:Calculating the coordinate points of circular edge according to the center point, angle and radius
     4         /// 作  者:HZH
     5         /// 创建日期:2019-09-25 09:46:32
     6         /// 任务编号:POS
     7         /// </summary>
     8         /// <param name="centrePoint">centrePoint</param>
     9         /// <param name="fltAngle">fltAngle</param>
    10         /// <param name="fltRadiusWidth">fltRadiusWidth</param>
    11         /// <returns>返回值</returns>
    12         private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
    13         {
    14             PointF p = centrePoint;
    15             if (fltAngle == 0)
    16             {
    17                 p.X += fltRadiusWidth;
    18             }
    19             else if (fltAngle == 90)
    20             {
    21                 p.Y += fltRadiusWidth;
    22             }
    23             else if (fltAngle == 180)
    24             {
    25                 p.X -= fltRadiusWidth;
    26             }
    27             else if (fltAngle == 270)
    28             {
    29                 p.Y -= fltRadiusWidth;
    30             }
    31             else if (fltAngle > 0 && fltAngle < 90)
    32             {
    33                 p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
    34                 p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
    35             }
    36             else if (fltAngle > 90 && fltAngle < 180)
    37             {
    38                 p.Y += (float)Math.Sin(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
    39                 p.X -= (float)Math.Cos(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
    40             }
    41             else if (fltAngle > 180 && fltAngle < 270)
    42             {
    43                 p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
    44                 p.X -= (float)Math.Cos(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
    45             }
    46             else if (fltAngle > 270 && fltAngle < 360)
    47             {
    48                 p.Y -= (float)Math.Sin(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
    49                 p.X += (float)Math.Cos(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
    50             }
    51             return p;
    52         }
    53         #endregion
    54 
    55         /// <summary>
    56         /// Resets the size of the title.
    57         /// </summary>
    58         private void ResetTitleSize()
    59         {
    60             if (!string.IsNullOrEmpty(title))
    61             {
    62                 using (Graphics g = this.CreateGraphics())
    63                 {
    64                     titleSize = g.MeasureString(title, titleFont);
    65                 }
    66             }
    67             else
    68             {
    69                 titleSize = SizeF.Empty;
    70             }
    71             titleSize.Height += 20;
    72             ResetWorkingRect();
    73         }

    完整代码

      1 // ***********************************************************************
      2 // Assembly         : HZH_Controls
      3 // Created          : 2019-09-25
      4 //
      5 // ***********************************************************************
      6 // <copyright file="UCRadarChart.cs">
      7 //     Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com
      8 // </copyright>
      9 //
     10 // Blog: https://www.cnblogs.com/bfyx
     11 // GitHub:https://github.com/kwwwvagaa/NetWinformControl
     12 // gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
     13 //
     14 // If you use this code, please keep this note.
     15 // ***********************************************************************
     16 using System;
     17 using System.Collections.Generic;
     18 using System.Linq;
     19 using System.Text;
     20 using System.Windows.Forms;
     21 using System.Drawing;
     22 using System.Drawing.Drawing2D;
     23 using System.ComponentModel;
     24 
     25 namespace HZH_Controls.Controls
     26 {
     27     /// <summary>
     28     /// Class UCRadarChart.
     29     /// Implements the <see cref="System.Windows.Forms.UserControl" />
     30     /// </summary>
     31     /// <seealso cref="System.Windows.Forms.UserControl" />
     32     public class UCRadarChart : UserControl
     33     {
     34         /// <summary>
     35         /// The split count
     36         /// </summary>
     37         private int splitCount = 5;
     38         /// <summary>
     39         /// Gets or sets the split count.
     40         /// </summary>
     41         /// <value>The split count.</value>
     42         [Browsable(true)]
     43         [Category("自定义")]
     44         [Description("获取或设置分隔份数")]
     45         public int SplitCount
     46         {
     47             get { return splitCount; }
     48             set
     49             {
     50                 splitCount = value;
     51                 Invalidate();
     52             }
     53         }
     54 
     55         /// <summary>
     56         /// The split odd color
     57         /// </summary>
     58         private Color splitOddColor = Color.White;
     59         /// <summary>
     60         /// 分隔奇数栏背景色
     61         /// </summary>
     62         /// <value>The color of the split odd.</value>
     63         [Browsable(true)]
     64         [Category("自定义")]
     65         [Description("获取或设置分隔奇数栏背景色")]
     66         public Color SplitOddColor
     67         {
     68             get { return splitOddColor; }
     69             set
     70             {
     71                 splitOddColor = value;
     72                 Invalidate();
     73             }
     74         }
     75         /// <summary>
     76         /// The split even color
     77         /// </summary>
     78         private Color splitEvenColor = Color.FromArgb(232, 232, 232);
     79         /// <summary>
     80         /// 分隔偶数栏背景色
     81         /// </summary>
     82         /// <value>The color of the split even.</value>
     83         [Browsable(true)]
     84         [Category("自定义")]
     85         [Description("获取或设置分隔偶数栏背景色")]
     86         public Color SplitEvenColor
     87         {
     88             get { return splitEvenColor; }
     89             set { splitEvenColor = value; }
     90         }
     91 
     92         /// <summary>
     93         /// The line color
     94         /// </summary>
     95         private Color lineColor = Color.FromArgb(153, 153, 153);
     96         /// <summary>
     97         /// Gets or sets the color of the line.
     98         /// </summary>
     99         /// <value>The color of the line.</value>
    100         [Browsable(true)]
    101         [Category("自定义")]
    102         [Description("获取或设置线条色")]
    103         public Color LineColor
    104         {
    105             get { return lineColor; }
    106             set
    107             {
    108                 lineColor = value;
    109                 Invalidate();
    110             }
    111         }
    112 
    113         /// <summary>
    114         /// The radar positions
    115         /// </summary>
    116         private RadarPosition[] radarPositions;
    117         /// <summary>
    118         /// 节点列表,至少需要3个
    119         /// </summary>
    120         /// <value>The radar positions.</value>
    121         [Browsable(true)]
    122         [Category("自定义")]
    123         [Description("获取或设置节点,至少需要3个")]
    124         public RadarPosition[] RadarPositions
    125         {
    126             get { return radarPositions; }
    127             set
    128             {
    129                 radarPositions = value;
    130                 Invalidate();
    131             }
    132         }
    133 
    134         /// <summary>
    135         /// The title
    136         /// </summary>
    137         private string title;
    138         /// <summary>
    139         /// 标题
    140         /// </summary>
    141         /// <value>The title.</value>
    142         [Browsable(true)]
    143         [Category("自定义")]
    144         [Description("获取或设置标题")]
    145         public string Title
    146         {
    147             get { return title; }
    148             set
    149             {
    150                 title = value;
    151                 ResetTitleSize();
    152                 Invalidate();
    153             }
    154         }
    155 
    156         /// <summary>
    157         /// The title font
    158         /// </summary>
    159         private Font titleFont = new Font("微软雅黑", 12);
    160         /// <summary>
    161         /// Gets or sets the title font.
    162         /// </summary>
    163         /// <value>The title font.</value>
    164         [Browsable(true)]
    165         [Category("自定义")]
    166         [Description("获取或设置标题字体")]
    167         public Font TitleFont
    168         {
    169             get { return titleFont; }
    170             set
    171             {
    172                 titleFont = value;
    173                 ResetTitleSize();
    174                 Invalidate();
    175             }
    176         }
    177 
    178         /// <summary>
    179         /// The title color
    180         /// </summary>
    181         private Color titleColor = Color.Black;
    182         /// <summary>
    183         /// Gets or sets the color of the title.
    184         /// </summary>
    185         /// <value>The color of the title.</value>
    186         [Browsable(true)]
    187         [Category("自定义")]
    188         [Description("获取或设置标题文本颜色")]
    189         public Color TitleColor
    190         {
    191             get { return titleColor; }
    192             set
    193             {
    194                 titleColor = value;
    195                 Invalidate();
    196             }
    197         }
    198 
    199         /// <summary>
    200         /// The lines
    201         /// </summary>
    202         private RadarLine[] lines;
    203         /// <summary>
    204         /// Gets or sets the lines.
    205         /// </summary>
    206         /// <value>The lines.</value>
    207         [Browsable(true)]
    208         [Category("自定义")]
    209         [Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")]
    210         public RadarLine[] Lines
    211         {
    212             get { return lines; }
    213             set
    214             {
    215                 lines = value;
    216                 Invalidate();
    217             }
    218         }
    219 
    220 
    221         /// <summary>
    222         /// The title size
    223         /// </summary>
    224         SizeF titleSize = SizeF.Empty;
    225         /// <summary>
    226         /// The m rect working
    227         /// </summary>
    228         private RectangleF m_rectWorking = Rectangle.Empty;
    229         /// <summary>
    230         /// The line value type size
    231         /// </summary>
    232         SizeF lineValueTypeSize = SizeF.Empty;
    233         /// <summary>
    234         /// The int line value COM count
    235         /// </summary>
    236         int intLineValueComCount = 0;
    237         /// <summary>
    238         /// The int line value row count
    239         /// </summary>
    240         int intLineValueRowCount = 0;
    241         /// <summary>
    242         /// Initializes a new instance of the <see cref="UCRadarChart"/> class.
    243         /// </summary>
    244         public UCRadarChart()
    245         {
    246             this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    247             this.SetStyle(ControlStyles.DoubleBuffer, true);
    248             this.SetStyle(ControlStyles.ResizeRedraw, true);
    249             this.SetStyle(ControlStyles.Selectable, true);
    250             this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    251             this.SetStyle(ControlStyles.UserPaint, true);
    252             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
    253             this.SizeChanged += UCRadarChart_SizeChanged;
    254             Size = new System.Drawing.Size(150, 150);
    255             radarPositions = new RadarPosition[0];
    256             if (ControlHelper.IsDesignMode())
    257             {
    258                 radarPositions = new RadarPosition[6];
    259                 for (int i = 0; i < 6; i++)
    260                 {
    261                     radarPositions[i] = new RadarPosition
    262                     {
    263                         Text = "Item" + (i + 1),
    264                         MaxValue = 100
    265                     };
    266                 }
    267             }
    268 
    269             lines = new RadarLine[0];
    270             if (ControlHelper.IsDesignMode())
    271             {
    272                 Random r = new Random();
    273                 lines = new RadarLine[2];
    274                 for (int i = 0; i < 2; i++)
    275                 {
    276                     lines[i] = new RadarLine()
    277                     {
    278                         Name = "line" + i
    279                     };
    280                     lines[i].Values = new double[radarPositions.Length];
    281                     for (int j = 0; j < radarPositions.Length; j++)
    282                     {
    283                         lines[i].Values[j] = r.Next(20, (int)radarPositions[j].MaxValue);
    284                     }
    285                 }
    286             }
    287         }
    288 
    289         /// <summary>
    290         /// Handles the SizeChanged event of the UCRadarChart control.
    291         /// </summary>
    292         /// <param name="sender">The source of the event.</param>
    293         /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    294         void UCRadarChart_SizeChanged(object sender, EventArgs e)
    295         {
    296             ResetWorkingRect();
    297         }
    298 
    299         /// <summary>
    300         /// Resets the working rect.
    301         /// </summary>
    302         private void ResetWorkingRect()
    303         {
    304             if (lines != null && lines.Length > 0)
    305             {
    306                 using (Graphics g = this.CreateGraphics())
    307                 {
    308                     foreach (var item in lines)
    309                     {
    310                         var s = g.MeasureString(item.Name, Font);
    311                         if (s.Width > lineValueTypeSize.Width)
    312                             lineValueTypeSize = s;
    313                     }
    314                 }
    315             }
    316             var lineTypePanelHeight = 0f;
    317             if (lineValueTypeSize != SizeF.Empty)
    318             {
    319                 intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + 25));
    320 
    321                 intLineValueRowCount = lines.Length / intLineValueComCount;
    322                 if (lines.Length % intLineValueComCount != 0)
    323                 {
    324                     intLineValueRowCount++;
    325                 }
    326                 lineTypePanelHeight = (lineValueTypeSize.Height + 10) * intLineValueRowCount;
    327             }
    328             var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
    329             var rectWorking = new RectangleF((this.Width - min) / 2 + 10, titleSize.Height + lineTypePanelHeight + 10, min - 10, min - 10);
    330             //处理文字
    331             float fltSplitAngle = 360F / radarPositions.Length;
    332             float fltRadiusWidth = rectWorking.Width / 2;
    333             float minX = rectWorking.Left;
    334             float maxX = rectWorking.Right;
    335             float minY = rectWorking.Top;
    336             float maxY = rectWorking.Bottom;
    337             using (Graphics g = this.CreateGraphics())
    338             {
    339                 PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / 2, rectWorking.Top + rectWorking.Height / 2);
    340                 for (int i = 0; i < radarPositions.Length; i++)
    341                 {
    342                     float fltAngle = 270 + fltSplitAngle * i;
    343                     fltAngle = fltAngle % 360;
    344                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
    345                     var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
    346                     if (_point.X < centrePoint.X)//
    347                     {
    348                         if (_point.X - _txtSize.Width < minX)
    349                         {
    350                             minX = rectWorking.Left + _txtSize.Width;
    351                         }
    352                     }
    353                     else//
    354                     {
    355                         if (_point.X + _txtSize.Width > maxX)
    356                         {
    357                             maxX = rectWorking.Right - _txtSize.Width;
    358                         }
    359                     }
    360                     if (_point.Y < centrePoint.Y)//
    361                     {
    362                         if (_point.Y - _txtSize.Height < minY)
    363                         {
    364                             minY = rectWorking.Top + _txtSize.Height;
    365                         }
    366                     }
    367                     else//
    368                     {
    369                         if (_point.Y + _txtSize.Height > maxY)
    370                         {
    371                             maxY = rectWorking.Bottom - _txtSize.Height;
    372                         }
    373                     }
    374                 }
    375             }
    376 
    377             min = Math.Min(maxX - minX, maxY - minY);
    378             m_rectWorking = new RectangleF(minX, minY, min, min);
    379         }
    380 
    381         /// <summary>
    382         /// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 事件。
    383         /// </summary>
    384         /// <param name="e">包含事件数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" /></param>
    385         protected override void OnPaint(PaintEventArgs e)
    386         {
    387             base.OnPaint(e);
    388             var g = e.Graphics;
    389             g.SetGDIHigh();
    390 
    391             if (!string.IsNullOrEmpty(title))
    392             {
    393                 g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / 2, m_rectWorking.Top - titleSize.Height - 10 - (intLineValueRowCount * (10 + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
    394             }
    395 
    396             if (radarPositions.Length <= 2)
    397             {
    398                 g.DrawString("至少需要3个顶点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
    399                 return;
    400             }
    401 
    402             var y = m_rectWorking.Top - 20 - (intLineValueRowCount * (10 + lineValueTypeSize.Height));
    403 
    404             for (int i = 0; i < intLineValueRowCount; i++)
    405             {
    406                 var x = 0f;
    407                 int intCount = intLineValueComCount;
    408                 if (i == intLineValueRowCount - 1)
    409                 {
    410                     intCount = lines.Length % intLineValueComCount;
    411 
    412                 }
    413                 x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + 25)) / 2;
    414 
    415                 for (int j = 0; j < intCount; j++)
    416                 {
    417                     g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + 25)*j, y + lineValueTypeSize.Height * i, 15, lineValueTypeSize.Height));
    418                     g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + 25) * j + 20, y + lineValueTypeSize.Height * i));
    419                 }
    420             }
    421 
    422             float fltSplitAngle = 360F / radarPositions.Length;
    423             float fltRadiusWidth = m_rectWorking.Width / 2;
    424             float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
    425             PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / 2, m_rectWorking.Top + m_rectWorking.Height / 2);
    426 
    427             List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
    428             //分割点
    429             for (int i = 0; i < radarPositions.Length; i++)
    430             {
    431                 float fltAngle = 270 + fltSplitAngle * i;
    432                 fltAngle = fltAngle % 360;
    433                 for (int j = 0; j < splitCount; j++)
    434                 {
    435                     if (i == 0)
    436                     {
    437                         lstRingPoints.Add(new List<PointF>());
    438                     }
    439                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
    440                     lstRingPoints[j].Add(_point);
    441                 }
    442             }
    443 
    444             for (int i = 0; i < lstRingPoints.Count; i++)
    445             {
    446                 var ring = lstRingPoints[i];
    447                 GraphicsPath path = new GraphicsPath();
    448                 path.AddLines(ring.ToArray());
    449                 if ((lstRingPoints.Count - i) % 2 == 0)
    450                 {
    451                     g.FillPath(new SolidBrush(splitEvenColor), path);
    452                 }
    453                 else
    454                 {
    455                     g.FillPath(new SolidBrush(splitOddColor), path);
    456                 }
    457             }
    458 
    459             //画环
    460             foreach (var ring in lstRingPoints)
    461             {
    462                 ring.Add(ring[0]);
    463                 g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
    464             }
    465             //分割线
    466             foreach (var item in lstRingPoints[0])
    467             {
    468                 g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
    469             }
    470 
    471             //
    472             for (int i = 0; i < lines.Length; i++)
    473             {
    474                 var line = lines[i];
    475                 if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
    476                     continue;
    477                 if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
    478                     line.LineColor = ControlHelper.Colors[i + 13];
    479                 List<PointF> ps = new List<PointF>();
    480                 for (int j = 0; j < radarPositions.Length; j++)
    481                 {
    482                     float fltAngle = 270 + fltSplitAngle * j;
    483                     fltAngle = fltAngle % 360;
    484                     PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
    485                     ps.Add(_point);
    486                 }
    487                 ps.Add(ps[0]);
    488                 if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
    489                 {
    490                     GraphicsPath path = new GraphicsPath();
    491                     path.AddLines(ps.ToArray());
    492                     g.FillPath(new SolidBrush(line.FillColor.Value), path);
    493                 }
    494                 g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), 2), ps.ToArray());
    495 
    496                 for (int j = 0; j < radarPositions.Length; j++)
    497                 {
    498                     var item = ps[j];
    499                     g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
    500                     g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - 3, item.Y - 3, 6, 6));
    501                     if (line.ShowValueText)
    502                     {
    503                         var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
    504                         g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / 2, item.Y - valueSize.Height - 5));
    505                     }
    506                 }
    507             }
    508 
    509             //文本
    510 
    511             for (int i = 0; i < radarPositions.Length; i++)
    512             {
    513                 PointF point = lstRingPoints[0][i];
    514                 var txtSize = g.MeasureString(radarPositions[i].Text, Font);
    515 
    516                 if (point.X == centrePoint.X)
    517                 {
    518                     if (point.Y > centrePoint.Y)
    519                     {
    520                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y + 10));
    521                     }
    522                     else
    523                     {
    524                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / 2, point.Y - 10 - txtSize.Height));
    525                     }
    526                 }
    527                 else if (point.Y == centrePoint.Y)
    528                 {
    529                     if (point.X < centrePoint.X)
    530                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - txtSize.Height / 2));
    531                     else
    532                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - txtSize.Height / 2));
    533                 }
    534                 else if (point.X < centrePoint.X)//
    535                 {
    536                     if (point.Y < centrePoint.Y)//左上
    537                     {
    538                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y - 10 + txtSize.Height / 2));
    539                     }
    540                     else//左下
    541                     {
    542                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - 10 - txtSize.Width, point.Y + 10 - txtSize.Height / 2));
    543                     }
    544                 }
    545                 else
    546                 {
    547                     if (point.Y < centrePoint.Y)//右上
    548                     {
    549                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y - 10 + txtSize.Height / 2));
    550                     }
    551                     else//右下
    552                     {
    553                         g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + 10, point.Y + 10 - txtSize.Height / 2));
    554                     }
    555                 }
    556             }
    557 
    558         }
    559 
    560         #region 根据中心点、角度、半径计算圆边坐标点    English:Calculating the coordinate points of circular edge according to the center point, angle and radius
    561         /// <summary>
    562         /// 功能描述:根据中心点、角度、半径计算圆边坐标点    English:Calculating the coordinate points of circular edge according to the center point, angle and radius
    563         /// 作  者:HZH
    564         /// 创建日期:2019-09-25 09:46:32
    565         /// 任务编号:POS
    566         /// </summary>
    567         /// <param name="centrePoint">centrePoint</param>
    568         /// <param name="fltAngle">fltAngle</param>
    569         /// <param name="fltRadiusWidth">fltRadiusWidth</param>
    570         /// <returns>返回值</returns>
    571         private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
    572         {
    573             PointF p = centrePoint;
    574             if (fltAngle == 0)
    575             {
    576                 p.X += fltRadiusWidth;
    577             }
    578             else if (fltAngle == 90)
    579             {
    580                 p.Y += fltRadiusWidth;
    581             }
    582             else if (fltAngle == 180)
    583             {
    584                 p.X -= fltRadiusWidth;
    585             }
    586             else if (fltAngle == 270)
    587             {
    588                 p.Y -= fltRadiusWidth;
    589             }
    590             else if (fltAngle > 0 && fltAngle < 90)
    591             {
    592                 p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
    593                 p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
    594             }
    595             else if (fltAngle > 90 && fltAngle < 180)
    596             {
    597                 p.Y += (float)Math.Sin(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
    598                 p.X -= (float)Math.Cos(Math.PI * ((180 - fltAngle) / 180.00F)) * fltRadiusWidth;
    599             }
    600             else if (fltAngle > 180 && fltAngle < 270)
    601             {
    602                 p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
    603                 p.X -= (float)Math.Cos(Math.PI * ((fltAngle - 180) / 180.00F)) * fltRadiusWidth;
    604             }
    605             else if (fltAngle > 270 && fltAngle < 360)
    606             {
    607                 p.Y -= (float)Math.Sin(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
    608                 p.X += (float)Math.Cos(Math.PI * ((360 - fltAngle) / 180.00F)) * fltRadiusWidth;
    609             }
    610             return p;
    611         }
    612         #endregion
    613 
    614         /// <summary>
    615         /// Resets the size of the title.
    616         /// </summary>
    617         private void ResetTitleSize()
    618         {
    619             if (!string.IsNullOrEmpty(title))
    620             {
    621                 using (Graphics g = this.CreateGraphics())
    622                 {
    623                     titleSize = g.MeasureString(title, titleFont);
    624                 }
    625             }
    626             else
    627             {
    628                 titleSize = SizeF.Empty;
    629             }
    630             titleSize.Height += 20;
    631             ResetWorkingRect();
    632         }
    633     }
    634 }
    View Code

    最后的话

    如果你喜欢的话,请到 https://gitee.com/kwwwvagaa/net_winform_custom_control 点个星星吧

  • 相关阅读:
    理解mipi协议【转】
    Using KernelShark to analyze the real-time scheduler【转】
    内核栈溢出【转】
    检测内核的堆栈溢出【转】
    gcc 编译 + 选项【转】
    service mysqld start 报错:service mysqld start 报错 090517 13:34:15 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it. 090Can't open the mysql.plugin table. Please run mysql
    【推荐】MySQL Cluster报错及解决方法(不断更新中)
    MySQL Cluster 配置文件(config.ini)详解
    关于MySQL Cluster集群NoOfReplicas参数问题
    通过PHP current()函数获取未知字符键名数组第一个元素的值
  • 原文地址:https://www.cnblogs.com/bfyx/p/11584514.html
Copyright © 2020-2023  润新知