From 5603bc88feff2c467a23d53b2f2704668e65c3d9 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 27 May 2022 15:41:12 +0800 Subject: [PATCH] =?UTF-8?q?*=20UIBarChart:=20=E9=87=8D=E5=86=99Y=E8=BD=B4?= =?UTF-8?q?=E5=9D=90=E6=A0=87=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SunnyUI.Demo/Bin/SunnyUI.Demo.exe | Bin 590336 -> 590336 bytes SunnyUI/Charts/UIBarChart.cs | 274 +++------- SunnyUI/Charts/UIBarChartEx.cs | 819 ------------------------------ 3 files changed, 72 insertions(+), 1021 deletions(-) delete mode 100644 SunnyUI/Charts/UIBarChartEx.cs diff --git a/SunnyUI.Demo/Bin/SunnyUI.Demo.exe b/SunnyUI.Demo/Bin/SunnyUI.Demo.exe index f2ea4088f055810285e2a22536fdb4908f37a815..c277ff1c906c017d7c96fe88e678f711a1835641 100644 GIT binary patch delta 248 zcmZo@QE6yVnb5)VeOuwji9Irm_a?3kXY85G$XL(#WO5^rJkZR@)XvDn2*ga=8JU>* zcMHr7uG;kM&o2KdhhI(n^{FGjU12XX5VHU=D-g47SJ=ybrh)gmUDrkiAYcOGoasMp zIkdG}*XSpo_WSHSThvgyZ&vtQMxd0lRZM7cYEf~FV@hsjUS@GgQDRAc(R4RE4s}hJ z)TGk%nBda9yvk5dJ(twne7%B{B!(?993X?i;2empFi}yA@xb);b{v|l|5X_j9HuwM fbDW<#fhA@;p99CQ{Y(#Rz#`Kp#B&?~3a0}A8KhY~ delta 236 zcmZo@QE6yVnb5(q>saZpi9Irmw* zcMB}8VBT_!C)s1^jK3#s+@jaCE9_+kViq7~1!A`C3VYelH1K*Z+k2M*2$+C4ZTe4J z4s9hrhE?lXvUfUdw&E&fI%jc}5h&$s6%$&VT2vepT$-0xIo;2WLtQ&4H77N(I29rk z>Z#|Fnwzgzkdnl(E`|eSBp93ku@xpNiZSk*zSWLHbNd}T4kN~i1}xk895{aMXS!oE TJ#Zbz`RNJkI1Wsmz>*FCLeEfU diff --git a/SunnyUI/Charts/UIBarChart.cs b/SunnyUI/Charts/UIBarChart.cs index 7aa5e31d..9b144177 100644 --- a/SunnyUI/Charts/UIBarChart.cs +++ b/SunnyUI/Charts/UIBarChart.cs @@ -21,6 +21,7 @@ * 2021-07-22: V3.0.5 增加更新数据的方法 * 2021-01-01: V3.0.9 增加柱子上显示数值 * 2022-03-08: V3.1.1 增加X轴文字倾斜 + * 2022-05-27: V3.1.9 重写Y轴坐标显示 ******************************************************************************/ using System; @@ -65,70 +66,7 @@ namespace Sunny.UI } } - /// - /// 计算刻度 - /// 起始值必须小于结束值 - /// - /// 起始值 - /// 结束值 - /// 期望刻度数量,实际数接近此数 - /// 刻度起始值,须乘以间隔使用 - /// 刻度结束值,须乘以间隔使用 - /// 刻度间隔 - public void CalcDegreeScale(double start, double end, int expect_num, - out int degree_start, out int degree_end, out double degree_gap, out int decimalCount) - { - if (start >= end) - { - 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; - _exponent = Math.Abs(_exponent); - if (fix_step.IsEven()) _exponent--; - if (_exponent < 0) _exponent = 0; - decimalCount = _exponent; - } + UILinearScale YScale = new UILinearScale(); protected override void CalcData() { @@ -157,6 +95,28 @@ namespace Sunny.UI if (!Option.YAxis.MaxAuto) max = Option.YAxis.Max; if (!Option.YAxis.MinAuto) min = Option.YAxis.Min; + if (Option.YAxis.MaxAuto && Option.YAxis.MinAuto) + { + if (min > 0) min = 0; + if (max < 0) max = 0; + + if (min.IsZero() && !max.IsZero()) + { + max = max * 1.2; + } + + if (max.IsZero() && !min.IsZero()) + { + min = min * 1.2; + } + + if (!max.IsZero() && !min.IsZero()) + { + max = max * 1.2; + min = min * 1.2; + } + } + if ((max - min).IsZero()) { if (min.IsZero()) @@ -176,13 +136,16 @@ namespace Sunny.UI } } - CalcDegreeScale(min, max, Option.YAxis.SplitNumber, - out int start, out int end, out double interval, out int decimalCount); + YScale.Max = max; + YScale.Min = min; + YScale.AxisChange(); - YAxisStart = start; - YAxisEnd = end; - YAxisInterval = interval; - YAxisDecimalCount = decimalCount; + YAxisStart = YScale.Min; + YAxisEnd = YScale.Max; + YAxisInterval = YScale.Step; + double[] YLabels = YScale.CalcLabels(); + float[] labels = YScale.CalcYPixels(YLabels, DrawOrigin.Y, DrawSize.Height); + YAxisDecimalCount = YScale.Format.Replace("F", "").ToInt(); float x1 = DrawBarWidth / (Option.SeriesCount * 2 + Option.SeriesCount + 1); float x2 = x1 * 2; @@ -202,70 +165,29 @@ namespace Sunny.UI float ww = Math.Min(x2, series.MaxWidth); xx -= ww / 2.0f; - if (YAxisStart >= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (series.Data[j] - start * interval) / ((end - start) * interval))); + float YZeroPos = YScale.CalcYPixel(0, DrawOrigin.Y, DrawSize.Height); + float VPos = YScale.CalcYPixel(series.Data[j], DrawOrigin.Y, DrawSize.Height); + if (VPos <= YZeroPos) + { Bars[i].Add(new BarInfo() { - Rect = new RectangleF(xx, DrawOrigin.Y - h, ww, h), + Rect = new RectangleF(xx, VPos, ww, (YZeroPos - VPos)), Value = series.Data[j], Color = color, Top = true }); } - else if (YAxisEnd <= 0) + else { - float h = Math.Abs((float)(DrawSize.Height * (end * interval - series.Data[j]) / ((end - start) * interval))); Bars[i].Add(new BarInfo() { - Rect = new RectangleF(xx, Option.Grid.Top + 1, ww, h - 1), + Rect = new RectangleF(xx, YZeroPos, ww, (VPos - YZeroPos)), Value = series.Data[j], Color = color, Top = false }); } - else - { - float lowH = 0; - float highH = 0; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - float lowV = 0; - float highV = 0; - for (int k = YAxisStart; k <= YAxisEnd; k++) - { - if (k < 0) lowH += DrawBarHeight; - if (k > 0) highH += DrawBarHeight; - if (k < 0) lowV += (float)YAxisInterval; - if (k > 0) highV += (float)YAxisInterval; - } - - // lowH.ConsoleWriteLine(); - // highH.ConsoleWriteLine(); - - if (series.Data[j] >= 0) - { - float h = Math.Abs((float)(highH * series.Data[j] / highV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH - h, ww, h), - Value = series.Data[j], - Color = color, - Top = true - }); - } - else - { - float h = Math.Abs((float)(lowH * series.Data[j] / lowV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH + 1, ww, h - 1), - Value = series.Data[j], - Color = color, - Top = false - }); - } - } barX += DrawBarWidth; } @@ -293,8 +215,8 @@ namespace Sunny.UI protected int selectIndex = -1; protected float DrawBarWidth; - protected int YAxisStart; - protected int YAxisEnd; + protected double YAxisStart; + protected double YAxisEnd; protected double YAxisInterval; protected int YAxisDecimalCount; protected readonly ConcurrentDictionary> Bars = new ConcurrentDictionary>(); @@ -367,11 +289,6 @@ namespace Sunny.UI UIOption option = BaseOption ?? EmptyOption; return (UIBarOption)option; } - - // set - // { - // SetOption(value); - // } } protected override void CreateEmptyOption() @@ -427,10 +344,12 @@ namespace Sunny.UI if (!NeedDraw) return; if (Option.ToolTip != null && Option.ToolTip.AxisPointer.Type == UIAxisPointerType.Shadow) DrawToolTip(g); + + DrawSeries(g, Option.Series); DrawAxis(g); DrawTitle(g, Option.Title); - DrawSeries(g, Option.Series); if (Option.ToolTip != null && Option.ToolTip.AxisPointer.Type == UIAxisPointerType.Line) DrawToolTip(g); + DrawLegend(g, Option.Legend); DrawAxisScales(g); } @@ -453,45 +372,22 @@ namespace Sunny.UI protected virtual void DrawAxis(Graphics g) { + g.FillRectangle(FillColor, Option.Grid.Left, 1, Width - Option.Grid.Left - Option.Grid.Right, Option.Grid.Top); + g.FillRectangle(FillColor, Option.Grid.Left, Height - Option.Grid.Bottom, Width - Option.Grid.Left - Option.Grid.Right, Option.Grid.Bottom - 1); + if (YAxisStart >= 0) g.DrawLine(ForeColor, DrawOrigin, new Point(DrawOrigin.X + DrawSize.Width, DrawOrigin.Y)); if (YAxisEnd <= 0) g.DrawLine(ForeColor, new Point(DrawOrigin.X, Option.Grid.Top), new Point(DrawOrigin.X + DrawSize.Width, Option.Grid.Top)); g.DrawLine(ForeColor, DrawOrigin, new Point(DrawOrigin.X, DrawOrigin.Y - DrawSize.Height)); + g.DrawLine(ForeColor, DrawOrigin, new Point(Width - Option.Grid.Right, DrawOrigin.Y)); if (Option.XAxis.AxisTick.Show) { - float start; - - if (Option.XAxis.AxisTick.AlignWithLabel) + float start = DrawOrigin.X + DrawBarWidth / 2.0f; + for (int i = 0; i < Option.XAxis.Data.Count; i++) { - start = DrawOrigin.X + DrawBarWidth / 2.0f; - for (int i = 0; i < Option.XAxis.Data.Count; i++) - { - g.DrawLine(ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += DrawBarWidth; - } - } - else - { - bool haveZero = false; - for (int i = YAxisStart; i <= YAxisEnd; i++) - { - if (i == 0) - { - haveZero = true; - break; - } - } - - if (!haveZero) - { - start = DrawOrigin.X; - for (int i = 0; i <= Option.XAxis.Data.Count; i++) - { - g.DrawLine(ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += DrawBarWidth; - } - } + g.DrawLine(ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); + start += DrawBarWidth; } } @@ -503,7 +399,7 @@ namespace Sunny.UI SizeF sf = g.MeasureString(data, TempFont); int angle = (Option.XAxis.AxisLabel.Angle + 36000) % 360; if (angle > 0 && angle <= 90) - g.DrawString(data, TempFont, ForeColor, new PointF(start, DrawOrigin.Y), + g.DrawString(data, TempFont, ForeColor, new PointF(start, DrawOrigin.Y + Option.XAxis.AxisTick.Length), new StringFormat() { Alignment = StringAlignment.Far }, (3600 - Option.XAxis.AxisLabel.Angle) % 360); else g.DrawString(data, TempFont, ForeColor, start - sf.Width / 2.0f, DrawOrigin.Y + Option.XAxis.AxisTick.Length); start += DrawBarWidth; @@ -513,63 +409,39 @@ namespace Sunny.UI g.DrawString(Option.XAxis.Name, TempFont, ForeColor, DrawOrigin.X + (DrawSize.Width - sfname.Width) / 2.0f, DrawOrigin.Y + Option.XAxis.AxisTick.Length + sfname.Height); } - if (Option.YAxis.AxisTick.Show) + double[] YLabels = YScale.CalcLabels(); + float[] labels = YScale.CalcYPixels(YLabels, DrawOrigin.Y, DrawSize.Height); + for (int i = 0; i < labels.Length; i++) { - float start = DrawOrigin.Y; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - for (int i = YAxisStart; i <= YAxisEnd; i++) + if (labels[i] > DrawOrigin.Y) continue; + if (labels[i] < Option.Grid.Top) continue; + if (Option.YAxis.AxisTick.Show) { - g.DrawLine(ForeColor, DrawOrigin.X, start, DrawOrigin.X - Option.YAxis.AxisTick.Length, start); - - if (i != 0) + g.DrawLine(ForeColor, DrawOrigin.X, labels[i], DrawOrigin.X - Option.YAxis.AxisTick.Length, labels[i]); + if (!YLabels[i].EqualsDouble(0)) { using (Pen pn = new Pen(ForeColor)) { pn.DashStyle = DashStyle.Dash; pn.DashPattern = new float[] { 3, 3 }; - g.DrawLine(pn, DrawOrigin.X, start, Width - Option.Grid.Right, start); + g.DrawLine(pn, DrawOrigin.X, labels[i], Width - Option.Grid.Right, labels[i]); } } else { - g.DrawLine(ForeColor, DrawOrigin.X, start, Width - Option.Grid.Right, start); - - float lineStart = DrawOrigin.X; - for (int j = 0; j <= Option.XAxis.Data.Count; j++) - { - g.DrawLine(ForeColor, lineStart, start, lineStart, start + Option.XAxis.AxisTick.Length); - lineStart += DrawBarWidth; - } + g.DrawLine(ForeColor, DrawOrigin.X, labels[i], Width - Option.Grid.Right, labels[i]); } - - start -= DrawBarHeight; } - } - if (Option.YAxis.AxisLabel.Show) - { - float start = DrawOrigin.Y; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - int idx = 0; - float wmax = 0; - - if (Option.YAxis.AxisLabel.AutoFormat) - Option.YAxis.AxisLabel.DecimalCount = YAxisDecimalCount; - - for (int i = YAxisStart; i <= YAxisEnd; i++) + if (Option.YAxis.AxisLabel.Show) { - string label = Option.YAxis.AxisLabel.GetLabel(i * YAxisInterval, idx); - SizeF sf = g.MeasureString(label, TempFont); - wmax = Math.Max(wmax, sf.Width); - g.DrawString(label, TempFont, ForeColor, DrawOrigin.X - Option.YAxis.AxisTick.Length - sf.Width, start - sf.Height / 2.0f); - start -= DrawBarHeight; - } + if (Option.YAxis.AxisLabel.AutoFormat) + Option.YAxis.AxisLabel.DecimalCount = YAxisDecimalCount; - SizeF sfname = g.MeasureString(Option.YAxis.Name, TempFont); - int x = (int)(DrawOrigin.X - Option.YAxis.AxisTick.Length - wmax - sfname.Height); - int y = (int)(Option.Grid.Top + (DrawSize.Height - sfname.Width) / 2); - g.DrawString(Option.YAxis.Name, TempFont, ForeColor, new Point(x, y), - new StringFormat() { Alignment = StringAlignment.Center }, 270); + string label = YLabels[i].ToString("F" + Option.YAxis.AxisLabel.DecimalCount); + SizeF sf = g.MeasureString(label, TempFont); + g.DrawString(label, TempFont, ForeColor, DrawOrigin.X - Option.YAxis.AxisTick.Length - sf.Width, labels[i] - sf.Height / 2.0f); + } } } @@ -579,8 +451,7 @@ namespace Sunny.UI { double ymin = YAxisStart * YAxisInterval; double ymax = YAxisEnd * YAxisInterval; - float pos = (float)((line.Value - ymin) * (Height - Option.Grid.Top - Option.Grid.Bottom) / (ymax - ymin)); - pos = (Height - Option.Grid.Bottom - pos); + float pos = YScale.CalcYPixel(line.Value, DrawOrigin.Y, DrawSize.Height); if (pos <= Option.Grid.Top || pos >= Height - Option.Grid.Bottom) continue; using (Pen pn = new Pen(line.Color, line.Size)) { @@ -588,7 +459,6 @@ namespace Sunny.UI } SizeF sf = g.MeasureString(line.Name, TempFont); - if (line.Left == UILeftAlignment.Left) g.DrawString(line.Name, TempFont, line.Color, DrawOrigin.X + 4, pos - 2 - sf.Height); if (line.Left == UILeftAlignment.Center) diff --git a/SunnyUI/Charts/UIBarChartEx.cs b/SunnyUI/Charts/UIBarChartEx.cs deleted file mode 100644 index d40c3141..00000000 --- a/SunnyUI/Charts/UIBarChartEx.cs +++ /dev/null @@ -1,819 +0,0 @@ -/****************************************************************************** - * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。 - * CopyRight (C) 2012-2022 ShenYongHua(沈永华). - * QQ群:56829229 QQ:17612584 EMail:SunnyUI@QQ.Com - * - * Blog: https://www.cnblogs.com/yhuse - * Gitee: https://gitee.com/yhuse/SunnyUI - * GitHub: https://github.com/yhuse/SunnyUI - * - * SunnyUI.dll can be used for free under the GPL-3.0 license. - * If you use this code, please keep this note. - * 如果您使用此代码,请保留此说明。 - ****************************************************************************** - * 文件名称: UIBarChartEx.cs - * 文件说明: 柱状图 - * 当前版本: V3.1 - * 创建日期: 2020-09-26 - * - * 2020-09-26: V2.2.8 增加文件说明 -******************************************************************************/ - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Linq; -using System.Windows.Forms; - -namespace Sunny.UI -{ - [ToolboxItem(false)] - public sealed class UIBarChartEx : UIBarChart - { - protected override void CalcData() - { - Bars.Clear(); - NeedDraw = false; - if (Option == null || Option.Series == null || Option.SeriesCount == 0) return; - if (DrawSize.Width <= 0 || DrawSize.Height <= 0) return; - if (Option.Series.Count == 0) return; - - NeedDraw = true; - DrawBarWidth = DrawSize.Width * 1.0f / Option.Series.Count; - - double min = double.MaxValue; - double max = double.MinValue; - foreach (var series in Option.Series) - { - min = Math.Min(min, series.Data.Min()); - max = Math.Max(max, series.Data.Max()); - } - - if (min > 0 && max > 0 && !Option.YAxis.Scale) - { - min = 0; - } - - if (min < 0 && max < 0 && !Option.YAxis.Scale) - { - max = 0; - } - - if (!Option.YAxis.MaxAuto) max = Option.YAxis.Max; - if (!Option.YAxis.MinAuto) min = Option.YAxis.Min; - - if ((max - min).IsZero()) - { - if (min.IsZero()) - { - max = 100; - min = 0; - } - else if (max > 0) - { - max = max * 2; - min = 0; - } - else - { - max = 0; - min = min * 2; - } - } - - CalcDegreeScale(min, max, Option.YAxis.SplitNumber, - out int start, out int end, out double interval, out int decimalCount); - - YAxisStart = start; - YAxisEnd = end; - YAxisInterval = interval; - YAxisDecimalCount = decimalCount; - float barX = DrawOrigin.X; - - if (Option.AutoSizeBars) - { - //每个柱子等宽 - float x1 = DrawSize.Width * 1.0f / DataCount / 4; - float x2 = x1 * 2; - - if (Option.AutoSizeBarsCompact) - { - //紧凑 - for (int i = 0; i < Option.SeriesCount; i++) - { - var series = Option.Series[i]; - Bars.TryAdd(i, new List()); - float sx = barX + x1 * ((series.Data.Count * 4 - (series.Data.Count * 2 + (series.Data.Count - 1) * Option.AutoSizeBarsCompactValue)) / 2.0f) + x1; - - for (int j = 0; j < series.Data.Count; j++) - { - Color color = ChartStyle.GetColor(i); - if (series.Colors.Count > 0 && j >= 0 && j < series.Colors.Count) - color = series.Colors[j]; - - float ww = Math.Min(x2, series.MaxWidth); - float xx = sx - ww / 2.0f; - - if (YAxisStart >= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (series.Data[j] - start * interval) / ((end - start) * interval))); - - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - h, ww, h), - Color = color - }); - } - else if (YAxisEnd <= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (end * interval - series.Data[j]) / ((end - start) * interval))); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, Option.Grid.Top + 1, ww, h - 1), - Color = color - }); - } - else - { - float lowH = 0; - float highH = 0; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - float lowV = 0; - float highV = 0; - for (int k = YAxisStart; k <= YAxisEnd; k++) - { - if (k < 0) lowH += DrawBarHeight; - if (k > 0) highH += DrawBarHeight; - if (k < 0) lowV += (float)YAxisInterval; - if (k > 0) highV += (float)YAxisInterval; - } - - if (series.Data[j] >= 0) - { - float h = Math.Abs((float)(highH * series.Data[j] / highV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH - h, ww, h), - Color = color - }); - } - else - { - float h = Math.Abs((float)(lowH * series.Data[j] / lowV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH + 1, ww, h - 1), - Color = color - }); - } - } - - sx += x2 + x1 * Option.AutoSizeBarsCompactValue; - } - - barX += x2 * 2 * series.Data.Count; - } - } - else - { - //宽松 - for (int i = 0; i < Option.SeriesCount; i++) - { - var series = Option.Series[i]; - Bars.TryAdd(i, new List()); - - for (int j = 0; j < series.Data.Count; j++) - { - Color color = ChartStyle.GetColor(i); - if (series.Colors.Count > 0 && j >= 0 && j < series.Colors.Count) - color = series.Colors[j]; - - float xx = barX + DrawSize.Width * 1.0f / DataCount / 2; - float ww = Math.Min(x2, series.MaxWidth); - xx -= ww / 2.0f; - - if (YAxisStart >= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (series.Data[j] - start * interval) / ((end - start) * interval))); - - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - h, ww, h), - Color = color - }); - } - else if (YAxisEnd <= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (end * interval - series.Data[j]) / ((end - start) * interval))); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, Option.Grid.Top + 1, ww, h - 1), - Color = color - }); - } - else - { - float lowH = 0; - float highH = 0; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - float lowV = 0; - float highV = 0; - for (int k = YAxisStart; k <= YAxisEnd; k++) - { - if (k < 0) lowH += DrawBarHeight; - if (k > 0) highH += DrawBarHeight; - if (k < 0) lowV += (float)YAxisInterval; - if (k > 0) highV += (float)YAxisInterval; - } - - if (series.Data[j] >= 0) - { - float h = Math.Abs((float)(highH * series.Data[j] / highV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH - h, ww, h), - Color = color - }); - } - else - { - float h = Math.Abs((float)(lowH * series.Data[j] / lowV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH + 1, ww, h - 1), - Color = color - }); - } - } - - barX += DrawSize.Width * 1.0f / DataCount; - } - } - } - } - else - { - //每个序列等宽 - if (Option.AutoSizeBarsCompact) - { - //紧凑 - - float CompactWidth = DrawBarWidth / 4.0f * Option.AutoSizeBarsCompactValue; - - for (int i = 0; i < Option.SeriesCount; i++) - { - var series = Option.Series[i]; - float x1 = DrawBarWidth / series.Data.Count / 4.0f; - float x2 = x1 * 2; - float ww = Math.Min(x2, series.MaxWidth); - - float sx = barX + (DrawBarWidth - ww * series.Data.Count - (series.Data.Count - 1) * CompactWidth) / 2.0f; - - Bars.TryAdd(i, new List()); - for (int j = 0; j < series.Data.Count; j++) - { - Color color = ChartStyle.GetColor(i); - if (series.Colors.Count > 0 && j >= 0 && j < series.Colors.Count) - color = series.Colors[j]; - - float xx = sx; - - if (YAxisStart >= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (series.Data[j] - start * interval) / ((end - start) * interval))); - - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - h, ww, h), - Color = color - }); - } - else if (YAxisEnd <= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (end * interval - series.Data[j]) / ((end - start) * interval))); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, Option.Grid.Top + 1, ww, h - 1), - Color = color - }); - } - else - { - float lowH = 0; - float highH = 0; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - float lowV = 0; - float highV = 0; - for (int k = YAxisStart; k <= YAxisEnd; k++) - { - if (k < 0) lowH += DrawBarHeight; - if (k > 0) highH += DrawBarHeight; - if (k < 0) lowV += (float)YAxisInterval; - if (k > 0) highV += (float)YAxisInterval; - } - - if (series.Data[j] >= 0) - { - float h = Math.Abs((float)(highH * series.Data[j] / highV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH - h, ww, h), - Color = color - }); - } - else - { - float h = Math.Abs((float)(lowH * series.Data[j] / lowV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH + 1, ww, h - 1), - Color = color - }); - } - } - - sx += ww + CompactWidth; - } - - barX += DrawBarWidth; - } - } - else - { - //宽松 - for (int i = 0; i < Option.SeriesCount; i++) - { - var series = Option.Series[i]; - float x1; - if (Option.FixedSeriesCount > 0) - x1 = DrawBarWidth / (Option.FixedSeriesCount * 2 + Option.FixedSeriesCount + 1); - else - x1 = DrawBarWidth / (series.Data.Count * 2 + series.Data.Count + 1); - - float x2 = x1 * 2; - - Bars.TryAdd(i, new List()); - for (int j = 0; j < series.Data.Count; j++) - { - Color color = ChartStyle.GetColor(i); - if (series.Colors.Count > 0 && j >= 0 && j < series.Colors.Count) - color = series.Colors[j]; - - float xx; - if (Option.FixedSeriesCount > 0) - xx = barX + DrawBarWidth / (series.Data.Count * 2 + series.Data.Count + 1) * ((j + 1) * 3 - 1); - else - xx = barX + x1 * ((j + 1) * 3 - 1); - - float ww = Math.Min(x2, series.MaxWidth); - xx -= ww / 2.0f; - - if (YAxisStart >= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (series.Data[j] - start * interval) / ((end - start) * interval))); - - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - h, ww, h), - Color = color - }); - } - else if (YAxisEnd <= 0) - { - float h = Math.Abs((float)(DrawSize.Height * (end * interval - series.Data[j]) / ((end - start) * interval))); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, Option.Grid.Top + 1, ww, h - 1), - Color = color - }); - } - else - { - float lowH = 0; - float highH = 0; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - float lowV = 0; - float highV = 0; - for (int k = YAxisStart; k <= YAxisEnd; k++) - { - if (k < 0) lowH += DrawBarHeight; - if (k > 0) highH += DrawBarHeight; - if (k < 0) lowV += (float)YAxisInterval; - if (k > 0) highV += (float)YAxisInterval; - } - - if (series.Data[j] >= 0) - { - float h = Math.Abs((float)(highH * series.Data[j] / highV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH - h, ww, h), - Color = color - }); - } - else - { - float h = Math.Abs((float)(lowH * series.Data[j] / lowV)); - Bars[i].Add(new BarInfo() - { - Rect = new RectangleF(xx, DrawOrigin.Y - lowH + 1, ww, h - 1), - Color = color - }); - } - } - } - - barX += DrawBarWidth; - } - } - } - - if (Option.ToolTip != null) - { - for (int i = 0; i < Option.Series.Count; i++) - { - var series = Option.Series[i]; - string str = Option.Series[i].Name; - for (int j = 0; j < series.Data.Count; j++) - { - str += '\n'; - if (series.BarName.Count > 0 && j < series.BarName.Count) - str += series.BarName[j] + " : "; - - str += series.Data[j].ToString(Option.ToolTip.ValueFormat); - } - - Bars[i][0].Tips = str; - } - } - } - - protected override void DrawSeries(Graphics g, List series) - { - if (series == null || series.Count == 0) return; - - for (int i = 0; i < Bars.Count; i++) - { - var bars = Bars[i]; - for (int j = 0; j < bars.Count; j++) - { - g.FillRectangle(bars[j].Color, bars[j].Rect); - var s = Option.Series[i]; - if (s.ShowBarName) - { - if (s.BarName.Count > 0 && j < s.BarName.Count) - { - SizeF sf = g.MeasureString(s.BarName[j], TempFont); - if (s.Data[j] >= 0) - g.DrawString(s.BarName[j], TempFont, ChartStyle.ForeColor, - bars[j].Rect.Left + bars[j].Rect.Width / 2 - sf.Width / 2, - bars[j].Rect.Bottom + 1); - else - g.DrawString(s.BarName[j], TempFont, ChartStyle.ForeColor, - bars[j].Rect.Left + bars[j].Rect.Width / 2 - sf.Width / 2, - bars[j].Rect.Top - sf.Height); - } - } - - if (s.ShowValue) - { - string value = s.Data[j].ToString("F" + Option.YAxis.AxisLabel.DecimalCount); - SizeF sf = g.MeasureString(value, TempFont); - if (s.Data[j] < 0) - g.DrawString(value, TempFont, bars[j].Color, - bars[j].Rect.Left + bars[j].Rect.Width / 2 - sf.Width / 2, - bars[j].Rect.Bottom + 1); - else - g.DrawString(value, TempFont, bars[j].Color, - bars[j].Rect.Left + bars[j].Rect.Width / 2 - sf.Width / 2, - bars[j].Rect.Top - sf.Height); - } - } - } - - for (int i = 0; i < Option.Series.Count; i++) - { - Bars[i][0].Size = g.MeasureString(Bars[i][0].Tips, TempFont); - } - } - - private int DataCount - { - get - { - int dataCount = 0; - for (int i = 0; i < Option.SeriesCount; i++) - { - dataCount += Option.Series[i].Data.Count; - } - - return dataCount; - } - } - - protected override void DrawAxis(Graphics g) - { - if (YAxisStart >= 0) g.DrawLine(ChartStyle.ForeColor, DrawOrigin, new Point(DrawOrigin.X + DrawSize.Width, DrawOrigin.Y)); - if (YAxisEnd <= 0) g.DrawLine(ChartStyle.ForeColor, new Point(DrawOrigin.X, Option.Grid.Top), new Point(DrawOrigin.X + DrawSize.Width, Option.Grid.Top)); - - g.DrawLine(ChartStyle.ForeColor, DrawOrigin, new Point(DrawOrigin.X, DrawOrigin.Y - DrawSize.Height)); - - //绘制X轴刻度 - if (Option.XAxis.AxisTick.Show) - { - float start; - - if (Option.XAxis.AxisTick.AlignWithLabel) - { - if (Option.AutoSizeBars) - { - start = DrawOrigin.X; - for (int i = 0; i < Option.Series.Count; i++) - { - float w = DrawSize.Width * Option.Series[i].Data.Count * 1.0f / DataCount; - g.DrawLine(ChartStyle.ForeColor, start + (int)(w / 2), DrawOrigin.Y, start + (int)(w / 2), DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += (int)w; - } - } - else - { - start = DrawOrigin.X + DrawBarWidth / 2.0f; - for (int i = 0; i < Option.Series.Count; i++) - { - g.DrawLine(ChartStyle.ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += DrawBarWidth; - } - } - } - else - { - start = DrawOrigin.X; - - if (Option.AutoSizeBars) - { - for (int i = 0; i < Option.Series.Count; i++) - { - float w = DrawSize.Width * Option.Series[i].Data.Count * 1.0f / DataCount; - g.DrawLine(ChartStyle.ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += (int)w; - } - - g.DrawLine(ChartStyle.ForeColor, DrawOrigin.X + DrawSize.Width, DrawOrigin.Y, DrawOrigin.X + DrawSize.Width, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - } - else - { - for (int i = 0; i <= Option.Series.Count; i++) - { - g.DrawLine(ChartStyle.ForeColor, start, DrawOrigin.Y, start, DrawOrigin.Y + Option.XAxis.AxisTick.Length); - start += DrawBarWidth; - } - } - } - } - - //绘制X轴标签 - if (Option.XAxis.AxisLabel.Show) - { - if (Option.AutoSizeBars) - { - float start = DrawOrigin.X; - foreach (var data in Option.Series) - { - float w = DrawSize.Width * data.Data.Count * 1.0f / DataCount; - SizeF sf = g.MeasureString(data.Name, TempFont); - if (Option.XAxis.AxisLabel.Angle != 0) - g.DrawString(data.Name, TempFont, ChartStyle.ForeColor, new PointF(start + w / 2.0f - 10, DrawOrigin.Y + Option.Grid.Bottom / 2.0f), - new StringFormat() { Alignment = StringAlignment.Center }, (3600 - Option.XAxis.AxisLabel.Angle) % 360); - else - g.DrawString(data.Name, TempFont, ChartStyle.ForeColor, start + w / 2.0f - sf.Width / 2.0f, DrawOrigin.Y + Option.XAxis.AxisTick.Length + Option.XAxis.AxisTick.Distance); - start += w; - } - } - else - { - float start = DrawOrigin.X + DrawBarWidth / 2.0f; - foreach (var data in Option.Series) - { - SizeF sf = g.MeasureString(data.Name, TempFont); - if (Option.XAxis.AxisLabel.Angle != 0) - g.DrawString(data.Name, TempFont, ChartStyle.ForeColor, new PointF(start - 10, DrawOrigin.Y + Option.Grid.Bottom / 2.0f), - new StringFormat() { Alignment = StringAlignment.Center }, (3600 - Option.XAxis.AxisLabel.Angle) % 360); - else - g.DrawString(data.Name, TempFont, ChartStyle.ForeColor, start - sf.Width / 2.0f, DrawOrigin.Y + Option.XAxis.AxisTick.Length + Option.XAxis.AxisTick.Distance); - start += DrawBarWidth; - } - } - - SizeF sfName = g.MeasureString(Option.XAxis.Name, TempFont); - g.DrawString(Option.XAxis.Name, TempFont, ChartStyle.ForeColor, DrawOrigin.X + (DrawSize.Width - sfName.Width) / 2.0f, DrawOrigin.Y + Option.XAxis.AxisTick.Length + Option.XAxis.AxisTick.Distance + sfName.Height); - } - - //绘制Y轴刻度 - if (Option.YAxis.AxisTick.Show) - { - float start = DrawOrigin.Y; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - for (int i = YAxisStart; i <= YAxisEnd; i++) - { - g.DrawLine(ChartStyle.ForeColor, DrawOrigin.X, start, DrawOrigin.X - Option.YAxis.AxisTick.Length, start); - - if (i != 0) - { - using (Pen pn = new Pen(ChartStyle.ForeColor)) - { - pn.DashStyle = DashStyle.Dash; - pn.DashPattern = new float[] { 3, 3 }; - g.DrawLine(pn, DrawOrigin.X, start, Width - Option.Grid.Right, start); - } - } - else - { - g.DrawLine(ChartStyle.ForeColor, DrawOrigin.X, start, Width - Option.Grid.Right, start); - - // float lineStart = DrawOrigin.X; - // for (int j = 0; j <= BarOption.Series.Count; j++) - // { - // g.DrawLine(ChartStyle.ForeColor, lineStart, start, lineStart, start + BarOption.XAxis.AxisTick.Length); - // lineStart += DrawBarWidth; - // } - } - - start -= DrawBarHeight; - } - } - - //绘制Y轴标签 - if (Option.YAxis.AxisLabel.Show) - { - float start = DrawOrigin.Y; - float DrawBarHeight = DrawSize.Height * 1.0f / (YAxisEnd - YAxisStart); - int idx = 0; - float wmax = 0; - - if (Option.YAxis.AxisLabel.AutoFormat) - Option.YAxis.AxisLabel.DecimalCount = YAxisDecimalCount; - - for (int i = YAxisStart; i <= YAxisEnd; i++) - { - string label = Option.YAxis.AxisLabel.GetLabel(i * YAxisInterval, idx); - SizeF sf = g.MeasureString(label, TempFont); - wmax = Math.Max(wmax, sf.Width); - g.DrawString(label, TempFont, ChartStyle.ForeColor, DrawOrigin.X - Option.YAxis.AxisTick.Length - sf.Width, start - sf.Height / 2.0f); - start -= DrawBarHeight; - } - - SizeF sfname = g.MeasureString(Option.YAxis.Name, TempFont); - int x = (int)(DrawOrigin.X - Option.YAxis.AxisTick.Length - wmax - sfname.Height); - int y = (int)(Option.Grid.Top + (DrawSize.Height - sfname.Width) / 2); - g.DrawString(Option.YAxis.Name, TempFont, ChartStyle.ForeColor, new Point(x, y), - new StringFormat() { Alignment = StringAlignment.Center }, 270); - } - } - - protected override void OnMouseMove(MouseEventArgs e) - { - try - { - if (!Option.ToolTip.Visible) return; - if (e.Location.X > Option.Grid.Left && - e.Location.X < Width - Option.Grid.Right && - e.Location.Y > Option.Grid.Top && - e.Location.Y < Height - Option.Grid.Bottom) - { - if (Option.AutoSizeBars) - { - float startX = DrawOrigin.X; - for (int i = 0; i < Option.Series.Count; i++) - { - float w = DrawSize.Width * Option.Series[i].Data.Count * 1.0f / DataCount; - if (e.Location.X >= startX && e.Location.X < startX + w) - { - SelectIndex = i; - break; - } - - startX += w; - } - } - else - { - SelectIndex = (int)((e.Location.X - Option.Grid.Left) / DrawBarWidth); - } - } - else - { - SelectIndex = -1; - } - - if (SelectIndex >= 0 && Bars.Count > 0) - { - if (tip.Text != Bars[selectIndex][0].Tips) - { - tip.Text = Bars[selectIndex][0].Tips; - tip.Size = new Size((int)Bars[selectIndex][0].Size.Width + 4, (int)Bars[selectIndex][0].Size.Height + 4); - } - - int x = e.Location.X + 15; - int y = e.Location.Y + 20; - if (e.Location.X + 15 + tip.Width > Width - Option.Grid.Right) - x = e.Location.X - tip.Width - 2; - if (e.Location.Y + 20 + tip.Height > Height - Option.Grid.Bottom) - y = e.Location.Y - tip.Height - 2; - - tip.Left = x; - tip.Top = y; - if (!tip.Visible) tip.Visible = Bars[selectIndex][0].Tips.IsValid(); - } - } - catch (Exception exception) - { - Console.WriteLine(exception); - } - } - - protected override void DrawToolTip(Graphics g) - { - if (selectIndex < 0) return; - - if (Option.AutoSizeBars) - { - float startX = DrawOrigin.X; - - for (int i = 0; i < Option.SeriesCount; i++) - { - float w = DrawSize.Width * Option.Series[i].Data.Count * 1.0f / DataCount; - - if (i == selectIndex) - { - if (Option.ToolTip.AxisPointer.Type == UIAxisPointerType.Line) - { - float x = startX + w / 2.0f; - g.DrawLine(ChartStyle.ToolTipShadowColor, x, DrawOrigin.Y, x, Option.Grid.Top); - } - - if (Option.ToolTip.AxisPointer.Type == UIAxisPointerType.Shadow) - { - g.FillRectangle(ChartStyle.ToolTipShadowColor, startX, Option.Grid.Top, w, Height - Option.Grid.Top - Option.Grid.Bottom); - } - } - - startX += w; - } - } - else - { - base.DrawToolTip(g); - } - } - - protected override void CreateEmptyOption() - { - if (emptyOption != null) return; - - UIBarOption option = new UIBarOption(); - option.Title = new UITitle(); - option.Title.Text = "SunnyUI"; - option.Title.SubText = "BarChartEx"; - - //设置Legend - option.Legend = new UILegend(); - option.Legend.Orient = UIOrient.Horizontal; - option.Legend.Top = UITopAlignment.Top; - option.Legend.Left = UILeftAlignment.Left; - option.Legend.AddData("Bar1"); - option.Legend.AddData("Bar2"); - option.Legend.AddData("Bar3"); - - var series = new UIBarSeries(); - series.Name = "Bar1"; - series.AddData(1); - option.Series.Add(series); - - series = new UIBarSeries(); - series.Name = "Bar2"; - series.AddData(2); - series.AddData(3); - option.Series.Add(series); - - series = new UIBarSeries(); - series.Name = "Bar2"; - series.AddData(4); - series.AddData(5); - series.AddData(6); - option.Series.Add(series); - - 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.ToolTip = new UIBarToolTip(); - option.ToolTip.AxisPointer.Type = UIAxisPointerType.Shadow; - - emptyOption = option; - } - } -}