官网

http://www.hzhcontrols.com

前提

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

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

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

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

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

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

NuGet

Install-Package HZH_Controls

目录

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

用处及效果

准备工作

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

开始

添加一个类UCRadarChart ,继承 UserControl

添加一些控制属性

  /// <summary>
/// The split count
/// </summary>
private int splitCount = ;
/// <summary>
/// Gets or sets the split count.
/// </summary>
/// <value>The split count.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔份数")]
public int SplitCount
{
get { return splitCount; }
set
{
splitCount = value;
Invalidate();
}
} /// <summary>
/// The split odd color
/// </summary>
private Color splitOddColor = Color.White;
/// <summary>
/// 分隔奇数栏背景色
/// </summary>
/// <value>The color of the split odd.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔奇数栏背景色")]
public Color SplitOddColor
{
get { return splitOddColor; }
set
{
splitOddColor = value;
Invalidate();
}
}
/// <summary>
/// The split even color
/// </summary>
private Color splitEvenColor = Color.FromArgb(, , );
/// <summary>
/// 分隔偶数栏背景色
/// </summary>
/// <value>The color of the split even.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔偶数栏背景色")]
public Color SplitEvenColor
{
get { return splitEvenColor; }
set { splitEvenColor = value; }
} /// <summary>
/// The line color
/// </summary>
private Color lineColor = Color.FromArgb(, , );
/// <summary>
/// Gets or sets the color of the line.
/// </summary>
/// <value>The color of the line.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置线条色")]
public Color LineColor
{
get { return lineColor; }
set
{
lineColor = value;
Invalidate();
}
} /// <summary>
/// The radar positions
/// </summary>
private RadarPosition[] radarPositions;
/// <summary>
/// 节点列表,至少需要3个
/// </summary>
/// <value>The radar positions.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置节点,至少需要3个")]
public RadarPosition[] RadarPositions
{
get { return radarPositions; }
set
{
radarPositions = value;
Invalidate();
}
} /// <summary>
/// The title
/// </summary>
private string title;
/// <summary>
/// 标题
/// </summary>
/// <value>The title.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题")]
public string Title
{
get { return title; }
set
{
title = value;
ResetTitleSize();
Invalidate();
}
} /// <summary>
/// The title font
/// </summary>
private Font titleFont = new Font("微软雅黑", );
/// <summary>
/// Gets or sets the title font.
/// </summary>
/// <value>The title font.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题字体")]
public Font TitleFont
{
get { return titleFont; }
set
{
titleFont = value;
ResetTitleSize();
Invalidate();
}
} /// <summary>
/// The title color
/// </summary>
private Color titleColor = Color.Black;
/// <summary>
/// Gets or sets the color of the title.
/// </summary>
/// <value>The color of the title.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题文本颜色")]
public Color TitleColor
{
get { return titleColor; }
set
{
titleColor = value;
Invalidate();
}
} /// <summary>
/// The lines
/// </summary>
private RadarLine[] lines;
/// <summary>
/// Gets or sets the lines.
/// </summary>
/// <value>The lines.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")]
public RadarLine[] Lines
{
get { return lines; }
set
{
lines = value;
Invalidate();
}
} /// <summary>
/// The title size
/// </summary>
SizeF titleSize = SizeF.Empty;
/// <summary>
/// The m rect working
/// </summary>
private RectangleF m_rectWorking = Rectangle.Empty;
/// <summary>
/// The line value type size
/// </summary>
SizeF lineValueTypeSize = SizeF.Empty;
/// <summary>
/// The int line value COM count
/// </summary>
int intLineValueComCount = ;
/// <summary>
/// The int line value row count
/// </summary>
int intLineValueRowCount = ;

属性改变时处理工作区域

  /// <summary>
/// Handles the SizeChanged event of the UCRadarChart control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
void UCRadarChart_SizeChanged(object sender, EventArgs e)
{
ResetWorkingRect();
} /// <summary>
/// Resets the working rect.
/// </summary>
private void ResetWorkingRect()
{
if (lines != null && lines.Length > )
{
using (Graphics g = this.CreateGraphics())
{
foreach (var item in lines)
{
var s = g.MeasureString(item.Name, Font);
if (s.Width > lineValueTypeSize.Width)
lineValueTypeSize = s;
}
}
}
var lineTypePanelHeight = 0f;
if (lineValueTypeSize != SizeF.Empty)
{
intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + )); intLineValueRowCount = lines.Length / intLineValueComCount;
if (lines.Length % intLineValueComCount != )
{
intLineValueRowCount++;
}
lineTypePanelHeight = (lineValueTypeSize.Height + ) * intLineValueRowCount;
}
var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
var rectWorking = new RectangleF((this.Width - min) / + , titleSize.Height + lineTypePanelHeight + , min - , min - );
//处理文字
float fltSplitAngle = 360F / radarPositions.Length;
float fltRadiusWidth = rectWorking.Width / ;
float minX = rectWorking.Left;
float maxX = rectWorking.Right;
float minY = rectWorking.Top;
float maxY = rectWorking.Bottom;
using (Graphics g = this.CreateGraphics())
{
PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / , rectWorking.Top + rectWorking.Height / );
for (int i = ; i < radarPositions.Length; i++)
{
float fltAngle = + fltSplitAngle * i;
fltAngle = fltAngle % ;
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
if (_point.X < centrePoint.X)//左
{
if (_point.X - _txtSize.Width < minX)
{
minX = rectWorking.Left + _txtSize.Width;
}
}
else//右
{
if (_point.X + _txtSize.Width > maxX)
{
maxX = rectWorking.Right - _txtSize.Width;
}
}
if (_point.Y < centrePoint.Y)//上
{
if (_point.Y - _txtSize.Height < minY)
{
minY = rectWorking.Top + _txtSize.Height;
}
}
else//下
{
if (_point.Y + _txtSize.Height > maxY)
{
maxY = rectWorking.Bottom - _txtSize.Height;
}
}
}
} min = Math.Min(maxX - minX, maxY - minY);
m_rectWorking = new RectangleF(minX, minY, min, min);
}

重绘

 protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
g.SetGDIHigh(); if (!string.IsNullOrEmpty(title))
{
g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / , m_rectWorking.Top - titleSize.Height - - (intLineValueRowCount * ( + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
} if (radarPositions.Length <= )
{
g.DrawString("至少需要3个顶点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
return;
} var y = m_rectWorking.Top - - (intLineValueRowCount * ( + lineValueTypeSize.Height)); for (int i = ; i < intLineValueRowCount; i++)
{
var x = 0f;
int intCount = intLineValueComCount;
if (i == intLineValueRowCount - )
{
intCount = lines.Length % intLineValueComCount; }
x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + )) / ; for (int j = ; j < intCount; j++)
{
g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + )*j, y + lineValueTypeSize.Height * i, , lineValueTypeSize.Height));
g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + ) * j + , y + lineValueTypeSize.Height * i));
}
} float fltSplitAngle = 360F / radarPositions.Length;
float fltRadiusWidth = m_rectWorking.Width / ;
float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / , m_rectWorking.Top + m_rectWorking.Height / ); List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
//分割点
for (int i = ; i < radarPositions.Length; i++)
{
float fltAngle = + fltSplitAngle * i;
fltAngle = fltAngle % ;
for (int j = ; j < splitCount; j++)
{
if (i == )
{
lstRingPoints.Add(new List<PointF>());
}
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
lstRingPoints[j].Add(_point);
}
} for (int i = ; i < lstRingPoints.Count; i++)
{
var ring = lstRingPoints[i];
GraphicsPath path = new GraphicsPath();
path.AddLines(ring.ToArray());
if ((lstRingPoints.Count - i) % == )
{
g.FillPath(new SolidBrush(splitEvenColor), path);
}
else
{
g.FillPath(new SolidBrush(splitOddColor), path);
}
} //画环
foreach (var ring in lstRingPoints)
{
ring.Add(ring[]);
g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
}
//分割线
foreach (var item in lstRingPoints[])
{
g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
} //值
for (int i = ; i < lines.Length; i++)
{
var line = lines[i];
if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
continue;
if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
line.LineColor = ControlHelper.Colors[i + ];
List<PointF> ps = new List<PointF>();
for (int j = ; j < radarPositions.Length; j++)
{
float fltAngle = + fltSplitAngle * j;
fltAngle = fltAngle % ;
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
ps.Add(_point);
}
ps.Add(ps[]);
if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
{
GraphicsPath path = new GraphicsPath();
path.AddLines(ps.ToArray());
g.FillPath(new SolidBrush(line.FillColor.Value), path);
}
g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), ), ps.ToArray()); for (int j = ; j < radarPositions.Length; j++)
{
var item = ps[j];
g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - , item.Y - , , ));
g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - , item.Y - , , ));
if (line.ShowValueText)
{
var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / , item.Y - valueSize.Height - ));
}
}
} //文本 for (int i = ; i < radarPositions.Length; i++)
{
PointF point = lstRingPoints[][i];
var txtSize = g.MeasureString(radarPositions[i].Text, Font); if (point.X == centrePoint.X)
{
if (point.Y > centrePoint.Y)
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / , point.Y + ));
}
else
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / , point.Y - - txtSize.Height));
}
}
else if (point.Y == centrePoint.Y)
{
if (point.X < centrePoint.X)
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y - txtSize.Height / ));
else
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y - txtSize.Height / ));
}
else if (point.X < centrePoint.X)//左
{
if (point.Y < centrePoint.Y)//左上
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y - + txtSize.Height / ));
}
else//左下
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y + - txtSize.Height / ));
}
}
else
{
if (point.Y < centrePoint.Y)//右上
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y - + txtSize.Height / ));
}
else//右下
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y + - txtSize.Height / ));
}
}
} }

辅助函数

  #region 根据中心点、角度、半径计算圆边坐标点    English:Calculating the coordinate points of circular edge according to the center point, angle and radius
/// <summary>
/// 功能描述:根据中心点、角度、半径计算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
/// 作  者:HZH
/// 创建日期:2019-09-25 09:46:32
/// 任务编号:POS
/// </summary>
/// <param name="centrePoint">centrePoint</param>
/// <param name="fltAngle">fltAngle</param>
/// <param name="fltRadiusWidth">fltRadiusWidth</param>
/// <returns>返回值</returns>
private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
{
PointF p = centrePoint;
if (fltAngle == )
{
p.X += fltRadiusWidth;
}
else if (fltAngle == )
{
p.Y += fltRadiusWidth;
}
else if (fltAngle == )
{
p.X -= fltRadiusWidth;
}
else if (fltAngle == )
{
p.Y -= fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y += (float)Math.Sin(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
p.X -= (float)Math.Cos(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - ) / 180.00F)) * fltRadiusWidth;
p.X -= (float)Math.Cos(Math.PI * ((fltAngle - ) / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y -= (float)Math.Sin(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
p.X += (float)Math.Cos(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
}
return p;
}
#endregion /// <summary>
/// Resets the size of the title.
/// </summary>
private void ResetTitleSize()
{
if (!string.IsNullOrEmpty(title))
{
using (Graphics g = this.CreateGraphics())
{
titleSize = g.MeasureString(title, titleFont);
}
}
else
{
titleSize = SizeF.Empty;
}
titleSize.Height += ;
ResetWorkingRect();
}

完整代码

 // ***********************************************************************
// Assembly : HZH_Controls
// Created : 2019-09-25
//
// ***********************************************************************
// <copyright file="UCRadarChart.cs">
// Copyright by Huang Zhenghui(黄正辉) All, QQ group:568015492 QQ:623128629 Email:623128629@qq.com
// </copyright>
//
// Blog: https://www.cnblogs.com/bfyx
// GitHub:https://github.com/kwwwvagaa/NetWinformControl
// gitee:https://gitee.com/kwwwvagaa/net_winform_custom_control.git
//
// If you use this code, please keep this note.
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel; namespace HZH_Controls.Controls
{
/// <summary>
/// Class UCRadarChart.
/// Implements the <see cref="System.Windows.Forms.UserControl" />
/// </summary>
/// <seealso cref="System.Windows.Forms.UserControl" />
public class UCRadarChart : UserControl
{
/// <summary>
/// The split count
/// </summary>
private int splitCount = ;
/// <summary>
/// Gets or sets the split count.
/// </summary>
/// <value>The split count.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔份数")]
public int SplitCount
{
get { return splitCount; }
set
{
splitCount = value;
Invalidate();
}
} /// <summary>
/// The split odd color
/// </summary>
private Color splitOddColor = Color.White;
/// <summary>
/// 分隔奇数栏背景色
/// </summary>
/// <value>The color of the split odd.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔奇数栏背景色")]
public Color SplitOddColor
{
get { return splitOddColor; }
set
{
splitOddColor = value;
Invalidate();
}
}
/// <summary>
/// The split even color
/// </summary>
private Color splitEvenColor = Color.FromArgb(, , );
/// <summary>
/// 分隔偶数栏背景色
/// </summary>
/// <value>The color of the split even.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置分隔偶数栏背景色")]
public Color SplitEvenColor
{
get { return splitEvenColor; }
set { splitEvenColor = value; }
} /// <summary>
/// The line color
/// </summary>
private Color lineColor = Color.FromArgb(, , );
/// <summary>
/// Gets or sets the color of the line.
/// </summary>
/// <value>The color of the line.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置线条色")]
public Color LineColor
{
get { return lineColor; }
set
{
lineColor = value;
Invalidate();
}
} /// <summary>
/// The radar positions
/// </summary>
private RadarPosition[] radarPositions;
/// <summary>
/// 节点列表,至少需要3个
/// </summary>
/// <value>The radar positions.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置节点,至少需要3个")]
public RadarPosition[] RadarPositions
{
get { return radarPositions; }
set
{
radarPositions = value;
Invalidate();
}
} /// <summary>
/// The title
/// </summary>
private string title;
/// <summary>
/// 标题
/// </summary>
/// <value>The title.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题")]
public string Title
{
get { return title; }
set
{
title = value;
ResetTitleSize();
Invalidate();
}
} /// <summary>
/// The title font
/// </summary>
private Font titleFont = new Font("微软雅黑", );
/// <summary>
/// Gets or sets the title font.
/// </summary>
/// <value>The title font.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题字体")]
public Font TitleFont
{
get { return titleFont; }
set
{
titleFont = value;
ResetTitleSize();
Invalidate();
}
} /// <summary>
/// The title color
/// </summary>
private Color titleColor = Color.Black;
/// <summary>
/// Gets or sets the color of the title.
/// </summary>
/// <value>The color of the title.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置标题文本颜色")]
public Color TitleColor
{
get { return titleColor; }
set
{
titleColor = value;
Invalidate();
}
} /// <summary>
/// The lines
/// </summary>
private RadarLine[] lines;
/// <summary>
/// Gets or sets the lines.
/// </summary>
/// <value>The lines.</value>
[Browsable(true)]
[Category("自定义")]
[Description("获取或设置值线条,Values长度必须与RadarPositions长度一致,否则无法显示")]
public RadarLine[] Lines
{
get { return lines; }
set
{
lines = value;
Invalidate();
}
} /// <summary>
/// The title size
/// </summary>
SizeF titleSize = SizeF.Empty;
/// <summary>
/// The m rect working
/// </summary>
private RectangleF m_rectWorking = Rectangle.Empty;
/// <summary>
/// The line value type size
/// </summary>
SizeF lineValueTypeSize = SizeF.Empty;
/// <summary>
/// The int line value COM count
/// </summary>
int intLineValueComCount = ;
/// <summary>
/// The int line value row count
/// </summary>
int intLineValueRowCount = ;
/// <summary>
/// Initializes a new instance of the <see cref="UCRadarChart"/> class.
/// </summary>
public UCRadarChart()
{
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.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.SizeChanged += UCRadarChart_SizeChanged;
Size = new System.Drawing.Size(, );
radarPositions = new RadarPosition[];
if (ControlHelper.IsDesignMode())
{
radarPositions = new RadarPosition[];
for (int i = ; i < ; i++)
{
radarPositions[i] = new RadarPosition
{
Text = "Item" + (i + ),
MaxValue =
};
}
} lines = new RadarLine[];
if (ControlHelper.IsDesignMode())
{
Random r = new Random();
lines = new RadarLine[];
for (int i = ; i < ; i++)
{
lines[i] = new RadarLine()
{
Name = "line" + i
};
lines[i].Values = new double[radarPositions.Length];
for (int j = ; j < radarPositions.Length; j++)
{
lines[i].Values[j] = r.Next(, (int)radarPositions[j].MaxValue);
}
}
}
} /// <summary>
/// Handles the SizeChanged event of the UCRadarChart control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
void UCRadarChart_SizeChanged(object sender, EventArgs e)
{
ResetWorkingRect();
} /// <summary>
/// Resets the working rect.
/// </summary>
private void ResetWorkingRect()
{
if (lines != null && lines.Length > )
{
using (Graphics g = this.CreateGraphics())
{
foreach (var item in lines)
{
var s = g.MeasureString(item.Name, Font);
if (s.Width > lineValueTypeSize.Width)
lineValueTypeSize = s;
}
}
}
var lineTypePanelHeight = 0f;
if (lineValueTypeSize != SizeF.Empty)
{
intLineValueComCount = (int)(this.Width / (lineValueTypeSize.Width + )); intLineValueRowCount = lines.Length / intLineValueComCount;
if (lines.Length % intLineValueComCount != )
{
intLineValueRowCount++;
}
lineTypePanelHeight = (lineValueTypeSize.Height + ) * intLineValueRowCount;
}
var min = Math.Min(this.Width, this.Height - titleSize.Height - lineTypePanelHeight);
var rectWorking = new RectangleF((this.Width - min) / + , titleSize.Height + lineTypePanelHeight + , min - , min - );
//处理文字
float fltSplitAngle = 360F / radarPositions.Length;
float fltRadiusWidth = rectWorking.Width / ;
float minX = rectWorking.Left;
float maxX = rectWorking.Right;
float minY = rectWorking.Top;
float maxY = rectWorking.Bottom;
using (Graphics g = this.CreateGraphics())
{
PointF centrePoint = new PointF(rectWorking.Left + rectWorking.Width / , rectWorking.Top + rectWorking.Height / );
for (int i = ; i < radarPositions.Length; i++)
{
float fltAngle = + fltSplitAngle * i;
fltAngle = fltAngle % ;
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth);
var _txtSize = g.MeasureString(radarPositions[i].Text, Font);
if (_point.X < centrePoint.X)//左
{
if (_point.X - _txtSize.Width < minX)
{
minX = rectWorking.Left + _txtSize.Width;
}
}
else//右
{
if (_point.X + _txtSize.Width > maxX)
{
maxX = rectWorking.Right - _txtSize.Width;
}
}
if (_point.Y < centrePoint.Y)//上
{
if (_point.Y - _txtSize.Height < minY)
{
minY = rectWorking.Top + _txtSize.Height;
}
}
else//下
{
if (_point.Y + _txtSize.Height > maxY)
{
maxY = rectWorking.Bottom - _txtSize.Height;
}
}
}
} min = Math.Min(maxX - minX, maxY - minY);
m_rectWorking = new RectangleF(minX, minY, min, min);
} /// <summary>
/// 引发 <see cref="E:System.Windows.Forms.Control.Paint" /> 事件。
/// </summary>
/// <param name="e">包含事件数据的 <see cref="T:System.Windows.Forms.PaintEventArgs" />。</param>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
g.SetGDIHigh(); if (!string.IsNullOrEmpty(title))
{
g.DrawString(title, titleFont, new SolidBrush(titleColor), new RectangleF(m_rectWorking.Left + (m_rectWorking.Width - titleSize.Width) / , m_rectWorking.Top - titleSize.Height - - (intLineValueRowCount * ( + lineValueTypeSize.Height)), titleSize.Width, titleSize.Height));
} if (radarPositions.Length <= )
{
g.DrawString("至少需要3个顶点", Font, new SolidBrush(Color.Black), m_rectWorking, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
return;
} var y = m_rectWorking.Top - - (intLineValueRowCount * ( + lineValueTypeSize.Height)); for (int i = ; i < intLineValueRowCount; i++)
{
var x = 0f;
int intCount = intLineValueComCount;
if (i == intLineValueRowCount - )
{
intCount = lines.Length % intLineValueComCount; }
x = m_rectWorking.Left + (m_rectWorking.Width - intCount * (lineValueTypeSize.Width + )) / ; for (int j = ; j < intCount; j++)
{
g.FillRectangle(new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new RectangleF(x + (lineValueTypeSize.Width + )*j, y + lineValueTypeSize.Height * i, , lineValueTypeSize.Height));
g.DrawString(lines[i * intLineValueComCount + j].Name, Font, new SolidBrush(lines[i * intLineValueComCount + j].LineColor.Value), new PointF(x + (lineValueTypeSize.Width + ) * j + , y + lineValueTypeSize.Height * i));
}
} float fltSplitAngle = 360F / radarPositions.Length;
float fltRadiusWidth = m_rectWorking.Width / ;
float fltSplitRadiusWidth = fltRadiusWidth / splitCount;
PointF centrePoint = new PointF(m_rectWorking.Left + m_rectWorking.Width / , m_rectWorking.Top + m_rectWorking.Height / ); List<List<PointF>> lstRingPoints = new List<List<PointF>>(splitCount);
//分割点
for (int i = ; i < radarPositions.Length; i++)
{
float fltAngle = + fltSplitAngle * i;
fltAngle = fltAngle % ;
for (int j = ; j < splitCount; j++)
{
if (i == )
{
lstRingPoints.Add(new List<PointF>());
}
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltSplitRadiusWidth * (splitCount - j));
lstRingPoints[j].Add(_point);
}
} for (int i = ; i < lstRingPoints.Count; i++)
{
var ring = lstRingPoints[i];
GraphicsPath path = new GraphicsPath();
path.AddLines(ring.ToArray());
if ((lstRingPoints.Count - i) % == )
{
g.FillPath(new SolidBrush(splitEvenColor), path);
}
else
{
g.FillPath(new SolidBrush(splitOddColor), path);
}
} //画环
foreach (var ring in lstRingPoints)
{
ring.Add(ring[]);
g.DrawLines(new Pen(new SolidBrush(lineColor)), ring.ToArray());
}
//分割线
foreach (var item in lstRingPoints[])
{
g.DrawLine(new Pen(new SolidBrush(lineColor)), centrePoint, item);
} //值
for (int i = ; i < lines.Length; i++)
{
var line = lines[i];
if (line.Values.Length != radarPositions.Length)//如果数据长度和节点长度不一致则不绘制
continue;
if (line.LineColor == null || line.LineColor == Color.Empty || line.LineColor == Color.Transparent)
line.LineColor = ControlHelper.Colors[i + ];
List<PointF> ps = new List<PointF>();
for (int j = ; j < radarPositions.Length; j++)
{
float fltAngle = + fltSplitAngle * j;
fltAngle = fltAngle % ;
PointF _point = GetPointByAngle(centrePoint, fltAngle, fltRadiusWidth * (float)(line.Values[j] / radarPositions[i].MaxValue));
ps.Add(_point);
}
ps.Add(ps[]);
if (line.FillColor != null && line.FillColor != Color.Empty && line.FillColor != Color.Transparent)
{
GraphicsPath path = new GraphicsPath();
path.AddLines(ps.ToArray());
g.FillPath(new SolidBrush(line.FillColor.Value), path);
}
g.DrawLines(new Pen(new SolidBrush(line.LineColor.Value), ), ps.ToArray()); for (int j = ; j < radarPositions.Length; j++)
{
var item = ps[j];
g.FillEllipse(new SolidBrush(Color.White), new RectangleF(item.X - , item.Y - , , ));
g.DrawEllipse(new Pen(new SolidBrush(line.LineColor.Value)), new RectangleF(item.X - , item.Y - , , ));
if (line.ShowValueText)
{
var valueSize = g.MeasureString(line.Values[j].ToString("0.##"), Font);
g.DrawString(line.Values[j].ToString("0.##"), Font, new SolidBrush(line.LineColor.Value), new PointF(item.X - valueSize.Width / , item.Y - valueSize.Height - ));
}
}
} //文本 for (int i = ; i < radarPositions.Length; i++)
{
PointF point = lstRingPoints[][i];
var txtSize = g.MeasureString(radarPositions[i].Text, Font); if (point.X == centrePoint.X)
{
if (point.Y > centrePoint.Y)
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / , point.Y + ));
}
else
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - txtSize.Width / , point.Y - - txtSize.Height));
}
}
else if (point.Y == centrePoint.Y)
{
if (point.X < centrePoint.X)
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y - txtSize.Height / ));
else
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y - txtSize.Height / ));
}
else if (point.X < centrePoint.X)//左
{
if (point.Y < centrePoint.Y)//左上
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y - + txtSize.Height / ));
}
else//左下
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X - - txtSize.Width, point.Y + - txtSize.Height / ));
}
}
else
{
if (point.Y < centrePoint.Y)//右上
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y - + txtSize.Height / ));
}
else//右下
{
g.DrawString(radarPositions[i].Text, Font, new SolidBrush(ForeColor), new PointF(point.X + , point.Y + - txtSize.Height / ));
}
}
} } #region 根据中心点、角度、半径计算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
/// <summary>
/// 功能描述:根据中心点、角度、半径计算圆边坐标点 English:Calculating the coordinate points of circular edge according to the center point, angle and radius
/// 作  者:HZH
/// 创建日期:2019-09-25 09:46:32
/// 任务编号:POS
/// </summary>
/// <param name="centrePoint">centrePoint</param>
/// <param name="fltAngle">fltAngle</param>
/// <param name="fltRadiusWidth">fltRadiusWidth</param>
/// <returns>返回值</returns>
private PointF GetPointByAngle(PointF centrePoint, float fltAngle, float fltRadiusWidth)
{
PointF p = centrePoint;
if (fltAngle == )
{
p.X += fltRadiusWidth;
}
else if (fltAngle == )
{
p.Y += fltRadiusWidth;
}
else if (fltAngle == )
{
p.X -= fltRadiusWidth;
}
else if (fltAngle == )
{
p.Y -= fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y += (float)Math.Sin(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
p.X += (float)Math.Cos(Math.PI * (fltAngle / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y += (float)Math.Sin(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
p.X -= (float)Math.Cos(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y -= (float)Math.Sin(Math.PI * ((fltAngle - ) / 180.00F)) * fltRadiusWidth;
p.X -= (float)Math.Cos(Math.PI * ((fltAngle - ) / 180.00F)) * fltRadiusWidth;
}
else if (fltAngle > && fltAngle < )
{
p.Y -= (float)Math.Sin(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
p.X += (float)Math.Cos(Math.PI * (( - fltAngle) / 180.00F)) * fltRadiusWidth;
}
return p;
}
#endregion /// <summary>
/// Resets the size of the title.
/// </summary>
private void ResetTitleSize()
{
if (!string.IsNullOrEmpty(title))
{
using (Graphics g = this.CreateGraphics())
{
titleSize = g.MeasureString(title, titleFont);
}
}
else
{
titleSize = SizeF.Empty;
}
titleSize.Height += ;
ResetWorkingRect();
}
}
}

最后的话

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

(七十二)c#Winform自定义控件-雷达图的更多相关文章

  1. (七十)c#Winform自定义控件-饼状图

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  2. 【基于WinForm+Access局域网共享数据库的项目总结】之篇二:WinForm开发扇形图统计和Excel数据导出

    篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...

  3. “全栈2019”Java第七十二章:静态内部类访问外部类成员

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. TNFE-Weekly[第七十二周已更新]

    前端行业发展飞速,新技术如雨后春笋般快速出现,尤其是各种小程序陆续推出,相关的信息.文章也铺天盖地的遍布在各处,我们有时候会困惑,不知道哪些信息对于自己是有价值的,那么TNFE-腾讯新闻前端团队启动了 ...

  5. (二十)c#Winform自定义控件-有后退的窗体

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  6. (三十)c#Winform自定义控件-文本框(三)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  7. JavaScript数据可视化编程学习(二)Flotr2,雷达图

    一.雷达图 使用雷达图显示多维数据. 如果你有多维的数据要展示,那么雷达图就是一种非常有效的可视化方法. 由于雷达图不常用,比较陌生,所以向用户解释的时候有一些难度.注意使用雷达图会增加用户认知负担. ...

  8. (七十三)c#Winform自定义控件-资源加载窗体

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  9. (五十)c#Winform自定义控件-滑块

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

随机推荐

  1. 逆向破解之160个CrackMe —— 016

    CrackMe —— 016 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合一共160个待逆向破解的程序 CrackMe:它们都是一些公开给别人尝试破解的小程序,制作 c ...

  2. spring中的事件 applicationevent 讲的确实不错(转)

    event,listener是observer模式一种体现,在spring 3.0.5中,已经可以使用annotation实现event和eventListner里. 我们以spring-webflo ...

  3. .Net Core 三大Redis客户端对比和使用心得

    前言 稍微复杂一点的互联网项目,技术选型都可能会涉及Redis,.NetCore的生态越发完善,支持.NetCore的Redis客户端越来越多, 下面三款常见的Redis客户端,相信大家平时或多或少用 ...

  4. Python多进程的Join和daemon(守护)的用法

    join和daemon 下面仅以多进程为例: 知识点一: 当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下 ...

  5. C#数据结构_基本概念及线性表

    常见的4类数据结构: 1.集合. 2.线性结构.3.树形结构.4.图状结构. 数据结构(Data Structure)简记为 DS,是一个二元组,DS = (D,R) 其中:D 是数据元素的有限集合, ...

  6. 栅格数据的批量镶嵌(附Python脚本)

    栅格数据的批量镶嵌(附Python脚本) 博客小序:在数据处理的过程中,会遇到需要大量镶嵌的情况,当数据较多时手动镶嵌较为麻烦,自己最近对分省的DEM数据进行镶嵌,由于利用python进行镶嵌较为方便 ...

  7. Java多线程(十二):中断机制

    这里详细分析interrupt(),interrupted(),isInterrupted()三个方法 interrupt() 中断这个线程,设置中断标识位 public void interrupt ...

  8. Bluetooth(蓝牙)连接过程分析

    一 基本概念 蓝牙的连接过程是十分重要的,特别是做蓝牙的技术人员来说,这个是十分重要的.理它的流程,是一件必修课.虽然进入蓝牙行业很久了,以前没怎么系统化的做一些事情,趁此机会,就梳理一下这里面的内容 ...

  9. POJ 1236 Network of Schools - 缩点

    POJ 1236 :http://poj.org/problem?id=1236 参考:https://www.cnblogs.com/TnT2333333/p/6875680.html 题意: 有好 ...

  10. lightoj 1057 - Collecting Gold(状压dp)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1057 题解:看似有点下记忆话搜索但是由于他是能走8个方向的也就是说两点的距离其 ...