* 重构图表控件,准备开发UIBarChart

* UITextBox:增加EnterAsTab
This commit is contained in:
Sunny 2020-06-14 19:35:38 +08:00
parent 04f3640217
commit e9b80647d3
17 changed files with 616 additions and 233 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -70,7 +70,6 @@
this.PieChart.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(54)))), ((int)(((byte)(54)))));
this.PieChart.Location = new System.Drawing.Point(26, 46);
this.PieChart.Name = "PieChart";
this.PieChart.Option = null;
this.PieChart.RectSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.None;
this.PieChart.Size = new System.Drawing.Size(687, 399);
this.PieChart.TabIndex = 20;

View File

@ -1,4 +1,6 @@
namespace Sunny.UI.Demo.Controls
using System;
namespace Sunny.UI.Demo.Controls
{
public partial class FPieChart : UITitlePage
{
@ -22,9 +24,11 @@
PieChart.ChartStyleType = UIChartStyleType.Dark;
}
private void uiSymbolButton1_Click(object sender, System.EventArgs e)
{
var option = new UIOption();
var option = new UIPieOption();
//设置Title
option.Title = new UITitle();
@ -33,11 +37,11 @@
option.Title.Left = UILeftAlignment.Center;
//设置ToolTip
option.ToolTip = new UIToolTip();
option.ToolTip = new UIPieToolTip();
//设置Legend
option.Legend = new UILegend();
option.Legend.Orient = Orient.Vertical;
option.Legend = new UIPieLegend();
option.Legend.Orient = UIOrient.Vertical;
option.Legend.Top = UITopAlignment.Top;
option.Legend.Left = UILeftAlignment.Left;
@ -50,9 +54,8 @@
option.Legend.AddData("2020-05-25");
//设置Series
var series = new UISeries();
var series = new UIPieSeries();
series.Name = "Star count";
series.Type = UISeriesType.Pie;
series.Center = new UICenter(50, 55);
series.Radius = 70;

View File

@ -52,10 +52,13 @@
// edtName
//
this.edtName.Cursor = System.Windows.Forms.Cursors.IBeam;
this.edtName.EnterAsTab = true;
this.edtName.FillColor = System.Drawing.Color.White;
this.edtName.Font = new System.Drawing.Font("微软雅黑", 12F);
this.edtName.Location = new System.Drawing.Point(150, 55);
this.edtName.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.edtName.Maximum = 2147483647D;
this.edtName.Minimum = -2147483648D;
this.edtName.Name = "edtName";
this.edtName.Padding = new System.Windows.Forms.Padding(5);
this.edtName.Size = new System.Drawing.Size(340, 29);
@ -113,6 +116,8 @@
this.edtAge.Font = new System.Drawing.Font("微软雅黑", 12F);
this.edtAge.Location = new System.Drawing.Point(150, 135);
this.edtAge.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.edtAge.Maximum = 2147483647D;
this.edtAge.Minimum = -2147483648D;
this.edtAge.Name = "edtAge";
this.edtAge.Padding = new System.Windows.Forms.Padding(5);
this.edtAge.Size = new System.Drawing.Size(170, 29);
@ -187,6 +192,8 @@
this.edtDate.Name = "edtDate";
this.edtDate.Padding = new System.Windows.Forms.Padding(0, 0, 30, 0);
this.edtDate.Size = new System.Drawing.Size(170, 29);
this.edtDate.SymbolDropDown = 61555;
this.edtDate.SymbolNormal = 61555;
this.edtDate.TabIndex = 5;
this.edtDate.Text = "2020-05-08";
this.edtDate.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
@ -199,6 +206,8 @@
this.edtAddress.Font = new System.Drawing.Font("微软雅黑", 12F);
this.edtAddress.Location = new System.Drawing.Point(150, 254);
this.edtAddress.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.edtAddress.Maximum = 2147483647D;
this.edtAddress.Minimum = -2147483648D;
this.edtAddress.Name = "edtAddress";
this.edtAddress.Padding = new System.Windows.Forms.Padding(5);
this.edtAddress.Size = new System.Drawing.Size(340, 29);

View File

@ -0,0 +1,89 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using Sunny.UI.Charts;
namespace Sunny.UI
{
[ToolboxItem(false)]
public class UIBarChart : UIChart
{
protected override void CalcData(UIOption option)
{
Bars.Clear();
UIBarOption o = (UIBarOption)option;
if (o == null || o.Series == null || o.Series.Count == 0) return;
DrawOrigin = new Point(PieOption.Grid.Left,Height - PieOption.Grid.Bottom);
DrawSize = new Size(Width- PieOption.Grid.Left- PieOption.Grid.Right,
Height -PieOption.Grid.Top-PieOption.Grid.Bottom);
if (DrawSize.Width<=0 || DrawSize.Height<=0) return;
if (o.XAxis.Data.Count==0) return;
DrawBarWidth = DrawSize.Width / o.XAxis.Data.Count;
}
private Point DrawOrigin;
private Size DrawSize;
private int DrawBarWidth;
private readonly ConcurrentDictionary<int, BarInfo> Bars = new ConcurrentDictionary<int, BarInfo>();
[Browsable(false)]
private UIBarOption PieOption
{
get
{
UIOption option = Option ?? EmptyOption;
UIBarOption o = (UIBarOption)option;
return o;
}
}
protected override void CreateEmptyOption()
{
if (emptyOption != null) return;
UIBarOption option = new UIBarOption();
option.Title = new UITitle();
option.Title = new UITitle();
option.Title.Text = "SunnyUI";
option.Title.SubText = "BarChart";
var series = new UIBarSeries();
series.Name = "柱状图";
series.AddData(1);
series.AddData(5);
series.AddData(2);
series.AddData(4);
series.AddData(3);
option.XAxis.Data.Add("Mon");
option.XAxis.Data.Add("Tue");
option.XAxis.Data.Add("Wed");
option.XAxis.Data.Add("Thu");
option.XAxis.Data.Add("Fri");
option.Series.Add(series);
emptyOption = option;
}
private void DrawTitle(Graphics g, UITitle title)
{
}
private void DrawSeries(Graphics g, UIPieOption o, List<UIPieSeries> series)
{
}
private void DrawLegend(Graphics g, UIPieLegend legend)
{
}
internal class BarInfo
{
public Rectangle Rect { get; set; }
}
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
namespace Sunny.UI.Charts
{
public class UIBarOption : UIOption, IDisposable
{
public UITitle Title;
public UICategoryAxis XAxis { get; set; } = new UICategoryAxis();
public UIValueAxis YAxis { get; set; } = new UIValueAxis();
public List<UIBarSeries> Series = new List<UIBarSeries>();
public UIChartGrid Grid = new UIChartGrid();
//public UIPieLegend Legend;
//public UIPieToolTip ToolTip;
public void AddSeries(UIBarSeries series)
{
Series.Add(series);
}
public void Dispose()
{
foreach (var series in Series)
{
series?.Dispose();
}
Series.Clear();
}
public int SeriesCount => Series.Count;
}
public class UIAxis
{
public string Name { get; set; }
public UIAxisType Type { get; set; }
/// <summary>
/// 坐标轴的分割段数,需要注意的是这个分割段数只是个预估值
/// 最后实际显示的段数会在这个基础上根据分割后坐标轴刻度显示的易读程度作调整。
/// 在类目轴中无效。
/// </summary>
public int SplitNumber { get; set; } = 5;
}
public class UICategoryAxis : UIAxis
{
public UICategoryAxis()
{
Type = UIAxisType.Category;
}
public List<string> Data = new List<string>();
public void Clear()
{
Data.Clear();
}
}
public class UIValueAxis : UIAxis
{
public UIValueAxis()
{
Type = UIAxisType.Value;
}
}
public class UIBarSeries : IDisposable
{
public string Name { get; set; }
public UISeriesType Type => UISeriesType.Bar;
public List<double> Data = new List<double>();
public void AddData(double value)
{
Data.Add(value);
}
public void Dispose()
{
Data.Clear();
}
}
}

View File

@ -19,7 +19,6 @@
* 2020-06-06: V2.2.5
******************************************************************************/
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
@ -60,22 +59,6 @@ namespace Sunny.UI
tip.Visible = false;
}
private int decimalNumber;
[DefaultValue(0),Description("显示数据格式化小数点后位数")]
public int DecimalNumber
{
get => decimalNumber;
set
{
if (decimalNumber != value)
{
decimalNumber = value;
Invalidate();
}
}
}
protected readonly UITransparentPanel tip = new UITransparentPanel();
private UIChartStyleType chartStyleType = UIChartStyleType.Plain;
@ -154,6 +137,7 @@ namespace Sunny.UI
protected UIOption emptyOption;
[Browsable(false)]
protected UIOption EmptyOption
{
get
@ -171,7 +155,11 @@ namespace Sunny.UI
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawOption(e.Graphics, Option ?? EmptyOption);
DrawOption(e.Graphics);
}
protected virtual void DrawOption(Graphics g)
{
}
protected virtual void CreateEmptyOption()
@ -180,26 +168,6 @@ namespace Sunny.UI
protected UIChartStyle ChartStyle => UIChartStyles.GetChartStyle(ChartStyleType);
private void DrawOption(Graphics g, UIOption o)
{
if (o == null) return;
if (o.Title != null) DrawTitle(g, o.Title);
if (o.Series.Count > 0) DrawSeries(g,o, o.Series);
if (o.Legend != null) DrawLegend(g, o.Legend);
}
protected virtual void DrawTitle(Graphics g, UITitle title)
{
}
protected virtual void DrawSeries(Graphics g,UIOption o, List<UISeries> series)
{
}
protected virtual void DrawLegend(Graphics g, UILegend legend)
{
}
public override void SetStyleColor(UIBaseStyle uiColor)
{
base.SetStyleColor(uiColor);

View File

@ -0,0 +1,90 @@
using System.Drawing;
namespace Sunny.UI
{
public class UIChartStyle
{
public virtual Color BackColor => Color.FromArgb(244, 244, 244);
public virtual Color ForeColor => Color.FromArgb(54, 54, 54);
public int ColorCount => 11;
public virtual Color[] SeriesColor
{
get
{
return new[]
{
Color.FromArgb(241,42,38),
Color.FromArgb(43,71,85),
Color.FromArgb(69,161,168),
Color.FromArgb(229,125,96),
Color.FromArgb(125,200,175),
Color.FromArgb(101,159,132),
Color.FromArgb(216,130,27),
Color.FromArgb(195,160,152),
Color.FromArgb(109,112,115),
Color.FromArgb(79,101,112),
Color.FromArgb(193,204,211)
};
}
}
}
public class UIDefaultChartStyle : UIChartStyle
{
}
public class UILightChartStyle : UIChartStyle
{
public override Color[] SeriesColor
{
get
{
return new[]
{
Color.FromArgb(0,163,219),
Color.FromArgb(0,199,235),
Color.FromArgb(0,227,230),
Color.FromArgb(131,232,187),
Color.FromArgb(255,217,91),
Color.FromArgb(255,153,120),
Color.FromArgb(255,104,139),
Color.FromArgb(245,89,168),
Color.FromArgb(247,139,205),
Color.FromArgb(241,185,242),
Color.FromArgb(156,149,245)
};
}
}
}
public class UIDarkChartStyle : UIChartStyle
{
public override Color BackColor => Color.FromArgb(54, 54, 54);
public override Color ForeColor => Color.FromArgb(239, 239, 239);
public override Color[] SeriesColor
{
get
{
return new[]
{
Color.FromArgb(242,99,95),
Color.FromArgb(103,154,160),
Color.FromArgb(246,152,130),
Color.FromArgb(122,194,170),
Color.FromArgb(255,119,74),
Color.FromArgb(244,220,120),
Color.FromArgb(98,163,117),
Color.FromArgb(83,186,189),
Color.FromArgb(105,137,170),
Color.FromArgb(124,203,143),
Color.FromArgb(255,154,59)
};
}
}
}
}

View File

@ -20,68 +20,35 @@
******************************************************************************/
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Sunny.UI
{
public class UIOption : IDisposable
public abstract class UIOption
{
public UITitle Title;
public List<UISeries> Series = new List<UISeries>();
public UILegend Legend;
public UIToolTip ToolTip;
public void AddSeries(UISeries series)
{
Series.Add(series);
}
public void Dispose()
{
foreach (var series in Series)
{
series?.Dispose();
}
Series.Clear();
}
public int SeriesCount => Series.Count;
}
public class UIToolTip
public class UIChartGrid
{
public string formatter { get; set; } = "{{a}}" + '\n' + "{{b}} : {{c}} ({{d}}%)";
public int Left { get; set; } = 80;
public int Right { get; set; } = 80;
public int Top { get; set; } = 40;
public int Bottom { get; set; } = 40;
}
public class UILegend
{
public UILeftAlignment Left { get; set; } = UILeftAlignment.Center;
public UITopAlignment Top { get; set; } = UITopAlignment.Top;
public Orient Orient { get; set; } = Orient.Vertical;
public readonly List<string> Data = new List<string>();
public int DataCount => Data.Count;
public void AddData(string data)
{
Data.Add(data);
}
}
public enum Orient
public enum UIOrient
{
Vertical,
Horizontal
}
public enum UIAxisType
{
Value,
Category,
Time,
Log
}
public class UITitle
{
public string Text { get; set; } = "UIPieChart";
@ -107,29 +74,6 @@ namespace Sunny.UI
Bottom
}
public class UISeries : IDisposable
{
public string Name { get; set; }
public UISeriesType Type { get; set; }
public int Radius { get; set; } = 50;
public UICenter Center { get; set; } = new UICenter(50, 50);
public readonly List<UISeriesData> Data = new List<UISeriesData>();
public void AddData(string name, double value)
{
Data.Add(new UISeriesData(name, value));
}
public void Dispose()
{
Data.Clear();
}
}
public class UICenter
{
public int Left { get; set; }
@ -147,23 +91,6 @@ namespace Sunny.UI
}
}
public class UISeriesData
{
public string Name { get; set; }
public double Value { get; set; }
public UISeriesData()
{
}
public UISeriesData(string name, double value)
{
Name = name;
Value = value;
}
}
public enum UISeriesType
{
Pie,
@ -194,89 +121,94 @@ namespace Sunny.UI
}
}
public class UIChartStyle
public static class UIChartHelper
{
public virtual Color BackColor => Color.FromArgb(244, 244, 244);
public virtual Color ForeColor => Color.FromArgb(54, 54, 54);
public int ColorCount => 11;
public virtual Color[] SeriesColor
/// <summary>
/// 计算刻度
/// 起始值必须小于结束值
/// </summary>
/// <param name="start">起始值</param>
/// <param name="end">结束值</param>
/// <param name="expect_num">期望刻度数量,实际数接近此数</param>
/// <param name="degree_start">刻度起始值,须乘以间隔使用</param>
/// <param name="degree_end">刻度结束值,须乘以间隔使用</param>
/// <param name="degree_gap">刻度间隔</param>
public static void CalcDegreeScale(double start, double end, int expect_num,
out int degree_start, out int degree_end, out double degree_gap)
{
get
if (start >= end)
{
return new[]
{
Color.FromArgb(241,42,38),
Color.FromArgb(43,71,85),
Color.FromArgb(69,161,168),
Color.FromArgb(229,125,96),
Color.FromArgb(125,200,175),
Color.FromArgb(101,159,132),
Color.FromArgb(216,130,27),
Color.FromArgb(195,160,152),
Color.FromArgb(109,112,115),
Color.FromArgb(79,101,112),
Color.FromArgb(193,204,211)
};
throw new Exception("起始值必须小于结束值");
}
double differ = end - start;
double differ_gap = differ / (expect_num - 1); //35, 4.6, 0.27
double exponent = Math.Log10(differ_gap) - 1; //0.54, -0.34, -1.57
int _exponent = (int)exponent; //0, 0=>-1, -1=>-2
if (exponent < 0 && Math.Abs(exponent) > 1e-8)
{
_exponent--;
}
int step = (int)(differ_gap / Math.Pow(10, _exponent)); //35, 46, 27
int[] fix_steps = new int[] { 10, 20, 25, 50, 100 };
int fix_step = 10; //25, 50, 25
for (int i = fix_steps.Length - 1; i >= 1; i--)
{
if (step > (fix_steps[i] + fix_steps[i - 1]) / 2)
{
fix_step = fix_steps[i];
break;
}
}
degree_gap = fix_step * Math.Pow(10, _exponent); //25, 5, 0.25
double start1 = start / degree_gap;
int start2 = (int)start1;
if (start1 < 0 && Math.Abs(start1 - start2) > 1e-8)
{
start2--;
}
degree_start = start2;
double end1 = end / degree_gap;
int end2 = (int)end1;
if (end1 >= 0 && Math.Abs(end1 - end2) > 1e-8)
{
end2++;
}
degree_end = end2;
}
}
public class UIDefaultChartStyle : UIChartStyle
{
}
public class UILightChartStyle : UIChartStyle
{
public override Color[] SeriesColor
/// <summary>
/// 计算刻度
/// 起始值必须小于结束值
/// </summary>
/// <param name="start">起始值</param>
/// <param name="end">结束值</param>
/// <param name="expect_num">期望刻度数量,实际数接近此数</param>
/// <returns>刻度列表</returns>
public static double[] CalcDegreeScale(double start, double end, int expect_num)
{
get
if (start >= end)
{
return new[]
{
Color.FromArgb(0,163,219),
Color.FromArgb(0,199,235),
Color.FromArgb(0,227,230),
Color.FromArgb(131,232,187),
Color.FromArgb(255,217,91),
Color.FromArgb(255,153,120),
Color.FromArgb(255,104,139),
Color.FromArgb(245,89,168),
Color.FromArgb(247,139,205),
Color.FromArgb(241,185,242),
Color.FromArgb(156,149,245)
};
throw new Exception("起始值必须小于结束值");
}
}
}
public class UIDarkChartStyle : UIChartStyle
{
public override Color BackColor => Color.FromArgb(54, 54, 54);
CalcDegreeScale(start, end, expect_num, out int degree_start, out int degree_end,
out double degree_gap);
public override Color ForeColor => Color.FromArgb(239, 239, 239);
public override Color[] SeriesColor
{
get
double[] list = new double[degree_end - degree_start + 1];
for (int i = degree_start; i <= degree_end; i++)
{
return new[]
{
Color.FromArgb(242,99,95),
Color.FromArgb(103,154,160),
Color.FromArgb(246,152,130),
Color.FromArgb(122,194,170),
Color.FromArgb(255,119,74),
Color.FromArgb(244,220,120),
Color.FromArgb(98,163,117),
Color.FromArgb(83,186,189),
Color.FromArgb(105,137,170),
Color.FromArgb(124,203,143),
Color.FromArgb(255,154,59)
};
list[i - degree_start] = i * degree_gap;
}
return list;
}
}
}

View File

@ -35,15 +35,14 @@ namespace Sunny.UI
{
if (emptyOption != null) return;
emptyOption = new UIOption();
UIPieOption option = new UIPieOption();
emptyOption.Title = new UITitle();
emptyOption.Title.Text = "SunnyUI";
emptyOption.Title.SubText = "PieChart";
option.Title = new UITitle();
option.Title.Text = "SunnyUI";
option.Title.SubText = "PieChart";
var series = new UISeries();
var series = new UIPieSeries();
series.Name = "饼状图";
series.Type = UISeriesType.Pie;
series.Center = new UICenter(50, 55);
series.Radius = 70;
for (int i = 0; i < 5; i++)
@ -51,10 +50,19 @@ namespace Sunny.UI
series.AddData("Data" + i, (i + 1) * 20);
}
emptyOption.Series.Add(series);
option.Series.Add(series);
emptyOption = option;
}
protected override void DrawTitle(Graphics g, UITitle title)
protected override void DrawOption(Graphics g)
{
if (PieOption == null) return;
if (PieOption.Title != null) DrawTitle(g, PieOption.Title);
if (PieOption.Series.Count > 0) DrawSeries(g, PieOption.Series);
if (PieOption.Legend != null) DrawLegend(g, PieOption.Legend);
}
private void DrawTitle(Graphics g, UITitle title)
{
if (title == null) return;
SizeF sf = g.MeasureString(title.Text, Font);
@ -93,17 +101,17 @@ namespace Sunny.UI
g.DrawString(title.SubText, subFont, ChartStyle.ForeColor, left, top);
}
protected override void CalcData(UIOption o)
protected override void CalcData(UIOption option)
{
Angles.Clear();
UIPieOption o = (UIPieOption)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);
template = new UITemplate(o.ToolTip.Formatter);
}
for (int pieIndex = 0; pieIndex < o.Series.Count; pieIndex++)
{
var pie = o.Series[pieIndex];
@ -130,14 +138,14 @@ namespace Sunny.UI
{
template.Set("a", pie.Name);
template.Set("b", pie.Data[i].Name);
template.Set("c", pie.Data[i].Value.ToString("F" + DecimalNumber));
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("F" + DecimalNumber) + "(" + percent.ToString("F2") + "%)";
text = pie.Data[i].Name + " : " + pie.Data[i].Value.ToString("F2") + "(" + percent.ToString("F2") + "%)";
if (pie.Name.IsValid()) text = pie.Name + '\n' + text;
}
}
@ -148,7 +156,7 @@ namespace Sunny.UI
}
}
protected override void DrawSeries(Graphics g, UIOption o, List<UISeries> series)
private void DrawSeries(Graphics g, List<UIPieSeries> series)
{
if (series == null || series.Count == 0) return;
@ -168,7 +176,7 @@ namespace Sunny.UI
private readonly ConcurrentDictionary<int, ConcurrentDictionary<int, Angle>> Angles = new ConcurrentDictionary<int, ConcurrentDictionary<int, Angle>>();
protected override void DrawLegend(Graphics g, UILegend legend)
private void DrawLegend(Graphics g, UIPieLegend legend)
{
if (legend == null) return;
@ -191,7 +199,7 @@ namespace Sunny.UI
float top = 0;
float left = 0;
if (legend.Orient == Orient.Horizontal)
if (legend.Orient == UIOrient.Horizontal)
{
if (legend.Left == UILeftAlignment.Left) left = TextInterval;
if (legend.Left == UILeftAlignment.Center) left = (Width - totalWidth) / 2.0f;
@ -202,7 +210,7 @@ namespace Sunny.UI
if (legend.Top == UITopAlignment.Bottom) top = Height - oneHeight - TextInterval;
}
if (legend.Orient == Orient.Vertical)
if (legend.Orient == UIOrient.Vertical)
{
if (legend.Left == UILeftAlignment.Left) left = TextInterval;
if (legend.Left == UILeftAlignment.Center) left = (Width - maxWidth) / 2.0f - 10;
@ -219,7 +227,7 @@ namespace Sunny.UI
{
var data = legend.Data[i];
SizeF sf = g.MeasureString(data, LegendFont);
if (legend.Orient == Orient.Horizontal)
if (legend.Orient == UIOrient.Horizontal)
{
g.FillRoundRectangle(ChartStyle.SeriesColor[i % ChartStyle.ColorCount], (int)startleft, (int)top + 1, 18, (int)oneHeight - 2, 5);
g.DrawString(data, LegendFont, ChartStyle.ForeColor, startleft + 20, top);
@ -227,7 +235,7 @@ namespace Sunny.UI
startleft += sf.Width;
}
if (legend.Orient == Orient.Vertical)
if (legend.Orient == UIOrient.Vertical)
{
g.FillRoundRectangle(ChartStyle.SeriesColor[i % ChartStyle.ColorCount], (int)left, (int)starttop + 1, 18, (int)oneHeight - 2, 5);
g.DrawString(data, LegendFont, ChartStyle.ForeColor, left + 20, starttop);
@ -236,27 +244,37 @@ namespace Sunny.UI
}
}
[Browsable(false)]
private UIPieOption PieOption
{
get
{
UIOption option = Option ?? EmptyOption;
UIPieOption o = (UIPieOption)option;
return o;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
UIOption option = Option ?? EmptyOption;
if (option.SeriesCount == 0)
if (PieOption.SeriesCount == 0)
{
SetPieAndAzIndex(-1, -1);
return;
}
for (int pieIndex = 0; pieIndex < option.SeriesCount; pieIndex++)
for (int pieIndex = 0; pieIndex < PieOption.SeriesCount; pieIndex++)
{
RectangleF rect = GetSeriesRect(option.Series[pieIndex]);
RectangleF rect = GetSeriesRect(PieOption.Series[pieIndex]);
if (!e.Location.InRect(rect)) continue;
PointF pf = new PointF(rect.Left + rect.Width / 2.0f, rect.Top + rect.Height / 2.0f);
if (MathEx.CalcDistance(e.Location, pf) * 2 > rect.Width) continue;
double az = MathEx.CalcAngle(e.Location, pf);
for (int azIndex = 0; azIndex < option.Series[pieIndex].Data.Count; azIndex++)
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)
{
@ -311,7 +329,7 @@ namespace Sunny.UI
}
}
private RectangleF GetSeriesRect(UISeries series)
private RectangleF GetSeriesRect(UIPieSeries series)
{
int left = series.Center.Left;
int top = series.Center.Top;
@ -321,7 +339,7 @@ namespace Sunny.UI
return new RectangleF(left - halfRadius, top - halfRadius, halfRadius * 2, halfRadius * 2);
}
public class Angle
internal class Angle
{
public float Start { get; set; }
public float Sweep { get; set; }

View File

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
namespace Sunny.UI
{
public class UIPieOption : UIOption, IDisposable
{
public UITitle Title;
public List<UIPieSeries> Series = new List<UIPieSeries>();
public UIPieLegend Legend;
public UIPieToolTip ToolTip;
public void AddSeries(UIPieSeries series)
{
Series.Clear();
Series.Add(series);
}
public void Dispose()
{
foreach (var series in Series)
{
series?.Dispose();
}
Series.Clear();
}
public int SeriesCount => Series.Count;
}
public class UIDoughnutPieOption : UIOption, IDisposable
{
public UITitle Title;
public List<UIDoughnutPieSeries> Series = new List<UIDoughnutPieSeries>();
public UIPieLegend Legend;
public UIPieToolTip ToolTip;
public void AddSeries(UIDoughnutPieSeries series)
{
Series.Clear();
Series.Add(series);
}
public void Dispose()
{
foreach (var series in Series)
{
series?.Dispose();
}
Series.Clear();
}
public int SeriesCount => Series.Count;
}
public class UIPieToolTip
{
public string Formatter { get; set; } = "{{a}}" + '\n' + "{{b}} : {{c}} ({{d}}%)";
public string ValueFormat { get; set; } = "F0";
}
public class UIPieLegend
{
public UILeftAlignment Left { get; set; } = UILeftAlignment.Center;
public UITopAlignment Top { get; set; } = UITopAlignment.Top;
public UIOrient Orient { get; set; } = UIOrient.Vertical;
public readonly List<string> Data = new List<string>();
public int DataCount => Data.Count;
public void AddData(string data)
{
Data.Add(data);
}
}
public class UIPieSeries : IDisposable
{
public string Name { get; set; }
public UISeriesType Type => UISeriesType.Pie;
public int Radius { get; set; } = 70;
public UICenter Center { get; set; } = new UICenter(50, 50);
public readonly List<UIPieSeriesData> Data = new List<UIPieSeriesData>();
public void AddData(string name, double value)
{
Data.Add(new UIPieSeriesData(name, value));
}
public void Dispose()
{
Data.Clear();
}
}
public class RadiusInOut
{
public int Inner { get; set; }
public int Outer { get; set; }
public RadiusInOut(int inner,int outer)
{
Inner = inner;
Outer = outer;
}
}
public class UIDoughnutPieSeries : IDisposable
{
public string Name { get; set; }
public UISeriesType Type { get; set; }
public RadiusInOut Radius { get; set; } = new RadiusInOut(50,70);
public UICenter Center { get; set; } = new UICenter(50, 50);
public readonly List<UIPieSeriesData> Data = new List<UIPieSeriesData>();
public void AddData(string name, double value)
{
Data.Add(new UIPieSeriesData(name, value));
}
public void Dispose()
{
Data.Clear();
}
}
public class UIPieSeriesData
{
public string Name { get; set; }
public double Value { get; set; }
public UIPieSeriesData()
{
}
public UIPieSeriesData(string name, double value)
{
Name = name;
Value = value;
}
}
}

View File

@ -25,7 +25,6 @@ using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Sunny.UI
@ -86,7 +85,7 @@ namespace Sunny.UI
private void OnMouseWheel(object sender, MouseEventArgs e)
{
base.OnMouseWheel(e);
if (bar!=null && bar.Visible && edit!=null)
if (bar != null && bar.Visible && edit != null)
{
var si = ScrollBarInfo.GetInfo(edit.Handle);
if (e.Delta > 10)
@ -509,7 +508,6 @@ namespace Sunny.UI
edit.Paste(text);
}
internal class TextBoxAutoCompleteSourceConverter : EnumConverter
{
public TextBoxAutoCompleteSourceConverter(Type type) : base(type)
@ -541,6 +539,13 @@ namespace Sunny.UI
set => edit.AcceptsTab = value;
}
[DefaultValue(false)]
public bool EnterAsTab
{
get => edit.EnterAsTab;
set => edit.EnterAsTab = value;
}
[DefaultValue(true)]
public bool ShortcutsEnabled
{

View File

@ -51,10 +51,16 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Charts\UIBarChart.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Charts\UIBarChartOption.cs" />
<Compile Include="Charts\UIChart.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Charts\UIChartStyle.cs" />
<Compile Include="Charts\UIOption.cs" />
<Compile Include="Charts\UIPieChartOption.cs" />
<Compile Include="Controls\Color\UIColorBar.cs">
<SubType>Component</SubType>
</Compile>

View File

@ -1,5 +1,9 @@
+ 增加; - 删除; * 修改
2020.06.14
* 重构图表控件准备开发UIBarChart
* UITextBox增加EnterAsTab
2020.06.12
+ UIComboBox增加数据绑定可设置DataSourceDisplayMemberValueMember