2020.06.26

+ UIDoughnutChart:增加控件:环状图
This commit is contained in:
Sunny 2020-06-26 15:13:36 +08:00
parent 9d12d58523
commit 6d93656a92
16 changed files with 704 additions and 27 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,153 @@
namespace Sunny.UI.Demo.Charts
{
partial class FDoughnutChart
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.DoughnutChart = new Sunny.UI.UIDoughnutChart();
this.uiSymbolButton1 = new Sunny.UI.UISymbolButton();
this.uiImageButton3 = new Sunny.UI.UIImageButton();
this.uiImageButton2 = new Sunny.UI.UIImageButton();
this.uiImageButton1 = new Sunny.UI.UIImageButton();
this.uiLine1 = new Sunny.UI.UILine();
this.PagePanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.uiImageButton1)).BeginInit();
this.SuspendLayout();
//
// PagePanel
//
this.PagePanel.Controls.Add(this.uiSymbolButton1);
this.PagePanel.Controls.Add(this.uiImageButton3);
this.PagePanel.Controls.Add(this.uiImageButton2);
this.PagePanel.Controls.Add(this.uiImageButton1);
this.PagePanel.Controls.Add(this.uiLine1);
this.PagePanel.Controls.Add(this.DoughnutChart);
this.PagePanel.Size = new System.Drawing.Size(800, 523);
//
// DoughnutChart
//
this.DoughnutChart.FillColor = System.Drawing.Color.FromArgb(((int)(((byte)(244)))), ((int)(((byte)(244)))), ((int)(((byte)(244)))));
this.DoughnutChart.Font = new System.Drawing.Font("微软雅黑", 12F);
this.DoughnutChart.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(54)))), ((int)(((byte)(54)))));
this.DoughnutChart.Location = new System.Drawing.Point(30, 48);
this.DoughnutChart.Name = "DoughnutChart";
this.DoughnutChart.Option = null;
this.DoughnutChart.Size = new System.Drawing.Size(670, 400);
this.DoughnutChart.TabIndex = 0;
this.DoughnutChart.Text = "uiDoughnutChart1";
//
// uiSymbolButton1
//
this.uiSymbolButton1.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiSymbolButton1.Font = new System.Drawing.Font("微软雅黑", 12F);
this.uiSymbolButton1.Location = new System.Drawing.Point(348, 466);
this.uiSymbolButton1.Name = "uiSymbolButton1";
this.uiSymbolButton1.Padding = new System.Windows.Forms.Padding(28, 0, 0, 0);
this.uiSymbolButton1.Size = new System.Drawing.Size(100, 27);
this.uiSymbolButton1.Symbol = 61952;
this.uiSymbolButton1.TabIndex = 33;
this.uiSymbolButton1.Text = "数据";
this.uiSymbolButton1.Click += new System.EventHandler(this.uiSymbolButton1_Click);
//
// uiImageButton3
//
this.uiImageButton3.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiImageButton3.Image = global::Sunny.UI.Demo.Properties.Resources.ChartDarkStyle;
this.uiImageButton3.Location = new System.Drawing.Point(242, 466);
this.uiImageButton3.Name = "uiImageButton3";
this.uiImageButton3.Size = new System.Drawing.Size(100, 27);
this.uiImageButton3.TabIndex = 32;
this.uiImageButton3.TabStop = false;
this.uiImageButton3.Text = " Dark";
this.uiImageButton3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.uiImageButton3.Click += new System.EventHandler(this.uiImageButton3_Click);
//
// uiImageButton2
//
this.uiImageButton2.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiImageButton2.Image = global::Sunny.UI.Demo.Properties.Resources.ChartPlainStyle;
this.uiImageButton2.Location = new System.Drawing.Point(136, 466);
this.uiImageButton2.Name = "uiImageButton2";
this.uiImageButton2.Size = new System.Drawing.Size(100, 27);
this.uiImageButton2.TabIndex = 31;
this.uiImageButton2.TabStop = false;
this.uiImageButton2.Text = " Plain";
this.uiImageButton2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.uiImageButton2.Click += new System.EventHandler(this.uiImageButton2_Click);
//
// uiImageButton1
//
this.uiImageButton1.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiImageButton1.Image = global::Sunny.UI.Demo.Properties.Resources.ChartDefaultStyle;
this.uiImageButton1.Location = new System.Drawing.Point(30, 466);
this.uiImageButton1.Name = "uiImageButton1";
this.uiImageButton1.Size = new System.Drawing.Size(100, 27);
this.uiImageButton1.TabIndex = 30;
this.uiImageButton1.TabStop = false;
this.uiImageButton1.Text = " Default";
this.uiImageButton1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.uiImageButton1.Click += new System.EventHandler(this.uiImageButton1_Click);
//
// uiLine1
//
this.uiLine1.Font = new System.Drawing.Font("微软雅黑", 12F);
this.uiLine1.Location = new System.Drawing.Point(30, 20);
this.uiLine1.MinimumSize = new System.Drawing.Size(16, 16);
this.uiLine1.Name = "uiLine1";
this.uiLine1.Size = new System.Drawing.Size(670, 20);
this.uiLine1.TabIndex = 29;
this.uiLine1.Text = "UIDoughnutChart";
this.uiLine1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// FDoughnutChart
//
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 558);
this.Name = "FDoughnutChart";
this.Symbol = 62056;
this.Text = "DoughnutChart";
this.PagePanel.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.uiImageButton3)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.uiImageButton2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.uiImageButton1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private UIDoughnutChart DoughnutChart;
private UISymbolButton uiSymbolButton1;
private UIImageButton uiImageButton3;
private UIImageButton uiImageButton2;
private UIImageButton uiImageButton1;
private UILine uiLine1;
}
}

View File

@ -0,0 +1,77 @@
namespace Sunny.UI.Demo.Charts
{
public partial class FDoughnutChart : UITitlePage
{
public FDoughnutChart()
{
InitializeComponent();
}
private void uiSymbolButton1_Click(object sender, System.EventArgs e)
{
var option = new UIDoughnutOption();
//设置Title
option.Title = new UITitle();
option.Title.Text = "SunnyUI";
option.Title.SubText = "Star";
option.Title.Left = UILeftAlignment.Center;
//设置ToolTip
option.ToolTip = new UIPieToolTip();
//设置Legend
option.Legend = new UILegend();
option.Legend.Orient = UIOrient.Vertical;
option.Legend.Top = UITopAlignment.Top;
option.Legend.Left = UILeftAlignment.Left;
option.Legend.AddData("2020-05-19");
option.Legend.AddData("2020-05-20");
option.Legend.AddData("2020-05-21");
option.Legend.AddData("2020-05-22");
option.Legend.AddData("2020-05-23");
option.Legend.AddData("2020-05-24");
option.Legend.AddData("2020-05-25");
//设置Series
var series = new UIDoughnutSeries();
series.Name = "Star count";
series.Center = new UICenter(50, 55);
series.Radius.Inner = 40;
series.Radius.Outer = 70;
series.Label.Show = true;
series.Label.Position = UIPieSeriesLabelPosition.Center;
//增加数据
series.AddData("2020-05-19", 38);
series.AddData("2020-05-20", 21);
series.AddData("2020-05-21", 11);
series.AddData("2020-05-22", 52);
series.AddData("2020-05-23", 23);
series.AddData("2020-05-24", 26);
series.AddData("2020-05-25", 27);
//增加Series
option.Series.Add(series);
//设置Option
DoughnutChart.SetOption(option);
}
private void uiImageButton1_Click(object sender, System.EventArgs e)
{
DoughnutChart.ChartStyleType = UIChartStyleType.Default;
}
private void uiImageButton2_Click(object sender, System.EventArgs e)
{
DoughnutChart.ChartStyleType = UIChartStyleType.Plain;
}
private void uiImageButton3_Click(object sender, System.EventArgs e)
{
DoughnutChart.ChartStyleType = UIChartStyleType.Dark;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -155,7 +155,7 @@
// uiProgressIndicator1
//
this.uiProgressIndicator1.Font = new System.Drawing.Font("微软雅黑", 12F);
this.uiProgressIndicator1.Location = new System.Drawing.Point(30, 217);
this.uiProgressIndicator1.Location = new System.Drawing.Point(30, 209);
this.uiProgressIndicator1.Name = "uiProgressIndicator1";
this.uiProgressIndicator1.Size = new System.Drawing.Size(119, 132);
this.uiProgressIndicator1.TabIndex = 26;

View File

@ -1,8 +1,8 @@
using Sunny.UI.Demo.Controls;
using Sunny.UI.Demo.Charts;
using Sunny.UI.Demo.Controls;
using Sunny.UI.Demo.Forms;
using System;
using System.Windows.Forms;
using Sunny.UI.Demo.Charts;
namespace Sunny.UI.Demo
{
@ -35,24 +35,25 @@ namespace Sunny.UI.Demo
Aside.CreateChildNode(parent, 62104, 24, AddPage(new FContextMenuStrip(), ++pageIndex));
Aside.CreateChildNode(parent, 61668, 24, AddPage(new FMeter(), ++pageIndex));
Aside.CreateChildNode(parent, 62173, 24, AddPage(new FOther(), ++pageIndex));
Aside.SetNodeTipsText(parent.Nodes[0],"1");
Aside.SetNodeTipsText(parent.Nodes[0], "1");
pageIndex = 2000;
Header.SetNodePageIndex(Header.Nodes[1], pageIndex);
Header.SetNodeSymbol(Header.Nodes[1], 61818);
parent = Aside.CreateNode("Forms", 61818, 24, pageIndex);
//通过设置GUID关联
Aside.CreateChildNode(parent, 62160, 24, AddPage(new FDialogs(), Guid.NewGuid()));
Aside.CreateChildNode(parent, 61508, 24, AddPage(new FEditor(), Guid.NewGuid()));
Aside.CreateChildNode(parent, 61674, 24, AddPage(new FFrames(), Guid.NewGuid()));
//通过设置GUID关联节点字体图标和大小由UIPage设置
Aside.CreateChildNode(parent, AddPage(new FDialogs(), Guid.NewGuid()));
Aside.CreateChildNode(parent, AddPage(new FEditor(), Guid.NewGuid()));
Aside.CreateChildNode(parent, AddPage(new FFrames(), Guid.NewGuid()));
pageIndex = 3000;
Header.SetNodePageIndex(Header.Nodes[2], pageIndex);
Header.SetNodeSymbol(Header.Nodes[2], 61950);
parent = Aside.CreateNode("Charts", 61950, 24, pageIndex);
//直接关联默认自动生成GUID
Aside.CreateChildNode(parent, 61952, 24, AddPage(new FPieChart()));
Aside.CreateChildNode(parent, 61568, 24, AddPage(new FBarChart()));
Aside.CreateChildNode(parent, AddPage(new FPieChart()));
Aside.CreateChildNode(parent, AddPage(new FBarChart()));
Aside.CreateChildNode(parent, AddPage(new FDoughnutChart()));
Header.SetNodeSymbol(Header.Nodes[3], 61502);
var styles = UIStyles.PopularStyles();

View File

@ -53,6 +53,12 @@
<Compile Include="Charts\FBarChart.Designer.cs">
<DependentUpon>FBarChart.cs</DependentUpon>
</Compile>
<Compile Include="Charts\FDoughnutChart.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Charts\FDoughnutChart.Designer.cs">
<DependentUpon>FDoughnutChart.cs</DependentUpon>
</Compile>
<Compile Include="Controls\FAvatar.cs">
<SubType>Form</SubType>
</Compile>
@ -286,6 +292,9 @@
<EmbeddedResource Include="Charts\FBarChart.resx">
<DependentUpon>FBarChart.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Charts\FDoughnutChart.resx">
<DependentUpon>FDoughnutChart.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\FAvatar.resx">
<DependentUpon>FAvatar.cs</DependentUpon>
</EmbeddedResource>

View File

@ -0,0 +1,263 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Sunny.UI
{
[ToolboxItem(true), Description("甜甜圈图")]
public class UIDoughnutChart : UIChart
{
protected override void CreateEmptyOption()
{
if (emptyOption != null) return;
UIDoughnutOption option = new UIDoughnutOption();
option.Title = new UITitle();
option.Title.Text = "SunnyUI";
option.Title.SubText = "DoughnutChart";
var series = new UIDoughnutSeries();
series.Name = "饼状图";
series.Center = new UICenter(50, 55);
series.Radius.Inner = 40;
series.Radius.Outer = 70;
for (int i = 0; i < 5; i++)
{
series.AddData("Data" + i, (i + 1) * 20);
}
option.Series.Add(series);
emptyOption = option;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
CalcData(DoughnutOption);
}
protected override void DrawOption(Graphics g)
{
if (DoughnutOption == null) return;
DrawTitle(g, DoughnutOption.Title);
DrawSeries(g, DoughnutOption.Series);
DrawLegend(g, DoughnutOption.Legend);
}
protected override void CalcData(UIOption option)
{
Angles.Clear();
UIDoughnutOption o = (UIDoughnutOption)option;
if (o == null || o.Series == null || o.Series.Count == 0) return;
UITemplate template = null;
if (o.ToolTip != null)
{
template = new UITemplate(o.ToolTip.Formatter);
}
for (int pieIndex = 0; pieIndex < o.Series.Count; pieIndex++)
{
var pie = o.Series[pieIndex];
Angles.TryAdd(pieIndex, new ConcurrentDictionary<int, Angle>());
double all = 0;
foreach (var data in pie.Data)
{
all += data.Value;
}
if (all.IsZero()) return;
float start = 0;
for (int i = 0; i < pie.Data.Count; i++)
{
float angle = (float)(pie.Data[i].Value * 360.0f / all);
float percent = (float)(pie.Data[i].Value * 100.0f / all);
string text = "";
if (o.ToolTip != null)
{
try
{
if (template != null)
{
template.Set("a", pie.Name);
template.Set("b", pie.Data[i].Name);
template.Set("c", pie.Data[i].Value.ToString(o.ToolTip.ValueFormat));
template.Set("d", percent.ToString("F2"));
text = template.Render();
}
}
catch
{
text = pie.Data[i].Name + " : " + pie.Data[i].Value.ToString("F2") + "(" + percent.ToString("F2") + "%)";
if (pie.Name.IsValid()) text = pie.Name + '\n' + text;
}
}
Angle pieAngle = new Angle(start, angle, text);
GetSeriesRect(pie, ref pieAngle);
Angles[pieIndex].AddOrUpdate(i, pieAngle);
start += angle;
}
}
}
private void DrawSeries(Graphics g, List<UIDoughnutSeries> series)
{
if (series == null || series.Count == 0) return;
for (int pieIndex = 0; pieIndex < series.Count; pieIndex++)
{
var pie = series[pieIndex];
for (int azIndex = 0; azIndex < pie.Data.Count; azIndex++)
{
Angle angle = Angles[pieIndex][azIndex];
Color color = ChartStyle.SeriesColor[azIndex % ChartStyle.ColorCount];
if (ActiveAzIndex == azIndex)
g.FillFan(color, angle.Center, angle.Inner, angle.Outer+5, angle.Start-90, angle.Sweep);
else
g.FillFan(color, angle.Center, angle.Inner, angle.Outer, angle.Start - 90, angle.Sweep);
Angles[pieIndex][azIndex].TextSize = g.MeasureString(Angles[pieIndex][azIndex].Text, legendFont);
if (pie.Label.Show && ActiveAzIndex == azIndex)
{
if (pie.Label.Position == UIPieSeriesLabelPosition.Center)
{
SizeF sf = g.MeasureString(pie.Data[azIndex].Name, Font);
g.DrawString(pie.Data[azIndex].Name, Font, color, angle.Center.X- sf.Width/2.0f,angle.Center.Y-sf.Height/2.0f);
}
}
}
}
}
private readonly ConcurrentDictionary<int, ConcurrentDictionary<int, Angle>> Angles = new ConcurrentDictionary<int, ConcurrentDictionary<int, Angle>>();
[Browsable(false)]
private UIDoughnutOption DoughnutOption
{
get
{
UIOption option = Option ?? EmptyOption;
UIDoughnutOption o = (UIDoughnutOption)option;
return o;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (DoughnutOption.SeriesCount == 0)
{
SetPieAndAzIndex(-1, -1);
return;
}
for (int pieIndex = 0; pieIndex < DoughnutOption.SeriesCount; pieIndex++)
{
for (int azIndex = 0; azIndex < DoughnutOption.Series[pieIndex].Data.Count; azIndex++)
{
Angle angle = Angles[pieIndex][azIndex];
PointF pf = angle.Center;
if (MathEx.CalcDistance(e.Location, pf) > angle.Outer) continue;
if (MathEx.CalcDistance(e.Location, pf) < angle.Inner) continue;
double az = MathEx.CalcAngle(e.Location, pf);
if (az >= angle.Start && az <= angle.Start + angle.Sweep)
{
SetPieAndAzIndex(pieIndex, azIndex);
if (tip.Text != angle.Text)
{
tip.Text = angle.Text;
tip.Size = new Size((int)angle.TextSize.Width + 4, (int)angle.TextSize.Height + 4);
}
if (az >= 0 && az < 90)
{
tip.Top = e.Location.Y + 20;
tip.Left = e.Location.X - tip.Width;
}
else if (az >= 90 && az < 180)
{
tip.Left = e.Location.X - tip.Width;
tip.Top = e.Location.Y - tip.Height - 2;
}
else if (az >= 180 && az < 270)
{
tip.Left = e.Location.X;
tip.Top = e.Location.Y - tip.Height - 2;
}
else if (az >= 270 && az < 360)
{
tip.Left = e.Location.X + 15;
tip.Top = e.Location.Y + 20;
}
if (!tip.Visible) tip.Visible = angle.Text.IsValid();
return;
}
}
}
SetPieAndAzIndex(-1, -1);
tip.Visible = false;
}
private int ActiveAzIndex = -1;
private int ActivePieIndex = -1;
private void SetPieAndAzIndex(int pieIndex, int azIndex)
{
if (ActivePieIndex != pieIndex || ActiveAzIndex != azIndex)
{
ActivePieIndex = pieIndex;
ActiveAzIndex = azIndex;
Invalidate();
}
}
private void GetSeriesRect(UIDoughnutSeries series, ref Angle angle)
{
int left = series.Center.Left;
int top = series.Center.Top;
left = Width * left / 100;
top = Height * top / 100;
angle.Center = new PointF(left, top);
angle.Inner = Math.Min(Width, Height) * series.Radius.Inner / 200.0f;
angle.Outer = Math.Min(Width, Height) * series.Radius.Outer / 200.0f;
}
private class Angle
{
public float Start { get; set; }
public float Sweep { get; set; }
public float Inner { get; set; }
public float Outer { get; set; }
public PointF Center { get; set; }
public Angle(float start, float sweep, string text)
{
Start = start;
Sweep = sweep;
Text = text;
}
public string Text { get; set; }
public SizeF TextSize { get; set; }
}
}
}

View File

@ -136,7 +136,7 @@ namespace Sunny.UI
Color color = ChartStyle.SeriesColor[azIndex % ChartStyle.ColorCount];
RectangleF rectx = new RectangleF(rect.X - 10, rect.Y - 10, rect.Width + 20, rect.Width + 20);
g.FillPie(color, (ActivePieIndex == pieIndex && ActiveAzIndex == azIndex) ? rectx : rect, Angles[pieIndex][azIndex].Start - 90, Angles[pieIndex][azIndex].Sweep);
Angles[pieIndex][azIndex].Size = g.MeasureString(Angles[pieIndex][azIndex].Text, legendFont);
Angles[pieIndex][azIndex].TextSize = g.MeasureString(Angles[pieIndex][azIndex].Text, legendFont);
}
}
}
@ -177,13 +177,14 @@ namespace Sunny.UI
double az = MathEx.CalcAngle(e.Location, pf);
for (int azIndex = 0; azIndex < PieOption.Series[pieIndex].Data.Count; azIndex++)
{
if (az >= Angles[pieIndex][azIndex].Start && az <= Angles[pieIndex][azIndex].Start + Angles[pieIndex][azIndex].Sweep)
Angle angle = Angles[pieIndex][azIndex];
if (az >= angle.Start && az <= angle.Start + angle.Sweep)
{
SetPieAndAzIndex(pieIndex, azIndex);
if (tip.Text != Angles[pieIndex][azIndex].Text)
if (tip.Text != angle.Text)
{
tip.Text = Angles[pieIndex][azIndex].Text;
tip.Size = new Size((int)Angles[pieIndex][azIndex].Size.Width + 4, (int)Angles[pieIndex][azIndex].Size.Height + 4);
tip.Text = angle.Text;
tip.Size = new Size((int)angle.TextSize.Width + 4, (int)angle.TextSize.Height + 4);
}
if (az >= 0 && az < 90)
@ -207,7 +208,7 @@ namespace Sunny.UI
tip.Top = e.Location.Y + 20;
}
if (!tip.Visible) tip.Visible = Angles[pieIndex][azIndex].Text.IsValid();
if (!tip.Visible) tip.Visible = angle.Text.IsValid();
return;
}
}
@ -240,7 +241,7 @@ namespace Sunny.UI
return new RectangleF(left - halfRadius, top - halfRadius, halfRadius * 2, halfRadius * 2);
}
internal class Angle
private class Angle
{
public float Start { get; set; }
public float Sweep { get; set; }
@ -254,7 +255,7 @@ namespace Sunny.UI
public string Text { get; set; }
public SizeF Size { get; set; }
public SizeF TextSize { get; set; }
}
}
}

View File

@ -28,13 +28,13 @@ namespace Sunny.UI
public int SeriesCount => Series.Count;
}
public class UIDoughnutPieOption : UIOption, IDisposable
public class UIDoughnutOption : UIOption, IDisposable
{
public List<UIDoughnutPieSeries> Series = new List<UIDoughnutPieSeries>();
public List<UIDoughnutSeries> Series = new List<UIDoughnutSeries>();
public UIPieToolTip ToolTip;
public void AddSeries(UIDoughnutPieSeries series)
public void AddSeries(UIDoughnutSeries series)
{
Series.Clear();
Series.Add(series);
@ -72,6 +72,8 @@ namespace Sunny.UI
public readonly List<UIPieSeriesData> Data = new List<UIPieSeriesData>();
public UIPieSeriesLabel Label = new UIPieSeriesLabel();
public void AddData(string name, double value)
{
Data.Add(new UIPieSeriesData(name, value));
@ -96,7 +98,7 @@ namespace Sunny.UI
}
}
public class UIDoughnutPieSeries : IDisposable
public class UIDoughnutSeries : IDisposable
{
public string Name { get; set; }
@ -108,6 +110,8 @@ namespace Sunny.UI
public readonly List<UIPieSeriesData> Data = new List<UIPieSeriesData>();
public UIPieSeriesLabel Label = new UIPieSeriesLabel();
public void AddData(string name, double value)
{
Data.Add(new UIPieSeriesData(name, value));
@ -135,4 +139,20 @@ namespace Sunny.UI
Value = value;
}
}
public class UIPieSeriesLabel
{
public bool Show { get; set; } = false;
public UIPieSeriesLabelPosition Position { get; set; } = UIPieSeriesLabelPosition.Center;
public string Formatter { get; set; } = "{{b}}";
}
public enum UIPieSeriesLabelPosition
{
Outside,
Inside,
Center
}
}

View File

@ -715,7 +715,13 @@ namespace Sunny.UI
public TreeNode CreateChildNode(TreeNode parent, UIPage page)
{
return CreateChildNode(parent, new NavMenuItem(page));
var childNode = CreateChildNode(parent, new NavMenuItem(page));
if (page.Symbol > 0)
{
MenuHelper.SetSymbol(childNode, page.Symbol, page.SymbolSize);
}
return childNode;
}
public TreeNode CreateChildNode(TreeNode parent, NavMenuItem item)

View File

@ -470,12 +470,22 @@ namespace Sunny.UI
return path;
}
public static GraphicsPath CreateFanPath(this Graphics g, Point center, int d1, int d2, float startAngle, float sweepAngle)
public static GraphicsPath CreateFanPath(this Graphics g, Point center, float d1, float d2, float startAngle, float sweepAngle)
{
return center.CreateFanPath(d1, d2, startAngle, sweepAngle);
}
public static GraphicsPath CreateFanPath(this Point center, int d1, int d2, float startAngle, float sweepAngle)
public static GraphicsPath CreateFanPath(this Graphics g, PointF center, float d1, float d2, float startAngle, float sweepAngle)
{
return center.CreateFanPath(d1, d2, startAngle, sweepAngle);
}
public static GraphicsPath CreateFanPath(this Point center, float d1, float d2, float startAngle, float sweepAngle)
{
return new PointF(center.X,center.Y).CreateFanPath(d1,d2,startAngle,sweepAngle);
}
public static GraphicsPath CreateFanPath(this PointF center, float d1, float d2, float startAngle, float sweepAngle)
{
GraphicsPath path = new GraphicsPath();
path.AddArc(center.X - d1, center.Y - d1, d1 * 2, d1 * 2, startAngle, sweepAngle);
@ -490,14 +500,28 @@ namespace Sunny.UI
g.DrawLine(color, center.X, center.Y - size, center.X, center.Y + size);
}
public static void DrawFan(this Graphics g, Color color, Point center, int d1, int d2, float startAngle, float sweepAngle, bool smooth = true)
public static void DrawFan(this Graphics g, Color color, Point center, float d1, float d2, float startAngle, float sweepAngle, bool smooth = true)
{
GraphicsPath path = g.CreateFanPath(center, d1, d2, startAngle, sweepAngle);
g.DrawPath(color, path, smooth);
path.Dispose();
}
public static void FillFan(this Graphics g, Color color, Point center, int d1, int d2, float startAngle, float sweepAngle, bool smooth = true)
public static void DrawFan(this Graphics g, Color color, PointF center, float d1, float d2, float startAngle, float sweepAngle, bool smooth = true)
{
GraphicsPath path = g.CreateFanPath(center, d1, d2, startAngle, sweepAngle);
g.DrawPath(color, path, smooth);
path.Dispose();
}
public static void FillFan(this Graphics g, Color color, Point center, float d1, float d2, float startAngle, float sweepAngle, bool smooth = true)
{
GraphicsPath path = g.CreateFanPath(center, d1, d2, startAngle, sweepAngle);
g.FillPath(color, path, smooth);
path.Dispose();
}
public static void FillFan(this Graphics g, Color color, PointF center, float d1, float d2, float startAngle, float sweepAngle, bool smooth = true)
{
GraphicsPath path = g.CreateFanPath(center, d1, d2, startAngle, sweepAngle);
g.FillPath(color, path, smooth);

View File

@ -59,6 +59,9 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="Charts\UIChartStyle.cs" />
<Compile Include="Charts\UIDoughnutChart.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Charts\UIOption.cs" />
<Compile Include="Charts\UIPieChartOption.cs" />
<Compile Include="Controls\Color\UIColorBar.cs">