* UILineChart: 数据显示的小数位数重构调整至数据序列Series.XAxisDecimalPlaces,XAxisDateTimeFormat,YAxisDecimalPlaces

This commit is contained in:
Sunny 2022-07-30 10:02:13 +08:00
parent 495328c1e5
commit 0fd7299962
4 changed files with 314 additions and 335 deletions

View File

@ -138,277 +138,6 @@ namespace Sunny.UI
Line, Shadow
}
public class UIAxis
{
public UIAxis()
{
}
public UIAxis(UIAxisType axisType)
{
Type = axisType;
}
public string Name { get; set; }
public UIAxisType Type { get; }
/// <summary>
/// 坐标轴的分割段数,需要注意的是这个分割段数只是个预估值
/// 最后实际显示的段数会在这个基础上根据分割后坐标轴刻度显示的易读程度作调整。
/// 在类目轴中无效。
/// </summary>
public int SplitNumber { get; set; } = 5;
/// <summary>
/// 是否是脱离 0 值比例。设置成 true 后坐标刻度不会强制包含零刻度。在双数值轴的散点图中比较有用。
/// </summary>
public bool Scale { get; set; }
/// <summary>
/// 坐标轴刻度
/// </summary>
public UIAxisTick AxisTick = new UIAxisTick();
/// <summary>
/// 坐标轴标签
/// </summary>
public UIAxisLabel AxisLabel = new UIAxisLabel();
public bool MaxAuto { get; set; } = true;
public bool MinAuto { get; set; } = true;
public double Max { get; set; } = 100;
public double Min { get; set; } = 0;
public List<string> Data = new List<string>();
public bool ShowGridLine { get; set; } = true;
public void Clear()
{
Data.Clear();
}
public CustomLabels CustomLabels;
public bool HaveCustomLabels
{
get => CustomLabels != null && CustomLabels.Count > 0;
}
public void SetMinValue(double min)
{
Min = min;
MinAuto = false;
}
public void SetMaxValue(double max)
{
Max = max;
MaxAuto = false;
}
public void SetRange(double min, double max)
{
SetMinValue(min);
SetMaxValue(max);
}
public void SetMinValue(DateTime min)
{
Min = new DateTimeInt64(min);
MinAuto = false;
}
public void SetMaxValue(DateTime max)
{
Max = new DateTimeInt64(max);
MaxAuto = false;
}
public void SetRange(DateTime min, DateTime max)
{
SetMinValue(min);
SetMaxValue(max);
}
}
public class CustomLabels
{
public double Start { get; set; }
public double Interval { get; set; }
public int Count { get; set; }
public double IntervalMilliseconds { get; set; }
public UIAxisType AxisType { get; set; }
public List<string> Labels = new List<string>();
public double[] LabelValues()
{
double[] values = new double[Count + 1];
for (int i = 0; i <= Count; i++)
{
if (AxisType == UIAxisType.DateTime)
{
DateTimeInt64 dateTime = new DateTimeInt64(Start);
dateTime.AddMilliseconds(IntervalMilliseconds * i);
values[i] = dateTime.DoubleValue;
}
else
{
values[i] = Start + Interval * i;
}
}
return values;
}
public double Stop
{
get
{
if (AxisType == UIAxisType.DateTime)
{
DateTimeInt64 dateTime = new DateTimeInt64(Start);
dateTime.AddMilliseconds(IntervalMilliseconds * Count);
return dateTime.DoubleValue;
}
else
{
return Start + Interval * Count;
}
}
}
public CustomLabels(double start, double interval, int count)
{
Start = start;
Interval = Math.Abs(interval);
Count = Math.Max(2, count);
AxisType = UIAxisType.Value;
}
public CustomLabels(DateTime start, int intervalMilliseconds, int count)
{
Start = new DateTimeInt64(start);
IntervalMilliseconds = Math.Abs(intervalMilliseconds);
Count = Math.Max(2, count);
AxisType = UIAxisType.DateTime;
}
public void SetLabels(string[] labels)
{
Labels.Clear();
Labels.AddRange(labels);
}
public void AddLabel(string label)
{
Labels.Add(label);
}
public void ClearLabels()
{
Labels.Clear();
}
public string GetLabel(int i)
{
if (i < Labels.Count) return Labels[i];
else return string.Empty;
}
}
public class UIAxisLabel
{
/// <summary>
/// 是否显示刻度标签。
/// </summary>
public bool Show { get; set; } = true;
/// <summary>
/// 坐标轴刻度的显示间隔,在类目轴中有效。默认同 axisLabel.interval 一样。
/// 默认会采用标签不重叠的策略间隔显示标签。
/// 可以设置成 0 强制显示所有标签。
/// 如果设置为 1表示『隔一个标签显示一个标签』如果值为 2表示隔两个标签显示一个标签以此类推。
/// </summary>
public int Interval { get; set; } = 0;
public delegate string DoFormatter(double value, int index);
public event DoFormatter Formatter;
public int Angle { get; set; } = 0;
public string GetLabel(double value, int index, UIAxisType axisType = UIAxisType.Value)
{
switch (axisType)
{
case UIAxisType.Value:
return Formatter != null ? Formatter?.Invoke(value, index) : value.ToString("F" + DecimalCount);
case UIAxisType.DateTime:
DateTimeInt64 dt = new DateTimeInt64((long)value);
return Formatter != null ? Formatter?.Invoke(dt, index) : (DateTimeFormat.IsNullOrEmpty() ? dt.ToString() : dt.ToString(DateTimeFormat));
case UIAxisType.Category:
return Formatter != null ? Formatter?.Invoke(value, index) : value.ToString("F0");
}
return value.ToString("F2");
}
public string GetAutoLabel(double value, int decimalCount)
{
return value.ToString("F" + decimalCount);
}
/// <summary>
/// 小数位个数Formatter不为空时以Formatter为准
/// </summary>
public int DecimalCount { get; set; } = 0;
/// <summary>
/// 日期格式化字符串Formatter不为空时以Formatter为准
/// </summary>
public string DateTimeFormat { get; set; } = "HH:mm";
public bool AutoFormat { get; set; } = true;
}
public class UIAxisTick
{
/// <summary>
/// 类目轴中在为 true 的时候有效,可以保证刻度线和标签对齐。
/// </summary>
public bool AlignWithLabel { get; set; }
/// <summary>
/// 是否显示坐标轴刻度。
/// </summary>
public bool Show { get; set; } = true;
/// <summary>
/// 坐标轴刻度的长度。
/// </summary>
public int Length { get; set; } = 5;
/// <summary>
/// 坐标轴刻度的显示间隔,在类目轴中有效。默认同 axisLabel.interval 一样。
/// 默认会采用标签不重叠的策略间隔显示标签。
/// 可以设置成 0 强制显示所有标签。
/// 如果设置为 1表示『隔一个标签显示一个标签』如果值为 2表示隔两个标签显示一个标签以此类推。
/// </summary>
public int Interval { get; set; } = 0;
public int Distance { get; set; } = 0;
}
public class UIBarSeries : IDisposable
{
public UIBarSeries(int decimalPlaces = 0)

View File

@ -34,6 +34,7 @@
* 2022-04-19: V3.1.5 Smooth绘制
* 2022-07-11: V3.2.1 线
* 2022-07-26: V3.2.2 Y轴数据点提示文字显示
* 2022-07-30: V3.2.2 Series.XAxisDecimalPlacesXAxisDateTimeFormatYAxisDecimalPlaces
******************************************************************************/
using System;
@ -457,32 +458,15 @@ namespace Sunny.UI
return;
}
//if (series.Points.Count == 2)
//{
// using (Pen pen = new Pen(color, series.Width))
// {
// g.DrawTwoPoints(pen, series.Points[0], series.Points[1], DrawRect);
// }
//
// return;
//}
if (series.ShowLine || series.Symbol == UILinePointSymbol.None)
{
using (Pen pen = new Pen(color, series.Width))
{
g.SetHighQuality();
//if (series.ContainsNan || !series.Smooth)
//{
for (int i = 0; i < series.Points.Count - 1; i++)
{
g.DrawTwoPoints(pen, series.Points[i], series.Points[i + 1], DrawRect);
}
//}
//else
//{
// g.DrawCurve(pen, series.Points.ToArray());
//}
g.SetDefaultQuality();
}
@ -498,21 +482,6 @@ namespace Sunny.UI
bmp?.Dispose();
bmp = new Bitmap(Width, Height);
//using (Graphics graphics = bmp.Graphics())
//{
// graphics.FillRectangle(FillColor, 0, 0, Width, Height);
//}
//
//using (Graphics graphics = bmpGreater.Graphics())
//{
// graphics.FillRectangle(FillColor, 0, 0, Width, Height);
//}
//
//using (Graphics graphics = bmpLess.Graphics())
//{
// graphics.FillRectangle(FillColor, 0, 0, Width, Height);
//}
int idx = 0;
float wTop = Option.Grid.Top;
float wBottom = Height - Option.Grid.Bottom;
@ -804,13 +773,11 @@ namespace Sunny.UI
if (series.GetNearestPoint(e.Location, 4, out double x, out double y, out int index))
{
UILineSelectPoint point = new UILineSelectPoint();
point.SeriesIndex = series.Index;
point.Name = series.Name;
point.Series = series;
point.Index = index;
point.X = x;
point.Y = y;
point.Location = new Point((int)series.Points[index].X, (int)series.Points[index].Y);
point.IsY2 = series.IsY2;
selectPointsTemp.Add(point);
}
}
@ -822,16 +789,16 @@ namespace Sunny.UI
}
else
{
Dictionary<string, UILineSelectPoint> points = selectPoints.ToDictionary(p => p.Name);
Dictionary<string, UILineSelectPoint> points = selectPoints.ToDictionary(p => p.Series.Name);
foreach (var point in selectPointsTemp)
{
if (!points.ContainsKey(point.Name))
if (!points.ContainsKey(point.Series.Name))
{
isNew = true;
break;
}
if (points[point.Name].Index != point.Index)
if (points[point.Series.Name].Index != point.Index)
{
isNew = true;
break;
@ -844,7 +811,7 @@ namespace Sunny.UI
selectPoints.Clear();
StringBuilder sb = new StringBuilder();
int idx = 0;
Dictionary<int, UILineSelectPoint> dictionary = selectPointsTemp.ToDictionary(p => p.SeriesIndex);
Dictionary<int, UILineSelectPoint> dictionary = selectPointsTemp.ToDictionary(p => p.Series.Index);
List<UILineSelectPoint> points = dictionary.SortedValues();
foreach (var point in points)
{
@ -852,19 +819,22 @@ namespace Sunny.UI
if (idx > 0) sb.Append('\n');
sb.Append(point.Name);
sb.Append(point.Series.Name);
sb.Append('\n');
sb.Append(Option.XAxis.Name + ": ");
if (Option.XAxisType == UIAxisType.DateTime)
sb.Append(new DateTimeInt64(point.X).ToString(Option.XAxis.AxisLabel.DateTimeFormat));
sb.Append(new DateTimeInt64(point.X).ToString(point.Series.XAxisDateTimeFormat.IsValid() ? point.Series.XAxisDateTimeFormat : XScale.Format));
else
sb.Append(point.X.ToString("F" + Option.XAxis.AxisLabel.DecimalCount));
sb.Append(point.X.ToString(point.Series.XAxisDecimalPlaces >= 0 ? "F" + point.Series.XAxisDecimalPlaces : XScale.Format));
sb.Append('\n');
if (point.IsY2)
sb.Append(Option.Y2Axis.Name + ": " + point.Y.ToString("F" + Option.Y2Axis.AxisLabel.DecimalCount));
if (point.Series.IsY2)
sb.Append(Option.Y2Axis.Name + ": " + point.Y.ToString(point.Series.YAxisDecimalPlaces >= 0 ? "F" + point.Series.YAxisDecimalPlaces : Y2Scale.Format));
else
sb.Append(Option.YAxis.Name + ": " + point.Y.ToString("F" + Option.YAxis.AxisLabel.DecimalCount));
sb.Append(Option.YAxis.Name + ": " + point.Y.ToString(point.Series.YAxisDecimalPlaces >= 0 ? "F" + point.Series.YAxisDecimalPlaces : YScale.Format));
idx++;
}

View File

@ -304,6 +304,66 @@ namespace Sunny.UI
public class UILineSeries
{
public UILineSeries(string name, bool isY2 = false)
{
Name = name;
Color = UIColor.Blue;
IsY2 = isY2;
}
public UILineSeries(string name, Color color, bool isY2 = false)
{
Name = name;
Color = color;
CustomColor = true;
IsY2 = isY2;
}
public void SetValueFormat(int xAxisDecimalPlaces, int yAxisDecimalPlaces)
{
XAxisDecimalPlaces = xAxisDecimalPlaces;
YAxisDecimalPlaces = yAxisDecimalPlaces;
}
public void SetValueFormat(string xAxisDateTimeFormat, int yAxisDecimalPlaces)
{
XAxisDateTimeFormat = xAxisDateTimeFormat;
YAxisDecimalPlaces = yAxisDecimalPlaces;
}
private int _xAxisDecimalPlaces = -1;
public int XAxisDecimalPlaces
{
get => _xAxisDecimalPlaces;
set => _xAxisDecimalPlaces = Math.Max(0, value);
}
private int _yAxisDecimalPlaces = -1;
public int YAxisDecimalPlaces
{
get => _yAxisDecimalPlaces;
set => _yAxisDecimalPlaces = Math.Max(0, value);
}
private string _dateTimeFormat = "";
public string XAxisDateTimeFormat
{
get => _dateTimeFormat;
set
{
try
{
DateTime.Now.ToString(value);
_dateTimeFormat = value;
}
catch
{
_dateTimeFormat = "";
}
}
}
public int Index { get; set; }
public string Name { get; private set; }
@ -336,21 +396,6 @@ namespace Sunny.UI
public bool Visible { get; set; } = true;
public UILineSeries(string name, bool isY2 = false)
{
Name = name;
Color = UIColor.Blue;
IsY2 = isY2;
}
public UILineSeries(string name, Color color, bool isY2 = false)
{
Name = name;
Color = color;
CustomColor = true;
IsY2 = isY2;
}
public readonly List<double> XData = new List<double>();
public readonly List<double> YData = new List<double>();
@ -483,9 +528,6 @@ namespace Sunny.UI
public struct UILineSelectPoint
{
public int SeriesIndex { get; set; }
public string Name { get; set; }
public int Index { get; set; }
public double X { get; set; }
@ -494,7 +536,7 @@ namespace Sunny.UI
public Point Location { get; set; }
public bool IsY2 { get; set; }
public UILineSeries Series { get; set; }
}
public class UILineWarningArea

View File

@ -38,6 +38,244 @@ namespace Sunny.UI
public string Formatter { get; set; }
}
public class UIAxis
{
public UIAxis()
{
}
public UIAxis(UIAxisType axisType)
{
Type = axisType;
}
public string Name { get; set; }
public UIAxisType Type { get; }
/// <summary>
/// 坐标轴的分割段数,需要注意的是这个分割段数只是个预估值
/// 最后实际显示的段数会在这个基础上根据分割后坐标轴刻度显示的易读程度作调整。
/// 在类目轴中无效。
/// </summary>
public int SplitNumber { get; set; } = 5;
/// <summary>
/// 是否是脱离 0 值比例。设置成 true 后坐标刻度不会强制包含零刻度。在双数值轴的散点图中比较有用。
/// </summary>
public bool Scale { get; set; }
/// <summary>
/// 坐标轴刻度
/// </summary>
public UIAxisTick AxisTick = new UIAxisTick();
/// <summary>
/// 坐标轴标签
/// </summary>
public UIAxisLabel AxisLabel = new UIAxisLabel();
public bool MaxAuto { get; set; } = true;
public bool MinAuto { get; set; } = true;
public double Max { get; set; } = 100;
public double Min { get; set; } = 0;
public List<string> Data = new List<string>();
public bool ShowGridLine { get; set; } = true;
public void Clear()
{
Data.Clear();
}
public CustomLabels CustomLabels;
public bool HaveCustomLabels
{
get => CustomLabels != null && CustomLabels.Count > 0;
}
public void SetMinValue(double min)
{
Min = min;
MinAuto = false;
}
public void SetMaxValue(double max)
{
Max = max;
MaxAuto = false;
}
public void SetRange(double min, double max)
{
SetMinValue(min);
SetMaxValue(max);
}
public void SetMinValue(DateTime min)
{
Min = new DateTimeInt64(min);
MinAuto = false;
}
public void SetMaxValue(DateTime max)
{
Max = new DateTimeInt64(max);
MaxAuto = false;
}
public void SetRange(DateTime min, DateTime max)
{
SetMinValue(min);
SetMaxValue(max);
}
}
public class CustomLabels
{
public double Start { get; set; }
public double Interval { get; set; }
public int Count { get; set; }
public double IntervalMilliseconds { get; set; }
public UIAxisType AxisType { get; set; }
public List<string> Labels = new List<string>();
public double[] LabelValues()
{
double[] values = new double[Count + 1];
for (int i = 0; i <= Count; i++)
{
if (AxisType == UIAxisType.DateTime)
{
DateTimeInt64 dateTime = new DateTimeInt64(Start);
dateTime.AddMilliseconds(IntervalMilliseconds * i);
values[i] = dateTime.DoubleValue;
}
else
{
values[i] = Start + Interval * i;
}
}
return values;
}
public double Stop
{
get
{
if (AxisType == UIAxisType.DateTime)
{
DateTimeInt64 dateTime = new DateTimeInt64(Start);
dateTime.AddMilliseconds(IntervalMilliseconds * Count);
return dateTime.DoubleValue;
}
else
{
return Start + Interval * Count;
}
}
}
public CustomLabels(double start, double interval, int count)
{
Start = start;
Interval = Math.Abs(interval);
Count = Math.Max(2, count);
AxisType = UIAxisType.Value;
}
public CustomLabels(DateTime start, int intervalMilliseconds, int count)
{
Start = new DateTimeInt64(start);
IntervalMilliseconds = Math.Abs(intervalMilliseconds);
Count = Math.Max(2, count);
AxisType = UIAxisType.DateTime;
}
public void SetLabels(string[] labels)
{
Labels.Clear();
Labels.AddRange(labels);
}
public void AddLabel(string label)
{
Labels.Add(label);
}
public void ClearLabels()
{
Labels.Clear();
}
public string GetLabel(int i)
{
if (i < Labels.Count) return Labels[i];
else return string.Empty;
}
}
public class UIAxisLabel
{
/// <summary>
/// 是否显示刻度标签。
/// </summary>
public bool Show { get; set; } = true;
public int Angle { get; set; } = 0;
/// <summary>
/// 小数位个数Formatter不为空时以Formatter为准
/// </summary>
public int DecimalCount { get; set; } = 0;
/// <summary>
/// 日期格式化字符串Formatter不为空时以Formatter为准
/// </summary>
public string DateTimeFormat { get; set; } = "HH:mm";
public bool AutoFormat { get; set; } = true;
}
public class UIAxisTick
{
/// <summary>
/// 类目轴中在为 true 的时候有效,可以保证刻度线和标签对齐。
/// </summary>
public bool AlignWithLabel { get; set; }
/// <summary>
/// 是否显示坐标轴刻度。
/// </summary>
public bool Show { get; set; } = true;
/// <summary>
/// 坐标轴刻度的长度。
/// </summary>
public int Length { get; set; } = 5;
/// <summary>
/// 坐标轴刻度的显示间隔,在类目轴中有效。默认同 axisLabel.interval 一样。
/// 默认会采用标签不重叠的策略间隔显示标签。
/// 可以设置成 0 强制显示所有标签。
/// 如果设置为 1表示『隔一个标签显示一个标签』如果值为 2表示隔两个标签显示一个标签以此类推。
/// </summary>
public int Interval { get; set; } = 0;
public int Distance { get; set; } = 0;
}
public class UIScaleLine
{
public double Value { get; set; }