using System;
using System.Collections.Generic;
using FastReport.DataVisualization.Charting;
using System.ComponentModel;
using System.Drawing;
using FastReport.Data;
using FastReport.Utils;
using System.Drawing.Design;
namespace FastReport.MSChart
{
///
/// Specifies how the series points are sorted.
///
public enum SortBy
{
///
/// Points are not sorted.
///
None,
///
/// Points are sorted by X value.
///
XValue,
///
/// Points are sorted by Y value.
///
YValue
}
///
/// Specifies the direction in which the series points are sorted.
///
public enum ChartSortOrder
{
///
/// Points are sorted in ascending order.
///
Ascending,
///
/// Points are sorted in descending order.
///
Descending
}
///
/// Specifies how the series points are grouped.
///
public enum GroupBy
{
///
/// Points are not grouped.
///
None,
///
/// Points are grouped by X value.
///
XValue,
///
/// Points are grouped by number specified in the .
///
Number,
///
/// Points are grouped by Years.
///
Years,
///
/// Points are grouped by Months.
///
Months,
///
/// Points are grouped by Weeks.
///
Weeks,
///
/// Points are grouped by Days.
///
Days,
///
/// Points are grouped by Hours.
///
Hours,
///
/// Points are grouped by Minutes.
///
Minutes,
///
/// Points are grouped by Seconds.
///
Seconds,
///
/// Points are grouped by Milliseconds.
///
Milliseconds
}
///
/// Specifies which pie value to explode.
///
public enum PieExplode
{
///
/// Do not explode pie values.
///
None,
///
/// Explode the biggest value.
///
BiggestValue,
///
/// Explode the lowest value.
///
LowestValue,
///
/// Explode the value specified in the property.
///
SpecificValue
}
///
/// Specifies which data points to collect into one point.
///
public enum Collect
{
///
/// Do not collect points.
///
None,
///
/// Show top N points (N value is specified in the
/// property), collect other points into one.
///
TopN,
///
/// Show bottom N points (N value is specified in the
/// property), collect other points into one.
///
BottomN,
///
/// Collect points which have Y value less than specified
/// in the property.
///
LessThan,
///
/// Collect points which have Y value less than percent specified
/// in the property.
///
LessThanPercent,
///
/// Collect points which have Y value greater than specified
/// in the property.
///
GreaterThan,
///
/// Collect points which have Y value greater than percent specified
/// in the property.
///
GreaterThanPercent
}
///
/// Represents a MS Chart series wrapper.
///
///
/// This class provides a data for MS Chart series. The series itself is stored inside the
/// MS Chart and is accessible via the property.
/// You don't need to create an instance of this class directly. Instead, use the
/// method.
///
public class MSChartSeries : Base
{
#region Fields
private string filter;
private SortBy sortBy;
private ChartSortOrder sortOrder;
private GroupBy groupBy;
private float groupInterval;
private TotalType groupFunction;
private Collect collect;
private float collectValue;
private string collectedItemText;
private Color collectedItemColor;
private PieExplode pieExplode;
private string pieExplodeValue;
private string xValue;
private string yValue1;
private string yValue2;
private string yValue3;
private string yValue4;
private string color;
private string label;
private bool autoSeriesForce;
private string autoSeriesColumn;
#endregion
#region Properties
///
/// Gets os sets the data filter expression.
///
///
/// The filter is applied for this series only. You can also use the
/// property to set a filter that will be applied to all
/// series in a chart.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string Filter
{
get { return filter; }
set { filter = value; }
}
///
/// Gets or set the data column or expression for automatically created series.
///
///
/// In order to create auto-series, you need to define one series that will be used as a
/// template for new series, and set up the property.
/// The value of this property will be a name of new series. If there is no series
/// with such name yet, the new series will be added.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string AutoSeriesColumn
{
get { return autoSeriesColumn; }
set { autoSeriesColumn = value; }
}
///
/// Gets or sets the sort method used to sort data points.
///
///
/// You have to specify the property as well. Data points in this series
/// will be sorted according selected sort criteria and order.
///
[Category("Data")]
[DefaultValue(SortBy.None)]
public SortBy SortBy
{
get { return sortBy; }
set { sortBy = value; }
}
///
/// Gets or set Force automatically created series.
///
[Category("Data")]
[DefaultValue(true)]
public bool AutoSeriesForce
{
get { return autoSeriesForce; }
set { autoSeriesForce = value; }
}
///
/// Gets or sets the sort order used to sort data points.
///
///
/// You have to specify the property as well. Data points in this series
/// will be sorted according selected sort criteria and order.
///
[Category("Data")]
[DefaultValue(ChartSortOrder.Ascending)]
public ChartSortOrder SortOrder
{
get { return sortOrder; }
set { sortOrder = value; }
}
///
/// Gets or sets the group method used to group data points.
///
///
/// This property is mainly used when series is filled with data with several identical X values.
/// In this case, you need to set the property to XValue. All identical data points will be
/// grouped into one point, their Y values will be summarized. You can choose the summary function
/// using the property.
///
[Category("Data")]
[DefaultValue(GroupBy.None)]
public GroupBy GroupBy
{
get { return groupBy; }
set { groupBy = value; }
}
///
/// Gets or sets the group interval.
///
///
/// This value is used if property is set to Number.
///
[Category("Data")]
[DefaultValue(1f)]
public float GroupInterval
{
get { return groupInterval; }
set { groupInterval = value; }
}
///
/// Gets or sets the function used to group data points.
///
[Category("Data")]
[DefaultValue(TotalType.Sum)]
public TotalType GroupFunction
{
get { return groupFunction; }
set { groupFunction = value; }
}
///
/// Gets or sets the collect method used to collect several data points into one.
///
///
/// This instrument for data processing allows to collect several series points into one point.
/// The collected point will be displaed using the text specified in the
/// property and color specified in the property.
/// For example, to display top 5 values, set this property to TopN and specify
/// N value (5) in the property.
///
[Category("Data")]
[DefaultValue(Collect.None)]
public Collect Collect
{
get { return collect; }
set { collect = value; }
}
///
/// Gets or sets the collect value used to collect several data points into one.
///
///
/// This property is used if the property is set to any value other than None.
///
[Category("Data")]
[DefaultValue(0f)]
public float CollectValue
{
get { return collectValue; }
set { collectValue = value; }
}
///
/// Gets or sets the text for the collected value.
///
[Category("Data")]
public string CollectedItemText
{
get { return collectedItemText; }
set { collectedItemText = value; }
}
///
/// Gets or sets the color for the collected value.
///
///
/// If this property is set to Transparent (by default), the default palette color
/// will be used to display a collected point.
///
[Category("Data")]
public Color CollectedItemColor
{
get { return collectedItemColor; }
set { collectedItemColor = value; }
}
///
/// Gets or sets the method used to explode values in pie-type series.
///
[Category("Data")]
[DefaultValue(PieExplode.None)]
public PieExplode PieExplode
{
get { return pieExplode; }
set { pieExplode = value; }
}
///
/// Gets or sets the value that must be exploded.
///
///
/// This property is used if property is set
/// to SpecificValue.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string PieExplodeValue
{
get { return pieExplodeValue; }
set { pieExplodeValue = value; }
}
///
/// Gets or sets the data column or expression that returns the X value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string XValue
{
get { return xValue; }
set { xValue = value; }
}
///
/// Gets or sets the data column or expression that returns the first Y value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string YValue1
{
get { return yValue1; }
set { yValue1 = value; }
}
///
/// Gets or sets the data column or expression returns the second Y value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string YValue2
{
get { return yValue2; }
set { yValue2 = value; }
}
///
/// Gets or sets the data column or expression returns the third Y value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string YValue3
{
get { return yValue3; }
set { yValue3 = value; }
}
///
/// Gets or sets the data column or expression returns the fourth Y value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string YValue4
{
get { return yValue4; }
set { yValue4 = value; }
}
///
/// Gets or sets the data column or expression that returns the color of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string Color
{
get { return color; }
set { color = value; }
}
///
/// Gets or sets the data column or expression returns the label value of data point.
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
public string Label
{
get { return label; }
set { label = value; }
}
///
/// Gets a reference to MS Chart Series object.
///
///
/// Use this property to set many options available for the Series object. These options
/// include: visual appearance, labels, marks, value types. Refer to the Microsoft Chart control
/// documentation to learn more.
///
[Category("Appearance")]
public Series SeriesSettings
{
get
{
MSChartObject parent = Parent as MSChartObject;
if (parent == null)
return null;
int index = parent.Series.IndexOf(this);
if (index < parent.Chart.Series.Count)
return parent.Chart.Series[index];
return null;
}
}
///
/// Gets a number of Y value per data point.
///
///
/// Number of Y values depends on series type. Most of series have only one Y value. Financial
/// series such as Stock and Candlestick, use four Y values.
///
[Browsable(false)]
public int YValuesPerPoint
{
get { return SeriesSettings.YValuesPerPoint; }
}
///
/// This property is not relevant to this class.
///
[Browsable(false)]
public new Restrictions Restrictions
{
get { return base.Restrictions; }
set { base.Restrictions = value; }
}
#endregion
#region Private Methods
private object Calc(string expression)
{
if (!String.IsNullOrEmpty(expression))
return Report.Calc(expression);
return null;
}
private void InternalProcessData()
{
object xValue = Calc(XValue);
if (xValue != null)
{
string[] expressions = new string[] { YValue1, YValue2, YValue3, YValue4 };
object[] values = new object[YValuesPerPoint];
for (int i = 0; i < YValuesPerPoint; i++)
{
values[i] = Calc(expressions[i]);
}
DataPoint point = SeriesSettings.Points[SeriesSettings.Points.AddXY(xValue, values)];
// color
object color = Calc(Color);
if (color != null)
{
if (color is System.Drawing.Color)
point.Color = (System.Drawing.Color)color;
else if (color is Int32)
point.Color = System.Drawing.Color.FromArgb(255, System.Drawing.Color.FromArgb((int)color));
else if (color is String)
point.Color = System.Drawing.Color.FromName((string)color);
}
// label
object label = Calc(Label);
if (label != null)
point.Label = label.ToString();
}
}
private void SortData()
{
SeriesSettings.Sort(new DataPointComparer(SortBy, SortOrder));
}
private void CollectValues()
{
double others = 0;
if (Collect == Collect.TopN || Collect == Collect.BottomN)
{
for (int i = (int)CollectValue; i < SeriesSettings.Points.Count;)
{
others += SeriesSettings.Points[i].YValues[0];
SeriesSettings.Points.RemoveAt(i);
}
}
else
{
double totalValue = 0;
if (Collect == Collect.LessThanPercent || Collect == Collect.GreaterThanPercent)
{
foreach (DataPoint point in SeriesSettings.Points)
{
totalValue += point.YValues[0];
}
}
for (int i = 0; i < SeriesSettings.Points.Count;)
{
double value = SeriesSettings.Points[i].YValues[0];
if ((Collect == Collect.LessThan && value < CollectValue) ||
(Collect == Collect.GreaterThan && value > CollectValue) ||
(Collect == Collect.LessThanPercent && value / totalValue * 100 < CollectValue) ||
(Collect == Collect.GreaterThanPercent && value / totalValue * 100 > CollectValue))
{
others += value;
SeriesSettings.Points.RemoveAt(i);
}
else
i++;
}
}
if (others > 0 && !String.IsNullOrEmpty(CollectedItemText))
{
SeriesSettings.Points.AddXY(CollectedItemText, others);
DataPoint point = SeriesSettings.Points[SeriesSettings.Points.Count - 1];
if (CollectedItemColor != System.Drawing.Color.Transparent)
point.Color = CollectedItemColor;
}
}
private void GroupData()
{
Chart chart = (Parent as MSChartObject).Chart;
string function = "";
switch (GroupFunction)
{
case TotalType.Sum:
function = "SUM";
break;
case TotalType.Min:
function = "MIN";
break;
case TotalType.Max:
function = "MAX";
break;
case TotalType.Avg:
function = "AVE";
break;
case TotalType.Count:
function = "COUNT";
break;
}
if (GroupBy == GroupBy.XValue)
chart.DataManipulator.GroupByAxisLabel(function, SeriesSettings);
else
{
IntervalType type = IntervalType.Number;
switch (GroupBy)
{
case GroupBy.Years:
type = IntervalType.Years;
break;
case GroupBy.Months:
type = IntervalType.Months;
break;
case GroupBy.Weeks:
type = IntervalType.Weeks;
break;
case GroupBy.Days:
type = IntervalType.Days;
break;
case GroupBy.Hours:
type = IntervalType.Hours;
break;
case GroupBy.Minutes:
type = IntervalType.Minutes;
break;
case GroupBy.Seconds:
type = IntervalType.Seconds;
break;
case GroupBy.Milliseconds:
type = IntervalType.Milliseconds;
break;
}
chart.DataManipulator.Group(function, GroupInterval, type, SeriesSettings);
}
}
private void ExplodePoint()
{
if (SeriesSettings.Points.Count == 0)
return;
if (PieExplode == PieExplode.SpecificValue)
{
object pieExplodeValue = Calc(PieExplodeValue);
if (pieExplodeValue != null)
{
foreach (DataPoint point in SeriesSettings.Points)
{
if (point.AxisLabel == pieExplodeValue.ToString())
{
point["Exploded"] = "true";
break;
}
}
}
}
else
{
List points = new List();
foreach (DataPoint point in SeriesSettings.Points)
{
points.Add(point);
}
points.Sort(new DataPointComparer(SortBy.YValue, ChartSortOrder.Ascending));
DataPoint explodePoint = null;
if (PieExplode == PieExplode.BiggestValue)
explodePoint = points[points.Count - 1];
else
explodePoint = points[0];
explodePoint["Exploded"] = "true";
}
}
#endregion
#region Public Methods
///
public override void Assign(Base source)
{
base.Assign(source);
MSChartSeries src = source as MSChartSeries;
Filter = src.Filter;
SortOrder = src.SortOrder;
SortBy = src.SortBy;
GroupBy = src.GroupBy;
GroupInterval = src.GroupInterval;
GroupFunction = src.GroupFunction;
Collect = src.Collect;
CollectValue = src.CollectValue;
CollectedItemText = src.CollectedItemText;
CollectedItemColor = src.CollectedItemColor;
PieExplode = src.PieExplode;
PieExplodeValue = src.PieExplodeValue;
AutoSeriesForce = src.AutoSeriesForce;
AutoSeriesColumn = src.AutoSeriesColumn;
XValue = src.XValue;
YValue1 = src.YValue1;
YValue2 = src.YValue2;
YValue3 = src.YValue3;
YValue4 = src.YValue4;
Color = src.Color;
Label = src.Label;
}
///
public override void Serialize(FRWriter writer)
{
MSChartSeries s = writer.DiffObject as MSChartSeries;
base.Serialize(writer);
if (Filter != s.Filter)
writer.WriteStr("Filter", Filter);
if (SortOrder != s.SortOrder)
writer.WriteValue("SortOrder", SortOrder);
if (SortBy != s.SortBy)
writer.WriteValue("SortBy", SortBy);
if (GroupBy != s.GroupBy)
writer.WriteValue("GroupBy", GroupBy);
if (GroupInterval != s.GroupInterval)
writer.WriteFloat("GroupInterval", GroupInterval);
if (GroupFunction != s.GroupFunction)
writer.WriteValue("GroupFunction", GroupFunction);
if (Collect != s.Collect)
writer.WriteValue("Collect", Collect);
if (CollectValue != s.CollectValue)
writer.WriteFloat("CollectValue", CollectValue);
if (CollectedItemText != s.CollectedItemText)
writer.WriteStr("CollectedItemText", CollectedItemText);
if (CollectedItemColor != s.CollectedItemColor)
writer.WriteValue("CollectedItemColor", CollectedItemColor);
if (PieExplode != s.PieExplode)
writer.WriteValue("PieExplode", PieExplode);
if (PieExplodeValue != s.PieExplodeValue)
writer.WriteStr("PieExplodeValue", PieExplodeValue);
if (XValue != s.XValue)
writer.WriteStr("XValue", XValue);
if (YValue1 != s.YValue1)
writer.WriteStr("YValue1", YValue1);
if (YValue2 != s.YValue2)
writer.WriteStr("YValue2", YValue2);
if (YValue3 != s.YValue3)
writer.WriteStr("YValue3", YValue3);
if (YValue4 != s.YValue4)
writer.WriteStr("YValue4", YValue4);
if (Color != s.Color)
writer.WriteStr("Color", Color);
if (Label != s.Label)
writer.WriteStr("Label", Label);
if (!AutoSeriesForce)
writer.WriteBool("AutoSeriesForce", AutoSeriesForce);
if (AutoSeriesColumn != s.AutoSeriesColumn)
writer.WriteStr("AutoSeriesColumn", AutoSeriesColumn);
}
///
public override void Deserialize(FRReader reader)
{
base.Deserialize(reader);
if (reader.HasProperty("GroupByXValue"))
GroupBy = GroupBy.XValue;
}
///
/// Clears all data points in this series.
///
public void ClearValues()
{
SeriesSettings.Points.Clear();
}
///
/// Adds a data point with specified X and Y values.
///
/// X value.
/// Array of Y values.
///
/// Note: number of values in the yValues parameter must be the same as value returned
/// by the property.
///
public void AddValue(object xValue, params object[] yValues)
{
SeriesSettings.Points.AddXY(xValue, yValues);
}
internal void ProcessData()
{
object match = true;
if (!String.IsNullOrEmpty(Filter))
match = Report.Calc(Filter);
if (match is bool && (bool)match == true)
InternalProcessData();
}
internal void FinishData()
{
// sort is required if we group by value, not by axis label
bool sortThenGroup = GroupBy != GroupBy.XValue;
if (!sortThenGroup)
{
if (GroupBy != GroupBy.None)
GroupData();
}
// sort
if (Collect == Collect.TopN)
{
SortBy = SortBy.YValue;
SortOrder = ChartSortOrder.Descending;
}
else if (Collect == Collect.BottomN)
{
SortBy = SortBy.YValue;
SortOrder = ChartSortOrder.Ascending;
}
if (SortBy != SortBy.None)
SortData();
// group
if (sortThenGroup)
{
if (GroupBy != GroupBy.None)
GroupData();
}
// collect topn values
if (Collect != Collect.None)
CollectValues();
// explode values
if (PieExplode != PieExplode.None)
ExplodePoint();
}
internal void CreateDummyData()
{
SeriesSettings.Points.Clear();
SeriesSettings.Points.AddXY("A", 1);
SeriesSettings.Points.AddXY("B", 3);
SeriesSettings.Points.AddXY("C", 2);
SeriesSettings.Points.AddXY("D", 4);
}
///
public override string[] GetExpressions()
{
List expressions = new List();
if (!String.IsNullOrEmpty(PieExplodeValue))
expressions.Add(PieExplodeValue);
if (!String.IsNullOrEmpty(XValue))
expressions.Add(XValue);
if (!String.IsNullOrEmpty(YValue1))
expressions.Add(YValue1);
if (!String.IsNullOrEmpty(YValue2))
expressions.Add(YValue2);
if (!String.IsNullOrEmpty(YValue3))
expressions.Add(YValue3);
if (!String.IsNullOrEmpty(YValue4))
expressions.Add(YValue4);
if (!String.IsNullOrEmpty(Color))
expressions.Add(Color);
if (!String.IsNullOrEmpty(Label))
expressions.Add(Label);
if (!String.IsNullOrEmpty(Filter))
expressions.Add(Filter);
return expressions.ToArray();
}
#endregion
///
/// Creates a new instance of the class with default settings.
///
public MSChartSeries()
{
filter = "";
groupInterval = 1;
collectedItemText = "";
collectedItemColor = System.Drawing.Color.Transparent;
pieExplodeValue = "";
xValue = "";
yValue1 = "";
yValue2 = "";
yValue3 = "";
yValue4 = "";
color = "";
label = "";
autoSeriesForce = true;
BaseName = "Series";
}
}
}