6453 lines
191 KiB
C#
6453 lines
191 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Design;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Windows.Forms;
|
|
using DevComponents.DotNetBar.Charts.Style;
|
|
|
|
namespace DevComponents.DotNetBar.Charts
|
|
{
|
|
/// <summary>
|
|
/// Represents an X/Y oriented chart.
|
|
/// </summary>pi
|
|
public class ChartXy : BaseChart
|
|
{
|
|
#region Private variables
|
|
|
|
private States _States;
|
|
|
|
private ChartAxisX _AxisX;
|
|
private ChartAxisY _AxisY;
|
|
|
|
private ChartAxesXCollection _AncillaryAxesX;
|
|
private ChartAxesYCollection _AncillaryAxesY;
|
|
|
|
private ChartSeriesCollection _ChartSeries;
|
|
|
|
private SeriesType _AutoGenSeriesChartType = SeriesType.Point;
|
|
|
|
private int _BarSpacing;
|
|
private double _BarSpacingRatio = 0.2d;
|
|
private double _BarWidthRatio;
|
|
private object _BarOrigin;
|
|
private Tbool _BarShadingEnabled = Tbool.NotSet;
|
|
private Tbool _BarShowAsHistogram = Tbool.NotSet;
|
|
|
|
private BarFillRange _BarFillRange = BarFillRange.NotSet;
|
|
private BarLabelPosition _BarLabelPosition = BarLabelPosition.NotSet;
|
|
|
|
private DataLabelOverlapMode _DataLabelOverlapMode = DataLabelOverlapMode.NotSet;
|
|
|
|
private DataLabelVisualStyle _DataLabelVisualStyle;
|
|
private EffectiveStyle<DataLabelVisualStyle> _EffectiveDataLabelStyle;
|
|
|
|
private ChartXyVisualStyle _ChartVisualStyle;
|
|
private EffectiveStyle<ChartXyVisualStyle> _EffectiveChartStyle;
|
|
|
|
private ChartSeriesVisualStyle _ChartSeriesVisualStyle;
|
|
private EffectiveStyle<ChartSeriesVisualStyle> _EffectiveChartSeriesStyle;
|
|
|
|
private ChartCrosshair _ChartCrosshair;
|
|
private List<CrosshairPoint> _CrosshairSeriesPoints;
|
|
private CrosshairPoint _NearestCrosshairPoint;
|
|
private bool _CanShowCrosshairLabel;
|
|
|
|
private object _MinValueX;
|
|
private object _MaxValueX;
|
|
|
|
private object _MinValueY;
|
|
private object _MaxValueY;
|
|
|
|
private Size _MinQualitativeSize;
|
|
|
|
private PointMarker _PointMarker;
|
|
|
|
private ChartLineDisplayMode _ChartLineDisplayMode = ChartLineDisplayMode.NotSet;
|
|
private ChartLineAreaDisplayMode _ChartLineAreaDisplayMode = ChartLineAreaDisplayMode.NotSet;
|
|
|
|
private BubbleSizeMode _BubbleSizeMode = BubbleSizeMode.NotSet;
|
|
private BubbleIntensityMode _BubbleIntensityMode = BubbleIntensityMode.NotSet;
|
|
private ConvexHullDisplayMode _ConvexHullDisplayMode = ConvexHullDisplayMode.NotSet;
|
|
private PointLabelDisplayMode _PointLabelDisplayType = PointLabelDisplayMode.NotSet;
|
|
|
|
private StepLines _StepLines = StepLines.NotSet;
|
|
private StepLineMode _StepLineMode = StepLineMode.NotSet;
|
|
|
|
private List<PointLabelGroup> _PointLabels;
|
|
|
|
#endregion
|
|
|
|
public ChartXy(string name)
|
|
: this()
|
|
{
|
|
Name = name;
|
|
}
|
|
|
|
public ChartXy()
|
|
{
|
|
InitDefaultStates();
|
|
|
|
_EffectiveChartStyle = new EffectiveStyle<ChartXyVisualStyle>(this);
|
|
_EffectiveChartSeriesStyle = new EffectiveStyle<ChartSeriesVisualStyle>(this);
|
|
|
|
_EffectiveDataLabelStyle = new EffectiveStyle<DataLabelVisualStyle>(this);
|
|
}
|
|
|
|
#region InitDefaultStates
|
|
|
|
private void InitDefaultStates()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public properties
|
|
|
|
#region AncillaryAxesX
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the collection of Ancillary X Axes (Axes that
|
|
/// can be presented in addition to the default X Axis)
|
|
/// </summary>
|
|
[Category("Axis")]
|
|
[Description("Indicates the collection of Ancillary X Axes (Axes that can be presented in addition to the default X Axis.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartAxesXCollection AncillaryAxesX
|
|
{
|
|
get
|
|
{
|
|
if (_AncillaryAxesX == null)
|
|
{
|
|
_AncillaryAxesX = new ChartAxesXCollection();
|
|
_AncillaryAxesX.CollectionChanged += AncillaryAxesCollectionChanged;
|
|
}
|
|
|
|
return (_AncillaryAxesX);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
if (_AncillaryAxesX != null)
|
|
_AncillaryAxesX.CollectionChanged -= AncillaryAxesCollectionChanged;
|
|
|
|
_AncillaryAxesX = value;
|
|
|
|
if (_AncillaryAxesX != null)
|
|
_AncillaryAxesX.CollectionChanged += AncillaryAxesCollectionChanged;
|
|
}
|
|
}
|
|
|
|
#region AncillaryAxesCollectionChanged
|
|
|
|
private void AncillaryAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
ChartAxesCollection cac = sender as ChartAxesCollection;
|
|
|
|
switch (e.Action)
|
|
{
|
|
case NotifyCollectionChangedAction.Add:
|
|
AddAxisItems(e, cac.AxisOrientation);
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Replace:
|
|
RemoveAxisItems(e);
|
|
AddAxisItems(e, cac.AxisOrientation);
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
|
RemoveAxisItems(e);
|
|
break;
|
|
}
|
|
|
|
InvalidateLayout();
|
|
}
|
|
|
|
#region AddAxisItems
|
|
|
|
private void AddAxisItems(
|
|
NotifyCollectionChangedEventArgs e, AxisOrientation axisOrientation)
|
|
{
|
|
foreach (ChartAxis axis in e.NewItems)
|
|
{
|
|
if (axis.AxisOrientation != axisOrientation)
|
|
{
|
|
if (axisOrientation == AxisOrientation.X)
|
|
throw new Exception("Cannot add Y-Axis element to X-Axis collection.");
|
|
else
|
|
throw new Exception("Cannot add X-Axis element to Y-Axis collection.");
|
|
}
|
|
|
|
axis.Parent = this;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RemoveAxisItems
|
|
|
|
private void RemoveAxisItems(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
foreach (ChartAxis axis in e.OldItems)
|
|
axis.Parent = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region AncillaryAxesY
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the collection of Ancillary Y Axes (Axes that
|
|
/// can be presented in addition to the default Y Axis)
|
|
/// </summary>
|
|
[Category("Axis")]
|
|
[Description("Indicates the collection of Ancillary Y Axes (Axes that can be presented in addition to the default Y Axis.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartAxesYCollection AncillaryAxesY
|
|
{
|
|
get
|
|
{
|
|
if (_AncillaryAxesY == null)
|
|
{
|
|
_AncillaryAxesY = new ChartAxesYCollection();
|
|
_AncillaryAxesY.CollectionChanged += AncillaryAxesCollectionChanged;
|
|
}
|
|
|
|
return (_AncillaryAxesY);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
if (_AncillaryAxesY != null)
|
|
_AncillaryAxesY.CollectionChanged -= AncillaryAxesCollectionChanged;
|
|
|
|
_AncillaryAxesY = value;
|
|
|
|
if (_AncillaryAxesY != null)
|
|
_AncillaryAxesY.CollectionChanged += AncillaryAxesCollectionChanged;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AutoGenSeriesType
|
|
|
|
///<summary>
|
|
/// Gets or sets the default SeriesType assigned to auto-generated Series.
|
|
///</summary>
|
|
[DefaultValue(SeriesType.Point), Category("Behavior")]
|
|
[Description("Indicates the default SeriesType assigned to auto-generated Series.")]
|
|
public SeriesType AutoGenSeriesType
|
|
{
|
|
get { return (_AutoGenSeriesChartType); }
|
|
|
|
set
|
|
{
|
|
if (value != _AutoGenSeriesChartType)
|
|
{
|
|
_AutoGenSeriesChartType = value;
|
|
|
|
OnPropertyChanged("AutoGenSeriesType");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AxisX
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the default, primary X Axis.
|
|
/// </summary>
|
|
[Category("Axis")]
|
|
[Description("Indicates the reference to the default, primary X Axis.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartAxisX AxisX
|
|
{
|
|
get
|
|
{
|
|
if (_AxisX == null)
|
|
{
|
|
_AxisX = new ChartAxisX();
|
|
|
|
_AxisX.Parent = this;
|
|
_AxisX.IsPrimaryAxis = true;
|
|
}
|
|
|
|
return (_AxisX);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
if (_AxisX != null)
|
|
_AxisX.Parent = null;
|
|
|
|
_AxisX = value;
|
|
|
|
if (_AxisX != null)
|
|
{
|
|
_AxisX.Parent = this;
|
|
_AxisX.IsPrimaryAxis = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AxisY
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the default, primary Y Axis.
|
|
/// </summary>
|
|
[Category("Axis")]
|
|
[Description("Indicates the reference to the default, primary Y Axis.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartAxisY AxisY
|
|
{
|
|
get
|
|
{
|
|
if (_AxisY == null)
|
|
{
|
|
_AxisY = new ChartAxisY();
|
|
|
|
_AxisY.Parent = this;
|
|
_AxisY.IsPrimaryAxis = true;
|
|
}
|
|
|
|
return (_AxisY);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
if (_AxisY != null)
|
|
_AxisY.Parent = null;
|
|
|
|
_AxisY = value;
|
|
|
|
if (_AxisY != null)
|
|
{
|
|
_AxisY.Parent = this;
|
|
_AxisY.IsPrimaryAxis = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarFillRange
|
|
|
|
/// <summary>
|
|
/// Gets or sets how series bars are filled by default (either according to
|
|
/// each individual bar range, or the entire series range).
|
|
/// </summary>
|
|
[DefaultValue(BarFillRange.NotSet), Category("Bar Display")]
|
|
[Description("Indicates how series bars are filled by default(either according to each individual bar range, or the entire series range).")]
|
|
public BarFillRange BarFillRange
|
|
{
|
|
get { return (_BarFillRange); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarFillRange)
|
|
{
|
|
_BarFillRange = value;
|
|
|
|
OnPropertyChangedEx("BarFillRange", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarLabelPosition
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default position of bar series labels (default is Center).
|
|
/// </summary>
|
|
[DefaultValue(BarLabelPosition.NotSet), Category("Bar Display")]
|
|
[Description("Indicates the default position of bar series labels (default is Center).")]
|
|
public BarLabelPosition BarLabelPosition
|
|
{
|
|
get { return (_BarLabelPosition); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarLabelPosition)
|
|
{
|
|
_BarLabelPosition = value;
|
|
|
|
OnPropertyChangedEx("BarLabelPosition", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarOrigin
|
|
|
|
/// <summary>
|
|
/// Gets or sets the bar 'origin'. This value is used as the base, or
|
|
/// starting Value, from which each bar originates.
|
|
/// </summary>
|
|
[DefaultValue(null), Category("Bar Display")]
|
|
[Description("Indicates the bar 'origin'. This value is used as the base, or starting Value, from which each bar originates.")]
|
|
[TypeConverter("DevComponents.Charts.Design.PointValueConverter, DevComponents.Charts.Design," +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")]
|
|
public object BarOrigin
|
|
{
|
|
get { return (_BarOrigin); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarOrigin)
|
|
{
|
|
_BarOrigin = value;
|
|
|
|
OnPropertyChangedEx("BarOrigin", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarOverlayEnabled
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether the intra-bar (or bar grouping) overlay
|
|
/// is enabled (bars within a group are positoned overlayed on top of each other).
|
|
/// </summary>
|
|
[DefaultValue(false), Category("Bar Display")]
|
|
[Description("Indicates whether the intra-bar (or bar grouping) overlay is enabled (bars within a group are positoned overlayed on top of each other).")]
|
|
public bool BarOverlayEnabled
|
|
{
|
|
get { return (TestState(States.BarOverlayEnabled)); }
|
|
|
|
set
|
|
{
|
|
if (value != BarOverlayEnabled)
|
|
{
|
|
SetState(States.BarOverlayEnabled, value);
|
|
|
|
OnPropertyChangedEx("BarOverlayEnabled", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarShadingEnabled
|
|
|
|
///<summary>
|
|
/// Gets or sets whether Bar shading is enabled by
|
|
/// default for Horizontal and Vertical Bar series.
|
|
///</summary>
|
|
[DefaultValue(Tbool.NotSet), Category("Bar Display")]
|
|
[Description("Indicates whether Bar shading is enabled by default for Horizontal and Vertical Bar series.")]
|
|
public Tbool BarShadingEnabled
|
|
{
|
|
get { return (_BarShadingEnabled); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarShadingEnabled)
|
|
{
|
|
_BarShadingEnabled = value;
|
|
|
|
OnPropertyChangedEx("BarShadingEnabled", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarShowAsHistogram
|
|
|
|
///<summary>
|
|
/// Gets or sets whether the bars will be shown as a Histogram.
|
|
/// Note that this will only be honored for single series bar displays.
|
|
///</summary>
|
|
[DefaultValue(Tbool.NotSet), Category("Bar Display")]
|
|
[Description("Indicates whether the bars will be shown as a Histogram. Note that this will only be honored for single series bar displays.")]
|
|
public Tbool BarShowAsHistogram
|
|
{
|
|
get { return (_BarShowAsHistogram); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarShowAsHistogram)
|
|
{
|
|
_BarShowAsHistogram = value;
|
|
|
|
OnPropertyChangedEx("BarShowAsHistogram", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarSpacing
|
|
|
|
/// <summary>
|
|
/// Gets or sets the intra-bar spacing (or spacing between bars
|
|
/// within the same group). Value is taken as a fixed pixel size.
|
|
/// Setting BarSpacing to 0 (zero) will disable the use of this
|
|
/// property, and will enable the use of the set BarSpacingRatio value.
|
|
/// </summary>
|
|
[DefaultValue(0), Category("Bar Display")]
|
|
[Description("Indicates the intra-bar spacing (or spacing between bars within the same group). Value is taken as a fixed pixel size. Setting BarSpacing to 0 (zero) will disable the use of this property, and will enable the use of the set BarSpacingRatio value.")]
|
|
public int BarSpacing
|
|
{
|
|
get { return (_BarSpacing); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarSpacing)
|
|
{
|
|
_BarSpacing = value;
|
|
|
|
OnPropertyChangedEx("BarSpacing", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarSpacingRatio
|
|
|
|
/// <summary>
|
|
/// Gets or sets the intra-bar spacing ratio (bar spacing between
|
|
/// multiple series bars associated with the same value. Default is .2).
|
|
/// </summary>
|
|
[DefaultValue(0.2d), Category("Bar Display")]
|
|
[Description("Indicates the intra-bar spacing ratio (bar spacing between multiple series bars associated with the same value. Default is .2).")]
|
|
public double BarSpacingRatio
|
|
{
|
|
get { return (_BarSpacingRatio); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarSpacingRatio)
|
|
{
|
|
if (value < 0)
|
|
throw new ArgumentException("Value must be >= 0");
|
|
|
|
_BarSpacingRatio = value;
|
|
|
|
OnPropertyChangedEx("BarSpacingRatio", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BarWidthRatio
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default ratio of bar width to bar
|
|
/// group spacing (defaults to 1 - bar width matches spacing).
|
|
/// </summary>
|
|
[DefaultValue(0d), Category("Bar Display")]
|
|
[Description("Indicates the default ratio of bar width to bar group spacing (defaults to 1 - bar width matches spacing).")]
|
|
public double BarWidthRatio
|
|
{
|
|
get { return (_BarWidthRatio); }
|
|
|
|
set
|
|
{
|
|
if (value != _BarWidthRatio)
|
|
{
|
|
if (value < 0)
|
|
throw new ArgumentException("Value must be >= 0");
|
|
|
|
_BarWidthRatio = value;
|
|
|
|
OnPropertyChangedEx("BarWidthRatio", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BubbleIntensityMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default mode used to determine series bubble intensities.
|
|
/// </summary>
|
|
[DefaultValue(BubbleIntensityMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default mode used to determine series bubble intensities.")]
|
|
public BubbleIntensityMode BubbleIntensityMode
|
|
{
|
|
get { return (_BubbleIntensityMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _BubbleIntensityMode)
|
|
{
|
|
_BubbleIntensityMode = value;
|
|
|
|
OnPropertyChangedEx("BubbleIntensityMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BubbleSizeMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default mode used to calculate series bubble sizes.
|
|
/// </summary>
|
|
[DefaultValue(BubbleSizeMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default mode used to calculate series bubble sizes.")]
|
|
public BubbleSizeMode BubbleSizeMode
|
|
{
|
|
get { return (_BubbleSizeMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _BubbleSizeMode)
|
|
{
|
|
_BubbleSizeMode = value;
|
|
|
|
OnPropertyChangedEx("BubbleSizeMode", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartLineAreaDisplayMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default Line 'Area' display mode.
|
|
/// </summary>
|
|
[DefaultValue(ChartLineAreaDisplayMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default Line 'Area' display mode.")]
|
|
[Editor("DevComponents.Charts.Design.FlagsEnumUIEditor, DevComponents.Charts.Design, " +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
|
|
public ChartLineAreaDisplayMode ChartLineAreaDisplayMode
|
|
{
|
|
get { return (_ChartLineAreaDisplayMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _ChartLineAreaDisplayMode)
|
|
{
|
|
_ChartLineAreaDisplayMode = value;
|
|
|
|
OnPropertyChangedEx("ChartLineAreaDisplayMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartLineDisplayMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default display mode for SeriesType.Line series.
|
|
/// </summary>
|
|
[DefaultValue(ChartLineDisplayMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default display mode for SeriesType.Line series.")]
|
|
[Editor("DevComponents.Charts.Design.FlagsEnumUIEditor, DevComponents.Charts.Design, " +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
|
|
public ChartLineDisplayMode ChartLineDisplayMode
|
|
{
|
|
get { return (_ChartLineDisplayMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _ChartLineDisplayMode)
|
|
{
|
|
if ((_ChartLineDisplayMode & ChartLineDisplayMode.DisplayUnsorted) !=
|
|
(value & ChartLineDisplayMode.DisplayUnsorted))
|
|
{
|
|
foreach (ChartSeries series in ChartSeries)
|
|
series.ResetSortedPoints();
|
|
}
|
|
|
|
_ChartLineDisplayMode = value;
|
|
|
|
OnPropertyChangedEx("ChartLineDisplayMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartSeries
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the collection of Chart Series
|
|
/// </summary>
|
|
[Category("Data")]
|
|
[Description("Indicates the collection of Chart Series.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartSeriesCollection ChartSeries
|
|
{
|
|
get
|
|
{
|
|
if (_ChartSeries == null)
|
|
{
|
|
_ChartSeries = new ChartSeriesCollection();
|
|
|
|
_ChartSeries.CollectionChanged += ChartSeriesCollectionChanged;
|
|
}
|
|
|
|
return (_ChartSeries);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
BaseSeries = null;
|
|
|
|
if (_ChartSeries != null)
|
|
_ChartSeries.CollectionChanged -= ChartSeriesCollectionChanged;
|
|
|
|
_ChartSeries = value;
|
|
|
|
if (_ChartSeries != null)
|
|
_ChartSeries.CollectionChanged += ChartSeriesCollectionChanged;
|
|
}
|
|
}
|
|
|
|
#region ChartSeriesCollectionChanged
|
|
|
|
void ChartSeriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
BaseSeries = null;
|
|
|
|
switch (e.Action)
|
|
{
|
|
case NotifyCollectionChangedAction.Add:
|
|
foreach (ChartSeries item in e.NewItems)
|
|
item.Parent = this;
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Replace:
|
|
foreach (ChartSeries item in e.OldItems)
|
|
item.Parent = null;
|
|
|
|
foreach (ChartSeries item in e.NewItems)
|
|
item.Parent = this;
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
|
foreach (ChartSeries item in e.OldItems)
|
|
item.Parent = null;
|
|
break;
|
|
}
|
|
|
|
ValidateSeriesCollection();
|
|
|
|
InvalidateLayout();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValidateSeriesCollection
|
|
|
|
private void ValidateSeriesCollection()
|
|
{
|
|
SeriesRangeChanged = true;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
ChartAxis axis = series.AxisX;
|
|
|
|
if (axis != null && axis.IsPrimaryAxis == false)
|
|
{
|
|
if (AncillaryAxesX.Contains(axis) == false)
|
|
{
|
|
throw new Exception("Cannot set the series YAxis. The axis is not primary " +
|
|
"or a member of the chart's AncillaryAxesY collection.");
|
|
}
|
|
}
|
|
|
|
axis = series.AxisY;
|
|
|
|
if (axis != null && axis.IsPrimaryAxis == false)
|
|
{
|
|
if (AncillaryAxesY.Contains(axis) == false)
|
|
{
|
|
throw new Exception("Cannot set the series YAxis. The axis is not primary " +
|
|
"or a member of the chart's AncillaryAxesY collection.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ChartSeriesVisualStyle
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default visual style for each chart series.
|
|
/// </summary>
|
|
[Category("Style")]
|
|
[Description("Indicates the default visual style for each chart series.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartSeriesVisualStyle ChartSeriesVisualStyle
|
|
{
|
|
get
|
|
{
|
|
if (_ChartSeriesVisualStyle == null)
|
|
{
|
|
_ChartSeriesVisualStyle = new ChartSeriesVisualStyle();
|
|
|
|
StyleVisualChangeHandler(null, _ChartSeriesVisualStyle);
|
|
}
|
|
|
|
return (_ChartSeriesVisualStyle);
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_ChartSeriesVisualStyle != value)
|
|
{
|
|
ChartSeriesVisualStyle oldValue = _ChartSeriesVisualStyle;
|
|
|
|
_ChartSeriesVisualStyle = value;
|
|
|
|
OnStyleChanged("ChartSeriesVisualStyle", oldValue, value);
|
|
|
|
if (oldValue != null)
|
|
oldValue.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartVisualStyle
|
|
|
|
/// <summary>
|
|
/// Gets or sets the visual style for the Chart.
|
|
/// </summary>
|
|
[Category("Style")]
|
|
[Description("Indicates the visual style for the Chart.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartXyVisualStyle ChartVisualStyle
|
|
{
|
|
get
|
|
{
|
|
if (_ChartVisualStyle == null)
|
|
{
|
|
_ChartVisualStyle = new ChartXyVisualStyle();
|
|
|
|
StyleVisualChangeHandler(null, _ChartVisualStyle);
|
|
}
|
|
|
|
return (_ChartVisualStyle);
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_ChartVisualStyle != value)
|
|
{
|
|
ChartXyVisualStyle oldValue = _ChartVisualStyle;
|
|
|
|
_ChartVisualStyle = value;
|
|
|
|
OnStyleChanged("ChartVisualStyle", oldValue, value);
|
|
|
|
if (oldValue != null)
|
|
oldValue.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartCrosshair
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the Crosshair element for the chart.
|
|
/// </summary>
|
|
[Category("Appearance")]
|
|
[Description("Indicates a reference to the Crosshair element for the chart.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public ChartCrosshair ChartCrosshair
|
|
{
|
|
get
|
|
{
|
|
if (_ChartCrosshair == null)
|
|
{
|
|
_ChartCrosshair = new ChartCrosshair();
|
|
|
|
_ChartCrosshair.Parent = this;
|
|
_ChartCrosshair.PropertyChanged += ChartCrosshair_PropertyChanged;
|
|
}
|
|
|
|
return (_ChartCrosshair);
|
|
}
|
|
|
|
internal set
|
|
{
|
|
if (_ChartCrosshair != null)
|
|
{
|
|
_ChartCrosshair.Parent = null;
|
|
_ChartCrosshair.PropertyChanged -= ChartCrosshair_PropertyChanged;
|
|
}
|
|
|
|
_ChartCrosshair = value;
|
|
|
|
if (_ChartCrosshair != null)
|
|
{
|
|
_ChartCrosshair.Parent = this;
|
|
_ChartCrosshair.PropertyChanged += ChartCrosshair_PropertyChanged;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region ChartCrosshair_PropertyChanged
|
|
|
|
void ChartCrosshair_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
InvalidateRender();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ConvexHullDisplayMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default ConvexHull display mode.
|
|
/// </summary>
|
|
[DefaultValue(ConvexHullDisplayMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default ConvexHull display mode.")]
|
|
[Editor("DevComponents.Charts.Design.FlagsEnumUIEditor, DevComponents.Charts.Design, " +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
|
|
public ConvexHullDisplayMode ConvexHullDisplayMode
|
|
{
|
|
get { return (_ConvexHullDisplayMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _ConvexHullDisplayMode)
|
|
{
|
|
_ConvexHullDisplayMode = value;
|
|
|
|
OnPropertyChangedEx("ConvexHullDisplayMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DataLabelOverlapMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the mode for resolving overlapping series data labels.
|
|
/// </summary>
|
|
[DefaultValue(DataLabelOverlapMode.NotSet), Category("Display")]
|
|
[Description("Indicates the mode for resolving overlapping series data labels.")]
|
|
public DataLabelOverlapMode DataLabelOverlapMode
|
|
{
|
|
get { return (_DataLabelOverlapMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _DataLabelOverlapMode)
|
|
{
|
|
_DataLabelOverlapMode = value;
|
|
|
|
OnPropertyChangedEx("DataLabelOverlapMode", VisualChangeType.Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DataLabelVisualStyle
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default visual style for the chart DataLabel's.
|
|
/// </summary>
|
|
[Category("Style")]
|
|
[Description("Indicates the default visual style for the chart DataLabel's.")]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
|
|
public DataLabelVisualStyle DataLabelVisualStyle
|
|
{
|
|
get
|
|
{
|
|
if (_DataLabelVisualStyle == null)
|
|
{
|
|
_DataLabelVisualStyle = new DataLabelVisualStyle();
|
|
|
|
StyleVisualChangeHandler(null, _DataLabelVisualStyle);
|
|
}
|
|
|
|
return (_DataLabelVisualStyle);
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_DataLabelVisualStyle != value)
|
|
{
|
|
DataLabelVisualStyle oldValue = _DataLabelVisualStyle;
|
|
|
|
_DataLabelVisualStyle = value;
|
|
|
|
OnStyleChanged("DataLabelVisualStyle", oldValue, value);
|
|
|
|
if (oldValue != null)
|
|
oldValue.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EffectiveChartSeriesStyle
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the ChartSeries effective (cached, composite) style.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public ChartSeriesVisualStyle EffectiveChartSeriesStyle
|
|
{
|
|
get { return (_EffectiveChartSeriesStyle.Style); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EffectiveChartStyle
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the Chart's Effective (cached, composite) style.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public ChartXyVisualStyle EffectiveChartStyle
|
|
{
|
|
get { return (_EffectiveChartStyle.Style); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region EffectiveDataLabelStyle
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the DataLabel Effective (cached, composite) style.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
[Description("Indicates a reference to the DataLabel Effective (cached, composite) style.")]
|
|
public DataLabelVisualStyle EffectiveDataLabelStyle
|
|
{
|
|
get { return (_EffectiveDataLabelStyle.Style); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MaxValueX
|
|
|
|
/// <summary>
|
|
/// Gets the calculated maximum X value (composite value of all associated series).
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object MaxValueX
|
|
{
|
|
get
|
|
{
|
|
UpdateRangeValues();
|
|
|
|
return (_MaxValueX);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MaxValueY
|
|
|
|
/// <summary>
|
|
/// Gets the calculated maximum Y value (composite value of all associated series).
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object MaxValueY
|
|
{
|
|
get
|
|
{
|
|
UpdateRangeValues();
|
|
|
|
return (_MaxValueY);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MinValueX
|
|
|
|
/// <summary>
|
|
/// Gets the calculated minimum X value (composite value of all associated series).
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object MinValueX
|
|
{
|
|
get
|
|
{
|
|
UpdateRangeValues();
|
|
|
|
return (_MinValueX);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MinValueY
|
|
|
|
/// <summary>
|
|
/// Gets the calculated minimum Y value (composite value of all associated series).
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object MinValueY
|
|
{
|
|
get
|
|
{
|
|
UpdateRangeValues();
|
|
|
|
return (_MinValueY);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region NearestCrosshairPoint
|
|
|
|
/// <summary>
|
|
/// Gets the last calculated CrosshairPoint nearest to the mouse cursor.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public CrosshairPoint NearestCrosshairPoint
|
|
{
|
|
get { return (_NearestCrosshairPoint); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PointLabelDisplayMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default display mode for the chart PointLabels.
|
|
/// </summary>
|
|
[DefaultValue(PointLabelDisplayMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default display mode for the chart PointLabels.")]
|
|
[Editor("DevComponents.Charts.Design.FlagsEnumUIEditor, DevComponents.Charts.Design, " +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
|
|
public PointLabelDisplayMode PointLabelDisplayMode
|
|
{
|
|
get { return (_PointLabelDisplayType); }
|
|
|
|
set
|
|
{
|
|
if (value != _PointLabelDisplayType)
|
|
{
|
|
_PointLabelDisplayType = value;
|
|
|
|
InvalidatePointLabels();
|
|
|
|
OnPropertyChangedEx("PointLabelDisplayMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ScrollBounds
|
|
|
|
/// <summary>
|
|
/// Gets the Scrollable bounds of the chart.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public override Rectangle ScrollBounds
|
|
{
|
|
get
|
|
{
|
|
ChartXyVisualStyle xyStyle = EffectiveChartStyle;
|
|
|
|
Rectangle cbounds = ContentBounds;
|
|
|
|
cbounds.X += xyStyle.BorderThickness.Left;
|
|
cbounds.Width -= xyStyle.BorderThickness.Horizontal;
|
|
|
|
cbounds.Y += xyStyle.BorderThickness.Top;
|
|
cbounds.Height -= xyStyle.BorderThickness.Vertical;
|
|
|
|
return (cbounds);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ScrollBoundsEx
|
|
|
|
/// <summary>
|
|
/// Gets the Scrollable, extended bounds of the chart.
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public override Rectangle ScrollBoundsEx
|
|
{
|
|
get
|
|
{
|
|
ChartXyVisualStyle xyStyle = EffectiveChartStyle;
|
|
|
|
Rectangle cbounds = ContentBoundsEx;
|
|
|
|
cbounds.X += xyStyle.BorderThickness.Left;
|
|
cbounds.Width -= xyStyle.BorderThickness.Horizontal;
|
|
|
|
cbounds.Y += xyStyle.BorderThickness.Top;
|
|
cbounds.Height -= xyStyle.BorderThickness.Vertical;
|
|
|
|
return (cbounds);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region StepLines
|
|
|
|
/// <summary>
|
|
/// Gets or sets which 'Step lines' are displayed by default.
|
|
/// </summary>
|
|
[DefaultValue(StepLines.NotSet), Category("Display")]
|
|
[Description("Indicates which 'Step lines' are displayed by default.")]
|
|
public StepLines StepLines
|
|
{
|
|
get { return (_StepLines); }
|
|
|
|
set
|
|
{
|
|
if (value != _StepLines)
|
|
{
|
|
_StepLines = value;
|
|
|
|
OnPropertyChangedEx("StepLines", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region StepLineMode
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default mode (or order of rendered Step Lines)
|
|
/// used to display "Step Lines" in the defined Line series.
|
|
/// </summary>
|
|
[DefaultValue(StepLineMode.NotSet), Category("Display")]
|
|
[Description("Indicates the default mode (or order of rendered Step Lines) used to display 'Step Lines' in the defined Line series.")]
|
|
public StepLineMode StepLineMode
|
|
{
|
|
get { return (_StepLineMode); }
|
|
|
|
set
|
|
{
|
|
if (value != _StepLineMode)
|
|
{
|
|
_StepLineMode = value;
|
|
|
|
OnPropertyChangedEx("StepLineMode", VisualChangeType.Render);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Internal properties
|
|
|
|
#region CrosshairSeriesPoints
|
|
|
|
internal List<CrosshairPoint> CrosshairSeriesPoints
|
|
{
|
|
get { return (_CrosshairSeriesPoints); }
|
|
set { _CrosshairSeriesPoints = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DisplayCrosshair
|
|
|
|
internal bool DisplayCrosshair
|
|
{
|
|
get { return (TestState(States.DisplayCrosshair)); }
|
|
set { SetState(States.DisplayCrosshair, value); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DropShadowDisplayed
|
|
|
|
internal bool DropShadowDisplayed
|
|
{
|
|
get { return (TestState(States.DropShadowDisplayed)); }
|
|
set { SetState(States.DropShadowDisplayed, value); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MinQualitativeSize
|
|
|
|
internal Size MinQualitativeSize
|
|
{
|
|
get { return (_MinQualitativeSize); }
|
|
set { _MinQualitativeSize = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PointLabels
|
|
|
|
internal List<PointLabelGroup> PointLabels
|
|
{
|
|
get { return (_PointLabels); }
|
|
set { _PointLabels = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region MeasureOverride
|
|
|
|
protected override void MeasureOverride(ChartLayoutInfo layoutInfo)
|
|
{
|
|
ContainerVisualStyle cstyle = GetEffectiveContainerStyle();
|
|
ChartXyVisualStyle xystyle = EffectiveChartStyle;
|
|
|
|
BoundsRelative = layoutInfo.LayoutBounds;
|
|
|
|
MinQualitativeSize = Size.Empty;
|
|
|
|
UpdateDataBindings();
|
|
UpdateRangeValues();
|
|
|
|
Rectangle oldFrameBounds = FrameBounds;
|
|
|
|
FrameBounds = GetAdjustedBounds(BoundsRelative, cstyle.Margin);
|
|
FrameBounds = GetAdjustedBounds(FrameBounds, cstyle.Padding);
|
|
FrameBounds = GetAdjustedBounds(FrameBounds, xystyle.Margin);
|
|
|
|
if (FrameBounds != oldFrameBounds)
|
|
SeriesPointCount++;
|
|
|
|
ContentBounds = GetAdjustedBounds(FrameBounds, cstyle.BorderThickness);
|
|
ContentBounds = GetAdjustedBounds(ContentBounds, xystyle.BorderThickness);
|
|
ContentBounds = GetAdjustedBounds(ContentBounds, xystyle.Padding);
|
|
|
|
layoutInfo.LayoutBounds = ContentBounds;
|
|
|
|
foreach (ChartTitle title in Titles)
|
|
{
|
|
if (title.Visible == true)
|
|
title.Measure(layoutInfo);
|
|
}
|
|
|
|
if (Legend.Visible == true && Legend.Placement != Placement.Inside)
|
|
Legend.Measure(layoutInfo);
|
|
|
|
DropShadowDisplayed = (xystyle.DropShadow.Enabled == Tbool.True);
|
|
|
|
MeasureSeries(layoutInfo);
|
|
MeasureXyAxes(layoutInfo);
|
|
|
|
if (DropShadowDisplayed == true)
|
|
layoutInfo.LayoutBounds = GetShadowBounds(layoutInfo.LayoutBounds);
|
|
|
|
if (Legend.Visible == true && Legend.Placement == Placement.Inside)
|
|
{
|
|
Rectangle t = layoutInfo.LayoutBounds;
|
|
|
|
if (VScrollBar.Visible == true)
|
|
{
|
|
t.Width -= (VScrollBar.Width - 1);
|
|
layoutInfo.LayoutBounds = t;
|
|
}
|
|
|
|
if (HScrollBar.Visible == true)
|
|
{
|
|
t.Height -= (HScrollBar.Height - 1);
|
|
layoutInfo.LayoutBounds = t;
|
|
}
|
|
|
|
Legend.Measure(layoutInfo);
|
|
|
|
if (VScrollBar.Visible == true)
|
|
{
|
|
t.Width += VScrollBar.Width;
|
|
layoutInfo.LayoutBounds = t;
|
|
}
|
|
|
|
if (HScrollBar.Visible == true)
|
|
{
|
|
t.Height += HScrollBar.Height;
|
|
layoutInfo.LayoutBounds = t;
|
|
}
|
|
}
|
|
|
|
Rectangle oldContentBoundsEx = ContentBoundsEx;
|
|
|
|
ContentBounds = layoutInfo.LayoutBounds;
|
|
ContentBoundsEx = ContentBounds;
|
|
|
|
Rectangle r = ContentBounds;
|
|
|
|
r.Size = new Size(
|
|
Math.Max(r.Size.Width, Dpi.Width(MinContentSize.Width)),
|
|
Math.Max(r.Size.Height, Dpi.Height(MinContentSize.Height)));
|
|
|
|
if (MinQualitativeSize.Width > r.Width)
|
|
r.Width = MinQualitativeSize.Width;
|
|
|
|
if (MinQualitativeSize.Height > r.Height)
|
|
r.Height = MinQualitativeSize.Height;
|
|
|
|
// Since our vertical scrollbar is reversed, we
|
|
// need to adjust the extended content area accordingly
|
|
|
|
if (r.Height > ContentBounds.Height)
|
|
r.Y -= (r.Height - ContentBounds.Height);
|
|
|
|
ContentBoundsEx = r;
|
|
|
|
if (ContentBoundsEx.Equals(oldContentBoundsEx) == false)
|
|
_PointLabels = null;
|
|
|
|
FinalizeDataBindings();
|
|
}
|
|
|
|
#region MeasureSeries
|
|
|
|
private void MeasureSeries(ChartLayoutInfo layoutInfo)
|
|
{
|
|
ChartSeries[] aseries = new ChartSeries[ChartSeries.Count];
|
|
|
|
ChartSeries.CopyTo(aseries, 0);
|
|
|
|
foreach (ChartSeries series in aseries)
|
|
{
|
|
if (series.Visible == true)
|
|
series.Measure(layoutInfo);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MeasureXyAxes
|
|
|
|
private void MeasureXyAxes(ChartLayoutInfo layoutInfo)
|
|
{
|
|
Rectangle obounds = layoutInfo.LayoutBounds;
|
|
|
|
SetEdgeAxes(AncillaryAxesX, AxisX);
|
|
SetEdgeAxes(AncillaryAxesY, AxisY);
|
|
|
|
MeasureAxes(layoutInfo, AncillaryAxesX, AxisX);
|
|
|
|
Rectangle nbounds = layoutInfo.LayoutBounds;
|
|
|
|
MeasureAxes(layoutInfo, AncillaryAxesY, AxisY);
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
if (layoutInfo.LayoutBounds.Width == nbounds.Width)
|
|
break;
|
|
|
|
nbounds = layoutInfo.LayoutBounds;
|
|
|
|
layoutInfo.LayoutBoundsAdjusted = true;
|
|
layoutInfo.LayoutBounds.Y = obounds.Y;
|
|
layoutInfo.LayoutBounds.Height = obounds.Height;
|
|
|
|
MeasureAxes(layoutInfo, AncillaryAxesX, AxisX);
|
|
|
|
if (layoutInfo.LayoutBounds.Height == nbounds.Height)
|
|
break;
|
|
|
|
layoutInfo.LayoutBounds.X = obounds.X;
|
|
layoutInfo.LayoutBounds.Width = obounds.Width;
|
|
|
|
MeasureAxes(layoutInfo, AncillaryAxesY, AxisY);
|
|
}
|
|
|
|
ValidateAxes();
|
|
}
|
|
|
|
#region MeasureAxes
|
|
|
|
private void MeasureAxes(
|
|
ChartLayoutInfo layoutInfo, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
for (int i = axes.Count - 1; i >= 0; i--)
|
|
{
|
|
ChartAxis ca = axes[i];
|
|
|
|
if (ca != null)
|
|
ca.Measure(layoutInfo);
|
|
}
|
|
|
|
axis.Measure(layoutInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValidateAxes
|
|
|
|
private void ValidateAxes()
|
|
{
|
|
if (ValidateAxes(AncillaryAxesX, AxisX) |
|
|
ValidateAxes(AncillaryAxesY, AxisY))
|
|
{
|
|
InvalidateSeriesPoints();
|
|
}
|
|
}
|
|
|
|
#region ValidateAxes
|
|
|
|
private bool ValidateAxes(ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
int visCount = 0;
|
|
bool altered = false;
|
|
|
|
for (int i = axes.Count - 1; i >= 0; i--)
|
|
{
|
|
ChartAxis ca = axes[i];
|
|
|
|
if (ca.Visible == true)
|
|
{
|
|
visCount++;
|
|
altered |= ca.ValidateAxis();
|
|
}
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
{
|
|
visCount++;
|
|
altered |= axis.ValidateAxis();
|
|
}
|
|
|
|
return (altered || (visCount == 0));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region SetEdgeAxes
|
|
|
|
private void SetEdgeAxes(ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
ChartAxis farEdgeAxis = null;
|
|
ChartAxis nearEdgeAxis = null;
|
|
|
|
for (int i = axes.Count - 1; i >= 0; i--)
|
|
{
|
|
ChartAxis ca = axes[i];
|
|
|
|
if (ca.Visible == true)
|
|
{
|
|
AxisAlignment axisAlignment = ca.GetAxisAlignment();
|
|
|
|
ca.EdgeAxis = false;
|
|
|
|
if (axisAlignment == AxisAlignment.Near)
|
|
nearEdgeAxis = ca;
|
|
|
|
else if (axisAlignment == AxisAlignment.Far)
|
|
farEdgeAxis = ca;
|
|
}
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
{
|
|
AxisAlignment axisAlignment = axis.GetAxisAlignment();
|
|
|
|
if (axisAlignment == AxisAlignment.Near)
|
|
nearEdgeAxis = axis;
|
|
|
|
else if (axisAlignment == AxisAlignment.Far)
|
|
farEdgeAxis = axis;
|
|
}
|
|
|
|
if (nearEdgeAxis != null)
|
|
nearEdgeAxis.EdgeAxis = true;
|
|
|
|
if (farEdgeAxis != null)
|
|
farEdgeAxis.EdgeAxis = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ArrangeOverride
|
|
|
|
protected override void ArrangeOverride(ChartLayoutInfo layoutInfo)
|
|
{
|
|
int vmax = VScrollBar.LargeChange;
|
|
|
|
UpdatePaletteColor();
|
|
|
|
base.ArrangeOverride(layoutInfo);
|
|
|
|
if (VScrollBar.LargeChange != vmax)
|
|
SeriesPointCount++;
|
|
|
|
ArrangeAxes(layoutInfo, AncillaryAxesX, AxisX);
|
|
ArrangeAxes(layoutInfo, AncillaryAxesY, AxisY);
|
|
|
|
ArrangeSeries(layoutInfo);
|
|
|
|
SeriesLayoutCount++;
|
|
}
|
|
|
|
#region UpdatePaletteColor
|
|
|
|
private void UpdatePaletteColor()
|
|
{
|
|
ChartBaseSeriesCollection baseSeries = BaseSeries;
|
|
|
|
for (int i = 0; i < baseSeries.Count; i++)
|
|
{
|
|
BaseSeries series = baseSeries[i];
|
|
|
|
series.DefaultPaletteColor = GetPaletteColor(i);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ArrangeAxes
|
|
|
|
private void ArrangeAxes(
|
|
ChartLayoutInfo layoutInfo, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
ca.Arrange(layoutInfo);
|
|
}
|
|
|
|
axis.Arrange(layoutInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ArrangeSeries
|
|
|
|
private void ArrangeSeries(ChartLayoutInfo layoutInfo)
|
|
{
|
|
if (ChartSeries.Count > 0)
|
|
{
|
|
for (int i = 0; i < ChartSeries.Count; i++)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
series.Arrange(layoutInfo);
|
|
|
|
if (series.IsBarSeries == true)
|
|
{
|
|
ChartAxis axis = (series.IsRotated == false)
|
|
? (series.AxisX ?? AxisX) : (series.AxisY ?? AxisY);
|
|
|
|
for (int j = 0; j < axis.AxisBars.Length; j++)
|
|
axis.AxisBars[j].Reset();
|
|
}
|
|
}
|
|
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
SetBarTotals(i);
|
|
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
SetBarWidth(i);
|
|
|
|
if (SeriesDisplayOrder == SeriesDisplayOrder.Reverse)
|
|
{
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
ArrangeBarSeries(i);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < ChartSeries.Count; i++)
|
|
ArrangeBarSeries(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region SetBarTotals
|
|
|
|
private void SetBarTotals(int i)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
if (series.IsDisplayed == true)
|
|
{
|
|
ChartAxis axis = (series.IsRotated == false)
|
|
? series.AxisX ?? AxisX : series.AxisY ?? AxisY;
|
|
|
|
switch (series.SeriesType)
|
|
{
|
|
case SeriesType.HorizontalBar:
|
|
case SeriesType.VerticalBar:
|
|
axis.AxisBars[(int)AxisBarType.Bar].BarCount++;
|
|
axis.AxisBars[(int)AxisBarType.Bar].BarTotal += series.GetBarWidthRatio(this);
|
|
break;
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
case SeriesType.VerticalHiLoBar:
|
|
axis.AxisBars[(int)AxisBarType.HiLoBar].BarCount++;
|
|
axis.AxisBars[(int)AxisBarType.HiLoBar].BarTotal += series.GetBarWidthRatio(this);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetBarWidth
|
|
|
|
private void SetBarWidth(int i)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
if (series.IsDisplayed == true)
|
|
{
|
|
switch (series.SeriesType)
|
|
{
|
|
case SeriesType.HorizontalBar:
|
|
case SeriesType.VerticalBar:
|
|
SetBarWidthEx(series, (int)AxisBarType.Bar);
|
|
break;
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
case SeriesType.VerticalHiLoBar:
|
|
SetBarWidthEx(series, (int)AxisBarType.HiLoBar);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region SetBarWidthEx
|
|
|
|
private void SetBarWidthEx(ChartSeries series, int barType)
|
|
{
|
|
ChartAxis axis = (series.IsRotated == false)
|
|
? series.AxisX ?? AxisX : series.AxisY ?? AxisY;
|
|
|
|
AxisBar axisBar = axis.AxisBars[barType];
|
|
|
|
double interval = axis.MajorTickmarks.TickmarkLayout.MajorInterval;
|
|
|
|
double dwidth = interval / axis.MajorTickmarks.TickmarkLayout.MajorSpacing;
|
|
double ratio = series.GetBarWidthRatio(this);
|
|
|
|
if (BarOverlayEnabled == true || axisBar.BarCount == 1)
|
|
{
|
|
int width = (int)(dwidth * (ratio / (ratio + 1)));
|
|
|
|
if (ratio > 100)
|
|
{
|
|
ratio = (ratio - 1) / 100;
|
|
width = (int)(dwidth * ratio);
|
|
}
|
|
|
|
width = (width < 0) ? 0 : ((width - 1) | 1);
|
|
|
|
series.BarWidth = (int)(width / axis.MajorTickmarks.TickmarkLayout.GridSpacing);
|
|
|
|
axisBar.BarTotalWidth += series.BarWidth;
|
|
}
|
|
else
|
|
{
|
|
int spacing = BarSpacing;
|
|
|
|
if (BarSpacing == 0)
|
|
{
|
|
double abt = (axisBar.BarTotal + 1) + (axisBar.BarCount - 1) * BarSpacingRatio;
|
|
double sr = BarSpacingRatio / abt;
|
|
|
|
spacing = (int)(Math.Ceiling(dwidth * sr));
|
|
}
|
|
|
|
dwidth -= ((axisBar.BarCount - 1) * spacing);
|
|
|
|
int width = (int)(dwidth * (ratio / (axisBar.BarTotal + 1)));
|
|
|
|
if (ratio > 100)
|
|
{
|
|
ratio = (ratio - 1) / 100;
|
|
width = (int)(dwidth * ratio);
|
|
}
|
|
|
|
width = (int)(width / axis.MajorTickmarks.TickmarkLayout.GridSpacing);
|
|
width = (width < 0) ? 0 : ((width - 1) | 1);
|
|
|
|
series.BarWidth = width;
|
|
|
|
axisBar.BarTotalWidth += (series.BarWidth + spacing);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ArrangeBarSeries
|
|
|
|
private void ArrangeBarSeries(int i)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
if (series.IsDisplayed == true)
|
|
{
|
|
switch (series.SeriesType)
|
|
{
|
|
case SeriesType.HorizontalBar:
|
|
case SeriesType.VerticalBar:
|
|
ArrangeBarSeriesEx(series, (int)AxisBarType.Bar);
|
|
break;
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
case SeriesType.VerticalHiLoBar:
|
|
ArrangeBarSeriesEx(series, (int)AxisBarType.HiLoBar);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region ArrangeBarSeriesEx
|
|
|
|
private void ArrangeBarSeriesEx(ChartSeries series, int barType)
|
|
{
|
|
ChartAxis axis = (series.IsRotated == false)
|
|
? (series.AxisX ?? AxisX) : (series.AxisY ?? AxisY);
|
|
|
|
AxisBar axisBar = axis.AxisBars[barType];
|
|
|
|
if (BarOverlayEnabled == true || axisBar.BarCount == 1)
|
|
{
|
|
series.BarOffset = 0;
|
|
|
|
axisBar.BarOffset = series.BarWidth;
|
|
}
|
|
else
|
|
{
|
|
int spacing = BarSpacing;
|
|
|
|
if (BarSpacing == 0)
|
|
{
|
|
double dwidth = axis.MajorTickmarks.TickmarkLayout.MajorInterval;
|
|
double abt = (axisBar.BarTotal + 1) + (axisBar.BarCount - 1) * BarSpacingRatio;
|
|
|
|
spacing = (int)(Math.Ceiling(dwidth * BarSpacingRatio / abt));
|
|
}
|
|
|
|
if (axisBar.BarOffset == int.MinValue)
|
|
axisBar.BarOffset = (int)((-axisBar.BarTotalWidth + spacing) / 2);
|
|
|
|
series.BarOffset = axisBar.BarOffset + series.BarWidth / 2;
|
|
|
|
axisBar.BarOffset += (series.BarWidth + spacing);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderOverride
|
|
|
|
private bool _LastDisplayCrosshair;
|
|
|
|
protected override void RenderOverride(ChartRenderInfo renderInfo)
|
|
{
|
|
Graphics g = renderInfo.Graphics;
|
|
|
|
ChartXyVisualStyle xystyle = EffectiveChartStyle;
|
|
ContainerVisualStyle cstyle = GetEffectiveContainerStyle();
|
|
|
|
Rectangle scFrameBounds = GetScrollBounds(FrameBounds);
|
|
Rectangle scContentBounds = GetScrollBounds(ContentBounds);
|
|
|
|
Rectangle scFigureBounds = GetFigureBounds(scFrameBounds, cstyle);
|
|
|
|
RenderFrameBackground(g, scFrameBounds, cstyle);
|
|
|
|
cstyle.RenderBackgroundFigure(g, scFigureBounds);
|
|
cstyle.RenderBorder(g, scFrameBounds);
|
|
|
|
Region clip = g.Clip;
|
|
g.SetClip(scContentBounds, CombineMode.Intersect);
|
|
|
|
RenderContentBackground(renderInfo, scContentBounds, xystyle);
|
|
|
|
Rectangle imageBounds = GetXyImageBounds(xystyle);
|
|
|
|
if (xystyle.ImageOverlay != ImageOverlay.Top && xystyle.ImageOverlay != ImageOverlay.Middle)
|
|
xystyle.RenderBackgroundFigure(g, scContentBounds, imageBounds);
|
|
|
|
Point cpt = Point.Empty;
|
|
Rectangle cbounds = Rectangle.Empty;
|
|
|
|
_NearestCrosshairPoint = null;
|
|
|
|
if (DisplayCrosshair == true)
|
|
{
|
|
cpt = ChartControl.PointToClient(Control.MousePosition);
|
|
cbounds = GetCrosshairBounds(cstyle);
|
|
|
|
_CrosshairSeriesPoints = GetCrosshairPoints(cbounds, cpt);
|
|
}
|
|
|
|
UpdatePointLabels(g);
|
|
|
|
RenderGrid(renderInfo, false);
|
|
RenderReferences(renderInfo, false);
|
|
|
|
if (xystyle.ImageOverlay == ImageOverlay.Middle)
|
|
xystyle.RenderBackgroundFigure(g, scContentBounds, imageBounds);
|
|
|
|
RenderChartContent(renderInfo, xystyle);
|
|
|
|
RenderGrid(renderInfo, true);
|
|
RenderReferences(renderInfo, true);
|
|
|
|
RenderPointLabels(g);
|
|
|
|
g.Clip = clip;
|
|
|
|
xystyle.RenderBorder(g, scContentBounds);
|
|
|
|
if (xystyle.ImageOverlay == ImageOverlay.Top)
|
|
xystyle.RenderBackgroundFigure(g, scContentBounds, imageBounds);
|
|
|
|
RenderAxes(renderInfo, AncillaryAxesX, AxisX);
|
|
RenderAxes(renderInfo, AncillaryAxesY, AxisY);
|
|
|
|
RenderScrollbars(renderInfo);
|
|
|
|
if (DropShadowDisplayed == true)
|
|
xystyle.DropShadow.RenderDropShadow(g, scContentBounds, true, true);
|
|
|
|
if (cstyle.DropShadow.Enabled == Tbool.True)
|
|
cstyle.DropShadow.RenderDropShadow(g, scFrameBounds, true, true);
|
|
|
|
if (DisplayCrosshair == true)
|
|
{
|
|
CrosshairVisualStyle chstyle = ChartCrosshair.EffectiveCrosshairStyle;
|
|
|
|
RenderCrosshairLines(g, cbounds, cpt, chstyle);
|
|
base.RenderOverride(renderInfo);
|
|
|
|
if (_CanShowCrosshairLabel == true)
|
|
RenderCrosshairLabels(g, cbounds, cpt, chstyle);
|
|
|
|
_LastDisplayCrosshair = DisplayCrosshair;
|
|
}
|
|
else
|
|
{
|
|
if (_CanShowCrosshairLabel == true)
|
|
{
|
|
if (DisplayCrosshair != _LastDisplayCrosshair)
|
|
RenderCrosshairLabels(g, cbounds, cpt, ChartCrosshair.EffectiveCrosshairStyle);
|
|
}
|
|
|
|
base.RenderOverride(renderInfo);
|
|
}
|
|
}
|
|
|
|
#region RenderContentBackground
|
|
|
|
private void RenderContentBackground(
|
|
ChartRenderInfo renderInfo, Rectangle bounds, ChartXyVisualStyle xyStyle)
|
|
{
|
|
Graphics g = renderInfo.Graphics;
|
|
ChartControl chartControl = ChartControl;
|
|
|
|
if (chartControl.DoPreRenderContentBackgroundEvent(g, this, bounds) == false)
|
|
{
|
|
xyStyle.RenderBackground(g, bounds);
|
|
|
|
RenderAxesBackground(renderInfo, bounds, AncillaryAxesX, AxisX);
|
|
RenderAxesBackground(renderInfo, bounds, AncillaryAxesY, AxisY);
|
|
|
|
chartControl.DoPostRenderContentBackgroundEvent(g, this, bounds);
|
|
}
|
|
}
|
|
|
|
#region RenderAxesBackground
|
|
|
|
private void RenderAxesBackground(ChartRenderInfo renderInfo,
|
|
Rectangle scContentBounds, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
ca.RenderBackground(renderInfo, scContentBounds);
|
|
}
|
|
|
|
axis.RenderBackground(renderInfo, scContentBounds);
|
|
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
ca.RenderStripes(renderInfo, scContentBounds);
|
|
}
|
|
|
|
axis.RenderStripes(renderInfo, scContentBounds);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderChartContent
|
|
|
|
protected virtual void RenderChartContent(
|
|
ChartRenderInfo renderInfo, ChartXyVisualStyle xyStyle)
|
|
{
|
|
Graphics g = renderInfo.Graphics;
|
|
|
|
if (ChartSeries.Count > 0)
|
|
{
|
|
SmoothingMode sm = g.SmoothingMode;
|
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
Point pt = GetLocalAdjustedPoint(Point.Empty);
|
|
|
|
if (pt.IsEmpty == false)
|
|
g.TranslateTransform(pt.X, pt.Y);
|
|
|
|
// Indicators on bottom
|
|
|
|
if (SeriesDisplayOrder == SeriesDisplayOrder.Reverse)
|
|
{
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
RenderIndicators(renderInfo, ChartSeries[i], false);
|
|
}
|
|
else
|
|
{
|
|
foreach (ChartSeries series in ChartSeries)
|
|
RenderIndicators(renderInfo, series, false);
|
|
}
|
|
|
|
// Series data
|
|
|
|
if (SeriesDisplayOrder == SeriesDisplayOrder.Reverse)
|
|
{
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
RenderSeries(renderInfo, ChartSeries[i]);
|
|
}
|
|
else
|
|
{
|
|
foreach (ChartSeries series in ChartSeries)
|
|
RenderSeries(renderInfo, series);
|
|
}
|
|
|
|
// Indicators on top
|
|
|
|
if (SeriesDisplayOrder == SeriesDisplayOrder.Reverse)
|
|
{
|
|
for (int i = ChartSeries.Count - 1; i >= 0; i--)
|
|
RenderIndicators(renderInfo, ChartSeries[i], true);
|
|
}
|
|
else
|
|
{
|
|
foreach (ChartSeries series in ChartSeries)
|
|
RenderIndicators(renderInfo, series, true);
|
|
}
|
|
|
|
if (pt.IsEmpty == false)
|
|
g.ResetTransform();
|
|
|
|
g.SmoothingMode = sm;
|
|
}
|
|
else
|
|
{
|
|
Rectangle scContentBounds = GetScrollBounds(ContentBounds);
|
|
Rectangle scDisplayBounds = GetDisplayBounds(scContentBounds);
|
|
|
|
RenderEmptyText(g, xyStyle, scDisplayBounds);
|
|
}
|
|
}
|
|
|
|
#region RenderIndicators
|
|
|
|
private void RenderIndicators(
|
|
ChartRenderInfo renderInfo, ChartSeries series, bool onTop)
|
|
{
|
|
if (Visible == true)
|
|
{
|
|
if (Legend.ItemCheckAction != ItemCheckAction.ShowItem || series.IsDisplayed == true)
|
|
series.RenderIndicators(renderInfo, onTop);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderSeries
|
|
|
|
private void RenderSeries(ChartRenderInfo renderInfo, ChartSeries series)
|
|
{
|
|
if (Visible == true)
|
|
{
|
|
if (Legend.ItemCheckAction != ItemCheckAction.ShowItem || series.IsDisplayed == true)
|
|
series.Render(renderInfo);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetFigureBounds
|
|
|
|
private Rectangle GetFigureBounds(Rectangle bounds, ContainerVisualStyle cStyle)
|
|
{
|
|
bounds = GetAdjustedBounds(bounds, cStyle.BorderThickness);
|
|
|
|
Rectangle scBounds = GetScrollBounds(bounds);
|
|
|
|
return (scBounds);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetXyImageBounds
|
|
|
|
private Rectangle GetXyImageBounds(ChartXyVisualStyle xyStyle)
|
|
{
|
|
Rectangle scBbounds = GetScrollBounds(ScrollBounds);
|
|
|
|
scBbounds = GetAdjustedBounds(scBbounds, xyStyle.BorderThickness);
|
|
|
|
if (xyStyle.EnableImageScroll != Tbool.False)
|
|
{
|
|
scBbounds = GetScrollBounds(ScrollBoundsEx);
|
|
scBbounds = GetDisplayBounds(scBbounds);
|
|
}
|
|
|
|
if (HScrollBar.Visible == true)
|
|
scBbounds.Height -= HScrollBar.Height;
|
|
|
|
if (VScrollBar.Visible == true)
|
|
scBbounds.Width -= VScrollBar.Width;
|
|
|
|
return (scBbounds);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderGrid
|
|
|
|
private void RenderGrid(ChartRenderInfo renderInfo, bool displayOnTop)
|
|
{
|
|
Rectangle scContentBounds = GetScrollBounds(ContentBounds);
|
|
|
|
if (scContentBounds.Width > 0 && scContentBounds.Height > 0)
|
|
{
|
|
RenderGridLines(renderInfo, displayOnTop, AncillaryAxesX, AxisX);
|
|
RenderGridLines(renderInfo, displayOnTop, AncillaryAxesY, AxisY);
|
|
}
|
|
}
|
|
|
|
#region RenderGridLines
|
|
|
|
private void RenderGridLines(ChartRenderInfo renderInfo,
|
|
bool displayOnTop, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
{
|
|
if (ca.MajorGridLines.DisplayOnTop == displayOnTop)
|
|
ca.MajorGridLines.Render(renderInfo);
|
|
|
|
if (ca.MinorGridLines.DisplayOnTop == displayOnTop)
|
|
ca.MinorGridLines.Render(renderInfo);
|
|
}
|
|
}
|
|
|
|
if (axis.MajorGridLines.DisplayOnTop == displayOnTop)
|
|
axis.MajorGridLines.Render(renderInfo);
|
|
|
|
if (axis.MinorGridLines.DisplayOnTop == displayOnTop)
|
|
axis.MinorGridLines.Render(renderInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderReferences
|
|
|
|
private void RenderReferences(ChartRenderInfo renderInfo, bool displayOnTop)
|
|
{
|
|
Rectangle scContentBounds = GetScrollBounds(ContentBounds);
|
|
|
|
if (scContentBounds.Width > 0 && scContentBounds.Height > 0)
|
|
{
|
|
RenderReferenceLines(renderInfo, displayOnTop, AncillaryAxesX, AxisX);
|
|
RenderReferenceLines(renderInfo, displayOnTop, AncillaryAxesY, AxisY);
|
|
}
|
|
}
|
|
|
|
#region RenderReferenceLines
|
|
|
|
private void RenderReferenceLines(ChartRenderInfo renderInfo,
|
|
bool displayOnTop, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
foreach (ReferenceLine line in ca.ReferenceLines)
|
|
RenderReferenceLine(renderInfo, line, displayOnTop);
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
{
|
|
foreach (ReferenceLine line in axis.ReferenceLines)
|
|
RenderReferenceLine(renderInfo, line, displayOnTop);
|
|
}
|
|
}
|
|
|
|
#region RenderReferenceLine
|
|
|
|
private void RenderReferenceLine(
|
|
ChartRenderInfo renderInfo, ReferenceLine line, bool displayOnTop)
|
|
{
|
|
if (line.Visible == true)
|
|
{
|
|
if (line.ShowCheckBoxInLegend == false || line.CheckedInLegend == true)
|
|
{
|
|
if (line.DisplayLineOnTop == displayOnTop)
|
|
line.RenderLine(renderInfo);
|
|
|
|
if (line.DisplayTextOnTop == displayOnTop)
|
|
line.RenderLineText(renderInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderPointLabels
|
|
|
|
private void RenderPointLabels(Graphics g)
|
|
{
|
|
Point pt = GetLocalAdjustedPoint(Point.Empty);
|
|
|
|
List<PointLabelGroup> pointLabels = UpdatePointLabels(g);
|
|
|
|
if (pointLabels.Count > 0)
|
|
{
|
|
SmoothingMode sm = g.SmoothingMode;
|
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
RenderPointConnector(g, pt, pointLabels);
|
|
RenderPointLabel(g, pt, pointLabels);
|
|
|
|
g.SmoothingMode = sm;
|
|
}
|
|
}
|
|
|
|
#region UpdatePointLabels
|
|
|
|
private List<PointLabelGroup> UpdatePointLabels(Graphics g)
|
|
{
|
|
if (_PointLabels == null)
|
|
{
|
|
List<PointLabelGroup> labelGroups = new List<PointLabelGroup>();
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (series.IsDisplayed == true)
|
|
{
|
|
List<PointLabel> list = series.GetPointLabels(g);
|
|
|
|
if (list != null)
|
|
{
|
|
PointLabelGroup plg = new PointLabelGroup(series, list);
|
|
|
|
labelGroups.Add(plg);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdatePointLabelsEx(g, labelGroups);
|
|
|
|
FlockAlign falign = new FlockAlign(labelGroups);
|
|
|
|
ContainerVisualStyle cstyle = GetEffectiveContainerStyle();
|
|
|
|
Rectangle r = GetAdjustedBounds(ContentBoundsEx, cstyle.BorderThickness);
|
|
|
|
falign.Iterate(r, DataLabelOverlapMode);
|
|
|
|
_PointLabels = labelGroups;
|
|
}
|
|
|
|
return (_PointLabels);
|
|
}
|
|
|
|
#region UpdatePointLabelsEx
|
|
|
|
private void UpdatePointLabelsEx(Graphics g, List<PointLabelGroup> labelGroups)
|
|
{
|
|
ChartControl control = ChartControl;
|
|
|
|
if (control.IsPointLabelUpdateHooked == true)
|
|
{
|
|
for (int i = 0; i < labelGroups.Count; i++)
|
|
{
|
|
PointLabelGroup lg = labelGroups[i];
|
|
List<PointLabel> lps = lg.PointLabels;
|
|
|
|
control.DoPointLabelUpdateEvent(this, lg.ChartSeries, lps);
|
|
|
|
for (int j = 0; j < lps.Count; j++)
|
|
{
|
|
PointLabel pl = lps[j];
|
|
|
|
if (pl.IsFixedSize == false && pl.NeedsMeasured == true)
|
|
pl.LabelSize = lg.ChartSeries.MeasurePointLabel(g, pl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderPointConnector
|
|
|
|
private void RenderPointConnector(
|
|
Graphics g, Point pt, List<PointLabelGroup> labelGroups)
|
|
{
|
|
ChartControl chartControl = ChartControl;
|
|
|
|
foreach (PointLabelGroup lg in labelGroups)
|
|
{
|
|
ChartSeries series = lg.ChartSeries;
|
|
|
|
foreach (PointLabel pl in lg.PointLabels)
|
|
{
|
|
if (pl.Visible == true &&
|
|
pl.SeriesPoint.IsEmpty == false && pl.EdgePoint.IsEmpty == false)
|
|
{
|
|
DataLabelVisualStyle dstyle = series.GetPointLabelVisualStyle(pl);
|
|
ConnectorLineVisualStyle cstyle = dstyle.ConnectorLineStyle;
|
|
|
|
if (dstyle.DrawConnector == Tbool.True && cstyle.LinePattern != LinePattern.None)
|
|
{
|
|
if (pl.Bounds.IsEmpty == false)
|
|
{
|
|
Rectangle r = pl.Bounds;
|
|
r.Offset(pt);
|
|
|
|
Point pt1 = pl.Point;
|
|
pt1.Offset(pt);
|
|
|
|
Point pt2 = pl.EdgePoint;
|
|
pt2.Offset(pt);
|
|
|
|
bool isChpt = IsCrosshairSeriesPoint(null, pt1);
|
|
|
|
if (cstyle.Origin == ConnectorOrigin.Edge)
|
|
{
|
|
int offset = Math.Max(
|
|
pl.SeriesPoint.PointSize.Width,
|
|
pl.SeriesPoint.PointSize.Height) / 2 + 1;
|
|
|
|
double radians = MathHelper.ToRadians(pl.Angle);
|
|
|
|
int x = (int)(Math.Cos(radians) * offset);
|
|
int y = (int)(Math.Sin(radians) * offset);
|
|
|
|
pt1.X += (int)(Math.Cos(radians) * offset);
|
|
pt1.Y += (int)(Math.Sin(radians) * offset);
|
|
}
|
|
|
|
if (r.Contains(pt1) == false)
|
|
{
|
|
if (chartControl.DoPreRenderPointConnectorEvent(g, this, series, pl, isChpt, pt1, pt2) == false)
|
|
{
|
|
using (Pen pen = new Pen(cstyle.LineColor, Dpi.Width(cstyle.LineWidth)))
|
|
{
|
|
if (cstyle.LinePattern != LinePattern.NotSet)
|
|
pen.DashStyle = (DashStyle)cstyle.LinePattern;
|
|
|
|
g.DrawLine(pen, pt1, pt2);
|
|
}
|
|
|
|
chartControl.DoPostRenderPointConnectorEvent(g, this, series, pl, isChpt, pt1, pt2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderPointLabel
|
|
|
|
private void RenderPointLabel(
|
|
Graphics g, Point pt, List<PointLabelGroup> labelGroups)
|
|
{
|
|
ChartControl chartControl = ChartControl;
|
|
|
|
foreach (PointLabelGroup lg in labelGroups)
|
|
{
|
|
ChartSeries series = lg.ChartSeries;
|
|
|
|
foreach (PointLabel pl in lg.PointLabels)
|
|
{
|
|
if (pl.Visible == true && pl.SeriesPoint.IsEmpty == false)
|
|
{
|
|
Rectangle r = pl.Bounds;
|
|
|
|
if (r.IsEmpty == false)
|
|
{
|
|
DataLabelVisualStyle dstyle = series.GetPointLabelVisualStyle(pl);
|
|
|
|
Point pt1 = r.Location;
|
|
pt1.Offset(pt);
|
|
r.Location = pt1;
|
|
|
|
Point pt2 = pl.Point;
|
|
pt2.Offset(pt);
|
|
|
|
bool isChpt = IsCrosshairSeriesPoint(null, pt2);
|
|
|
|
if (chartControl.DoPreRenderPointLabelEvent(g, this, series, pl, isChpt, r) == false)
|
|
{
|
|
using (StringFormat sf = new StringFormat())
|
|
{
|
|
dstyle.GetStringFormatFlags(sf);
|
|
|
|
Color textColor = dstyle.TextColor;
|
|
Color borderColor = dstyle.BorderColor;
|
|
Background background = dstyle.Background;
|
|
LinePattern borderPattern = dstyle.BorderPattern;
|
|
|
|
if (isChpt == true)
|
|
{
|
|
if (dstyle.HighlightTextColor.IsEmpty == false)
|
|
textColor = dstyle.HighlightTextColor;
|
|
|
|
if (dstyle.HighlightBorderColor.IsEmpty == false)
|
|
borderColor = dstyle.HighlightBorderColor;
|
|
|
|
if (dstyle.HighlightBackground.IsEmpty == false)
|
|
background = dstyle.HighlightBackground;
|
|
|
|
if (dstyle.HighlightBorderPattern != LinePattern.NotSet)
|
|
borderPattern = dstyle.HighlightBorderPattern;
|
|
}
|
|
|
|
RenderPointLabelEx(g, lg, pl, r, textColor,
|
|
background, borderColor, dstyle.BorderThickness, borderPattern, dstyle, sf);
|
|
}
|
|
|
|
chartControl.DoPostRenderPointLabelEvent(g, this, series, pl, isChpt, r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#region RenderPointLabelEx
|
|
|
|
private void RenderPointLabelEx(Graphics g, PointLabelGroup lg, PointLabel pl,
|
|
Rectangle r, Color tcolor, Background background, Color bcolor,
|
|
int thickness, LinePattern pattern, DataLabelVisualStyle dstyle, StringFormat sf)
|
|
{
|
|
if (background.IsEmpty == false)
|
|
{
|
|
using (Brush hbr = background.GetBrush(r))
|
|
g.FillRectangle(hbr, r);
|
|
}
|
|
|
|
g.SmoothingMode = SmoothingMode.None;
|
|
|
|
using (Pen hBorderPen = GetBorderPen(bcolor, thickness, pattern))
|
|
{
|
|
if (hBorderPen != null)
|
|
g.DrawRectangle(hBorderPen, r);
|
|
}
|
|
|
|
if (dstyle.DropShadow.Enabled == Tbool.True)
|
|
dstyle.DropShadow.RenderDropShadow(g, r, true, true);
|
|
|
|
RotateDegrees rotateDegrees = lg.ChartSeries.GetRotateDegrees(dstyle);
|
|
float angle = GetRotateAngle(rotateDegrees);
|
|
|
|
r.Size = pl.LabelSize;
|
|
|
|
Point ptc = new Point(
|
|
r.X + pl.Bounds.Width / 2,
|
|
r.Y + pl.Bounds.Height / 2);
|
|
|
|
if (angle != 0)
|
|
{
|
|
g.TranslateTransform(ptc.X, ptc.Y);
|
|
g.RotateTransform(angle);
|
|
|
|
r.X = -((r.Width + dstyle.Padding.Horizontal) / 2);
|
|
r.Y = -((r.Height + dstyle.Padding.Vertical) / 2);
|
|
|
|
if (angle == 90)
|
|
r.Y--;
|
|
|
|
else if (angle == 270)
|
|
r.X--;
|
|
|
|
r.X += dstyle.Padding.Left;
|
|
r.Y += dstyle.Padding.Top;
|
|
}
|
|
else
|
|
{
|
|
if (dstyle.HasBorder == true)
|
|
{
|
|
r.X += dstyle.BorderThickness;
|
|
r.Y += dstyle.BorderThickness;
|
|
}
|
|
|
|
r.X += dstyle.Padding.Left;
|
|
r.Y += dstyle.Padding.Top;
|
|
}
|
|
|
|
using (Brush htbr = new SolidBrush(tcolor))
|
|
g.DrawString(pl.Label, dstyle.Font, htbr, r, sf);
|
|
|
|
if (angle != 0)
|
|
g.ResetTransform();
|
|
}
|
|
|
|
#region GetRotateAngle
|
|
|
|
private float GetRotateAngle(RotateDegrees rotateDegrees)
|
|
{
|
|
switch (rotateDegrees)
|
|
{
|
|
case RotateDegrees.Rotate90:
|
|
return (90);
|
|
|
|
case RotateDegrees.Rotate180:
|
|
return (180);
|
|
|
|
case RotateDegrees.Rotate270:
|
|
return (270);
|
|
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetBorderPen
|
|
|
|
private Pen GetBorderPen(Color color, int thickness, LinePattern pattern)
|
|
{
|
|
Pen pen = null;
|
|
|
|
if (pattern != LinePattern.None && pattern != LinePattern.NotSet &&
|
|
color.IsEmpty == false && thickness > 0)
|
|
{
|
|
pen = new Pen(color, Dpi.Width(thickness));
|
|
|
|
pen.Alignment = PenAlignment.Inset;
|
|
|
|
if (pattern != LinePattern.NotSet)
|
|
pen.DashStyle = (DashStyle)pattern;
|
|
}
|
|
|
|
return (pen);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderAxes
|
|
|
|
private void RenderAxes(ChartRenderInfo renderInfo,
|
|
ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
ca.Render(renderInfo);
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
axis.Render(renderInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshair
|
|
|
|
#region GetCrosshairBounds
|
|
|
|
private Rectangle GetCrosshairBounds(ContainerVisualStyle cstyle)
|
|
{
|
|
ChartCrosshair crosshair = ChartCrosshair;
|
|
|
|
Rectangle bounds = GetScrollBounds(ContentBounds);
|
|
|
|
if (cstyle.BorderThickness.IsEmpty == false)
|
|
{
|
|
bounds.X += Dpi.Width(cstyle.BorderThickness.Left);
|
|
bounds.Width -= Dpi.Width(cstyle.BorderThickness.Horizontal);
|
|
|
|
bounds.Y += Dpi.Height(cstyle.BorderThickness.Top);
|
|
bounds.Height -= Dpi.Height(cstyle.BorderThickness.Vertical);
|
|
}
|
|
|
|
if (HScrollBar.Visible == true)
|
|
bounds.Height -= (HScrollBar.Height + 1);
|
|
|
|
if (VScrollBar.Visible == true)
|
|
bounds.Width -= (VScrollBar.Width + 1);
|
|
|
|
return (bounds);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetCrosshairPoints
|
|
|
|
private List<CrosshairPoint> GetCrosshairPoints(Rectangle bounds, Point pt)
|
|
{
|
|
List<CrosshairPoint> cps;
|
|
|
|
if (ChartCrosshair.AxisOrientation == AxisOrientation.X)
|
|
cps = GetCrosshairPointsX(pt, bounds);
|
|
else
|
|
cps = GetCrosshairPointsY(pt, bounds);
|
|
|
|
_NearestCrosshairPoint = null;
|
|
|
|
if (cps != null && cps.Count > 0)
|
|
{
|
|
if (ChartCrosshair.CrosshairLabelMode == CrosshairLabelMode.NearestSeries)
|
|
_NearestCrosshairPoint = GetNearestPoint(cps, pt, ChartCrosshair.AxisOrientation);
|
|
}
|
|
|
|
return (cps);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshair
|
|
|
|
internal void RenderCrosshairLines(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
if (ChartCrosshair.AxisOrientation == AxisOrientation.X)
|
|
RenderCrosshairX(g, bounds, pt, cstyle);
|
|
else
|
|
RenderCrosshairY(g, bounds, pt, cstyle);
|
|
}
|
|
|
|
#region RenderCrosshairX
|
|
|
|
private void RenderCrosshairX(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
ChartCrosshair crosshair = ChartCrosshair;
|
|
|
|
List<CrosshairPoint> cps = _CrosshairSeriesPoints;
|
|
|
|
if (crosshair.ShowValueXLine == true)
|
|
RenderCrosshairLineX(g, pt, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueXLabels == true)
|
|
RenderCrosshairLabelX(g, pt);
|
|
|
|
if (crosshair.ShowValueYLine == true || crosshair.ShowValueYLabels == true)
|
|
{
|
|
if (crosshair.CrosshairLabelMode == CrosshairLabelMode.NearestSeries)
|
|
{
|
|
CrosshairPoint mcp = _NearestCrosshairPoint;
|
|
|
|
if (mcp != null)
|
|
{
|
|
if (crosshair.ShowValueYLine == true)
|
|
RenderCrosshairLineY(g, mcp.Point, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueYLabels == true)
|
|
RenderCrosshairLabelY(g, mcp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (CrosshairPoint cp in cps)
|
|
{
|
|
if (crosshair.ShowValueYLine == true)
|
|
RenderCrosshairLineY(g, cp.Point, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueYLabels == true)
|
|
RenderCrosshairLabelY(g, cp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#region GetCrosshairPointsX
|
|
|
|
private List<CrosshairPoint> GetCrosshairPointsX(Point pt, Rectangle bounds)
|
|
{
|
|
List<CrosshairPoint> cps = new List<CrosshairPoint>();
|
|
|
|
for (int i = 0; i < ChartSeries.Count; i++)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
if (series.IsDisplayed == true && series.CrosshairEnabled != Tbool.False)
|
|
{
|
|
if (series.SeriesPoints.Count > 0)
|
|
{
|
|
SortedSeriesPoints ssp = series.GetSortedSeriesPoints(this);
|
|
|
|
ChartAxis axis = (series.IsRotated == true)
|
|
? series.AxisY ?? AxisY : series.AxisX ?? AxisX;
|
|
|
|
TickmarkLayout layout = axis.TickmarkLayout;
|
|
|
|
if (layout.Ticks != null && layout.Ticks.Length > 0)
|
|
{
|
|
Size msize = GetCrosshairMarkerSize(series);
|
|
|
|
for (int j = 0; j < ssp.Count; j++)
|
|
SetCrosshairPointX(cps, series, ssp, j, msize, pt, bounds);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (cps);
|
|
}
|
|
|
|
#region SetCrosshairPointX
|
|
|
|
private void SetCrosshairPointX(List<CrosshairPoint> cps, ChartSeries series,
|
|
SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
if (sp.IsEmpty == false)
|
|
{
|
|
int vindex = 0;
|
|
bool descending = false;
|
|
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
switch (series.SeriesType)
|
|
{
|
|
case SeriesType.VerticalDot:
|
|
spt = GetDataPointEx(series, sp, -1);
|
|
spt.Offset(lpt);
|
|
|
|
spt.Y -= (msize.Height * ssp.CountArray[index]);
|
|
spt.Y += (msize.Height / 2);
|
|
break;
|
|
|
|
case SeriesType.HorizontalDot:
|
|
object x1 = GetDataPointValueX(series, sp);
|
|
|
|
spt = GetPointFromValue(series, 0, x1);
|
|
spt.Offset(lpt);
|
|
|
|
spt.X += (msize.Width * ssp.CountArray[index]);
|
|
spt.X -= (msize.Width / 2);
|
|
break;
|
|
|
|
case SeriesType.VerticalBar:
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Offset(lpt);
|
|
|
|
spt.X += series.BarOffset;
|
|
|
|
int yStart = series.GetVBarStart(this, sp) + lpt.Y;
|
|
|
|
descending = (spt.Y > yStart);
|
|
|
|
if (sp.ValueY.Length > 1)
|
|
{
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, 0, spt, msize, pt, bounds, descending);
|
|
|
|
spt.Y = yStart;
|
|
|
|
vindex = 1;
|
|
descending = !descending;
|
|
}
|
|
break;
|
|
|
|
case SeriesType.HorizontalBar:
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Offset(lpt);
|
|
|
|
spt.Y += series.BarOffset;
|
|
|
|
int xStart = series.GetHBarStart(this, sp) + lpt.X;
|
|
|
|
descending = (spt.X < xStart);
|
|
|
|
if (sp.ValueY.Length > 1)
|
|
{
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, 0, spt, msize, pt, bounds, descending);
|
|
|
|
spt.X = xStart;
|
|
|
|
vindex = 1;
|
|
descending = !descending;
|
|
}
|
|
break;
|
|
|
|
case SeriesType.VerticalHiLoBar:
|
|
SetCrosshairPointVHiLoX(cps, series, ssp, index, msize, pt, bounds);
|
|
return;
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
SetCrosshairPointHHiLoX(cps, series, ssp, index, msize, pt, bounds);
|
|
return;
|
|
|
|
case SeriesType.Point:
|
|
for (int i = 0; i < sp.ValueY.Length; i++)
|
|
{
|
|
spt = GetDataPointEx(series, sp, i);
|
|
spt.Offset(lpt);
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, i, spt, msize, pt, bounds, descending);
|
|
}
|
|
return;
|
|
|
|
default:
|
|
spt = GetDataPoint(series, sp, 0);
|
|
break;
|
|
}
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, vindex, spt, msize, pt, bounds, descending);
|
|
}
|
|
}
|
|
|
|
#region SetCrosshairPointVHiLoX
|
|
|
|
private void SetCrosshairPointVHiLoX(List<CrosshairPoint> cps,
|
|
ChartSeries series, SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.X += (series.BarOffset + lpt.X);
|
|
|
|
int[] values = GetVStockValues(series, sp);
|
|
|
|
int v0, v1, v2, v3;
|
|
NormalizeStockValues(values, out v0, out v1, out v2, out v3);
|
|
|
|
if (values[v2] != int.MinValue)
|
|
{
|
|
spt.Y = values[v2] + lpt.Y;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v2, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v3] != int.MinValue)
|
|
{
|
|
spt.Y = values[v3] + lpt.Y;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v3, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v2] == int.MinValue || values[v0] < values[v2])
|
|
{
|
|
spt.Y = values[v0] + lpt.Y;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v0, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v3] == int.MinValue || values[v1] > values[v3])
|
|
{
|
|
spt.Y = values[v1] + lpt.Y;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v1, spt, msize, pt, bounds, true);
|
|
}
|
|
}
|
|
|
|
#region GetVStockValues
|
|
|
|
private int[] GetVStockValues(ChartSeries series, SeriesPoint sp)
|
|
{
|
|
ChartAxis axis = series.AxisY ?? AxisY;
|
|
|
|
int[] values = new int[4];
|
|
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
if (i < sp.ValueY.Length)
|
|
values[i] = GetDataPointY(axis, sp.ValueY[i]);
|
|
else
|
|
values[i] = int.MinValue;
|
|
}
|
|
|
|
return (values);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region SetCrosshairPointHHiLoX
|
|
|
|
private void SetCrosshairPointHHiLoX(List<CrosshairPoint> cps,
|
|
ChartSeries series, SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Y += (series.BarOffset + lpt.Y);
|
|
|
|
int[] values = GetHStockValues(series, sp);
|
|
|
|
int v0, v1, v2, v3;
|
|
NormalizeStockValues(values, out v0, out v1, out v2, out v3);
|
|
|
|
if (values[v2] != int.MinValue)
|
|
{
|
|
spt.X = values[v2] + lpt.X;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v2, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v3] != int.MinValue)
|
|
{
|
|
spt.X = values[v3] + lpt.X;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v3, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v2] == int.MinValue || values[v0] < values[v2])
|
|
{
|
|
spt.X = values[v0] + lpt.X;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v0, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v3] == int.MinValue || values[v1] > values[v3])
|
|
{
|
|
spt.X = values[v1] + lpt.X;
|
|
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, v1, spt, msize, pt, bounds, false);
|
|
}
|
|
}
|
|
|
|
#region GetHStockValues
|
|
|
|
private int[] GetHStockValues(ChartSeries series, SeriesPoint sp)
|
|
{
|
|
ChartAxis axis = series.AxisX ?? AxisX;
|
|
|
|
int[] values = new int[4];
|
|
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
if (i < sp.ValueY.Length)
|
|
values[i] = GetDataPointX(axis, sp.ValueY[i]);
|
|
else
|
|
values[i] = int.MinValue;
|
|
}
|
|
|
|
return (values);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region NormalizeStockValues
|
|
|
|
private void NormalizeStockValues(int[] values,
|
|
out int v0, out int v1, out int v2, out int v3)
|
|
{
|
|
v0 = 0;
|
|
v1 = 1;
|
|
v2 = 2;
|
|
v3 = 3;
|
|
|
|
if (values[1] < values[0])
|
|
{
|
|
v0 = 1;
|
|
v1 = 0;
|
|
}
|
|
|
|
if (values[v3] != int.MinValue && values[v3] < values[v2])
|
|
{
|
|
v2 = 3;
|
|
v3 = 2;
|
|
}
|
|
|
|
if (values[v2] != int.MinValue)
|
|
{
|
|
if (values[v2] < values[v0])
|
|
v0 = v2;
|
|
|
|
if (values[v2] > values[v1])
|
|
v1 = v2;
|
|
}
|
|
|
|
if (values[v3] != int.MinValue)
|
|
{
|
|
if (values[v3] > values[v1])
|
|
v1 = v3;
|
|
|
|
if (values[v3] < values[v0])
|
|
v0 = v3;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AddCrosshairPointX
|
|
|
|
private void AddCrosshairPointX(List<CrosshairPoint> cps, ChartSeries series,
|
|
SortedSeriesPoints ssp, int index, SeriesPoint sp, int vindex, Point spt,
|
|
Size msize, Point pt, Rectangle bounds, bool descending)
|
|
{
|
|
if (spt != Point.Empty && bounds.Contains(spt))
|
|
{
|
|
int dx = ChartCrosshair.PointIntersectMargin;
|
|
|
|
if (ChartCrosshair.PointIntersectMode == PointIntersectMode.Edge)
|
|
{
|
|
if (sp.PointSize.Width > 0)
|
|
dx += (sp.PointSize.Width / 2);
|
|
else
|
|
dx += (msize.Height / 2);
|
|
}
|
|
|
|
if (spt.X >= pt.X - dx && spt.X <= pt.X + dx)
|
|
cps.Add(new CrosshairPoint(series, ssp, index, sp, vindex, spt, descending));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairY
|
|
|
|
private void RenderCrosshairY(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
ChartCrosshair crosshair = ChartCrosshair;
|
|
|
|
List<CrosshairPoint> cps = _CrosshairSeriesPoints;
|
|
|
|
if (crosshair.ShowValueYLine == true)
|
|
RenderCrosshairLineY(g, pt, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueYLabels == true)
|
|
RenderCrosshairLabelY(g, pt);
|
|
|
|
if (crosshair.ShowValueXLine == true || crosshair.ShowValueXLabels == true)
|
|
{
|
|
if (crosshair.CrosshairLabelMode == CrosshairLabelMode.NearestSeries)
|
|
{
|
|
CrosshairPoint mcp = _NearestCrosshairPoint;
|
|
|
|
if (mcp != null)
|
|
{
|
|
if (crosshair.ShowValueXLine == true)
|
|
RenderCrosshairLineX(g, mcp.Point, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueXLabels == true)
|
|
RenderCrosshairLabelX(g, mcp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (CrosshairPoint cp in cps)
|
|
{
|
|
if (crosshair.ShowValueXLine == true)
|
|
RenderCrosshairLineX(g, cp.Point, bounds, cstyle);
|
|
|
|
if (crosshair.ShowValueXLabels == true)
|
|
RenderCrosshairLabelX(g, cp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#region GetCrosshairPointsY
|
|
|
|
private List<CrosshairPoint> GetCrosshairPointsY(Point pt, Rectangle bounds)
|
|
{
|
|
List<CrosshairPoint> cps = new List<CrosshairPoint>();
|
|
|
|
for (int i = 0; i < ChartSeries.Count; i++)
|
|
{
|
|
ChartSeries series = ChartSeries[i];
|
|
|
|
if (series.IsDisplayed == true && series.CrosshairEnabled != Tbool.False)
|
|
{
|
|
if (series.SeriesPoints.Count > 0)
|
|
{
|
|
SortedSeriesPoints ssp = series.GetSortedSeriesPoints(this);
|
|
|
|
ChartAxis axis = series.AxisX ?? AxisX;
|
|
TickmarkLayout layout = axis.TickmarkLayout;
|
|
|
|
if (layout.Ticks != null && layout.Ticks.Length > 0)
|
|
{
|
|
Size msize = GetCrosshairMarkerSize(series);
|
|
|
|
for (int j = 0; j < ssp.Count; j++)
|
|
SetCrosshairPointY(cps, series, ssp, j, msize, pt, bounds);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (cps);
|
|
}
|
|
|
|
#region SetCrosshairPointY
|
|
|
|
private void SetCrosshairPointY(List<CrosshairPoint> cps, ChartSeries series,
|
|
SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
if (sp.IsEmpty == false)
|
|
{
|
|
int vindex = 0;
|
|
bool descending = false;
|
|
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
switch (series.SeriesType)
|
|
{
|
|
case SeriesType.VerticalDot:
|
|
spt = GetDataPointEx(series, sp, -1);
|
|
spt.Offset(lpt);
|
|
|
|
spt.Y -= (msize.Height * ssp.CountArray[index] - msize.Height / 2);
|
|
break;
|
|
|
|
case SeriesType.HorizontalDot:
|
|
object x1 = GetDataPointValueX(series, sp);
|
|
|
|
spt = GetPointFromValue(series, 0, x1);
|
|
spt.Offset(lpt);
|
|
|
|
spt.X += (msize.Width * ssp.CountArray[index] - msize.Width / 2);
|
|
break;
|
|
|
|
case SeriesType.VerticalBar:
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Offset(lpt);
|
|
|
|
spt.X += series.BarOffset;
|
|
|
|
int yStart = series.GetVBarStart(this, sp) + lpt.Y;
|
|
|
|
descending = (spt.Y > yStart);
|
|
|
|
if (sp.ValueY.Length > 1)
|
|
{
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, 0, spt, msize, pt, bounds, descending);
|
|
|
|
spt.Y = yStart;
|
|
|
|
vindex = 1;
|
|
descending = !descending;
|
|
}
|
|
break;
|
|
|
|
case SeriesType.HorizontalBar:
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Offset(lpt);
|
|
|
|
spt.Y += series.BarOffset;
|
|
|
|
int xStart = series.GetHBarStart(this, sp) + lpt.X;
|
|
|
|
descending = (spt.X < xStart);
|
|
|
|
if (sp.ValueY.Length > 1)
|
|
{
|
|
AddCrosshairPointX(cps, series,
|
|
ssp, index, sp, 0, spt, msize, pt, bounds, descending);
|
|
|
|
spt.X = xStart;
|
|
|
|
vindex = 1;
|
|
descending = !descending;
|
|
}
|
|
break;
|
|
|
|
case SeriesType.VerticalHiLoBar:
|
|
SetCrosshairPointVHiLoY(cps, series, ssp, index, msize, pt, bounds);
|
|
return;
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
SetCrosshairPointHHiLoY(cps, series, ssp, index, msize, pt, bounds);
|
|
return;
|
|
|
|
case SeriesType.Point:
|
|
for (int i = 0; i < sp.ValueY.Length; i++)
|
|
{
|
|
spt = GetDataPointEx(series, sp, i);
|
|
spt.Offset(lpt);
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, i, spt, msize, pt, bounds, descending);
|
|
}
|
|
return;
|
|
|
|
default:
|
|
spt = GetDataPoint(series, sp, 0);
|
|
break;
|
|
}
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, vindex, spt, msize, pt, bounds, descending);
|
|
}
|
|
}
|
|
|
|
#region SetCrosshairPointVHiLoY
|
|
|
|
private void SetCrosshairPointVHiLoY(List<CrosshairPoint> cps,
|
|
ChartSeries series, SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.X += (series.BarOffset + lpt.X);
|
|
|
|
int[] values = GetVStockValues(series, sp);
|
|
|
|
int v0, v1, v2, v3;
|
|
NormalizeStockValues(values, out v0, out v1, out v2, out v3);
|
|
|
|
if (values[v2] != int.MinValue)
|
|
{
|
|
spt.Y = values[v2] + lpt.Y;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v2, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v3] != int.MinValue)
|
|
{
|
|
spt.Y = values[v3] + lpt.Y;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v3, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v2] == int.MinValue || values[v0] < values[v2])
|
|
{
|
|
spt.Y = values[v0] + lpt.Y;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v0, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v3] == int.MinValue || values[v1] > values[v3])
|
|
{
|
|
spt.Y = values[v1] + lpt.Y;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v1, spt, msize, pt, bounds, true);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetCrosshairPointHHiLoY
|
|
|
|
private void SetCrosshairPointHHiLoY(List<CrosshairPoint> cps,
|
|
ChartSeries series, SortedSeriesPoints ssp, int index, Size msize, Point pt, Rectangle bounds)
|
|
{
|
|
Point spt = Point.Empty;
|
|
Point lpt = GetLocalAdjustedPoint(spt);
|
|
|
|
SeriesPoint sp = ssp[index];
|
|
|
|
spt = GetDataPointEx(series, sp, 0);
|
|
spt.Y += (series.BarOffset + lpt.Y);
|
|
|
|
int[] values = GetHStockValues(series, sp);
|
|
|
|
int v0, v1, v2, v3;
|
|
NormalizeStockValues(values, out v0, out v1, out v2, out v3);
|
|
|
|
if (values[v2] != int.MinValue)
|
|
{
|
|
spt.X = values[v2] + lpt.X;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v2, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v3] != int.MinValue)
|
|
{
|
|
spt.X = values[v3] + lpt.X;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v3, spt, msize, pt, bounds, false);
|
|
}
|
|
|
|
if (values[v2] == int.MinValue || values[v0] < values[v2])
|
|
{
|
|
spt.X = values[v0] + lpt.X;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v0, spt, msize, pt, bounds, true);
|
|
}
|
|
|
|
if (values[v3] == int.MinValue || values[v1] > values[v3])
|
|
{
|
|
spt.X = values[v1] + lpt.X;
|
|
|
|
AddCrosshairPointY(cps, series,
|
|
ssp, index, sp, v1, spt, msize, pt, bounds, false);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AddCrosshairPointY
|
|
|
|
private void AddCrosshairPointY(List<CrosshairPoint> cps, ChartSeries series,
|
|
SortedSeriesPoints ssp, int index, SeriesPoint sp, int vindex, Point spt,
|
|
Size msize, Point pt, Rectangle bounds, bool descending)
|
|
{
|
|
if (spt != Point.Empty && bounds.Contains(spt))
|
|
{
|
|
int dy = ChartCrosshair.PointIntersectMargin;
|
|
|
|
if (ChartCrosshair.PointIntersectMode == PointIntersectMode.Edge)
|
|
{
|
|
if (sp.PointSize.Height > 0)
|
|
dy += (sp.PointSize.Height / 2);
|
|
else
|
|
dy += (msize.Height / 2);
|
|
}
|
|
|
|
if (spt.Y >= pt.Y - dy && spt.Y <= pt.Y + dy)
|
|
cps.Add(new CrosshairPoint(series, ssp, index, sp, vindex, spt, descending));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetCrosshairMarkerSize
|
|
|
|
private Size GetCrosshairMarkerSize(ChartSeries series)
|
|
{
|
|
Size msize = Size.Empty;
|
|
|
|
if (series.SeriesType == SeriesType.VerticalDot ||
|
|
series.SeriesType == SeriesType.HorizontalDot)
|
|
{
|
|
msize = GetMaxDotPlotMarkerSize();
|
|
}
|
|
else if (series.IsBarSeries == true)
|
|
{
|
|
msize = new Size(series.BarWidth, Dpi.Height5);
|
|
}
|
|
else
|
|
{
|
|
Image marker = series.PointMarkerImage;
|
|
|
|
if (marker != null)
|
|
msize = marker.Size;
|
|
}
|
|
|
|
return (msize);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLineX
|
|
|
|
private void RenderCrosshairLineX(Graphics g,
|
|
Point pt, Rectangle bounds, CrosshairVisualStyle cstyle)
|
|
{
|
|
Point pt1 = new Point(pt.X, bounds.Y);
|
|
Point pt2 = new Point(pt.X, bounds.Bottom - 1);
|
|
|
|
ChartLineVisualStyle lstyle = cstyle.ValueXLineStyle;
|
|
|
|
using (Pen pen = new Pen(lstyle.LineColor, Dpi.Width(lstyle.LineWidth)))
|
|
{
|
|
pen.DashStyle = (DashStyle)lstyle.LinePattern;
|
|
|
|
g.DrawLine(pen, pt1, pt2);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLineY
|
|
|
|
private void RenderCrosshairLineY(Graphics g,
|
|
Point pt, Rectangle bounds, CrosshairVisualStyle cstyle)
|
|
{
|
|
Point pt1 = new Point(bounds.X, pt.Y);
|
|
Point pt2 = new Point(bounds.Right - 1, pt.Y);
|
|
|
|
ChartLineVisualStyle lstyle = cstyle.ValueYLineStyle;
|
|
|
|
using (Pen pen = new Pen(lstyle.LineColor, Dpi.Height(lstyle.LineWidth)))
|
|
{
|
|
pen.DashStyle = (DashStyle)lstyle.LinePattern;
|
|
|
|
g.DrawLine(pen, pt1, pt2);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabelX
|
|
|
|
private void RenderCrosshairLabelX(Graphics g, Point pt)
|
|
{
|
|
RenderAxesCrosshairLabelXy(g, pt, AncillaryAxesX, AxisX);
|
|
}
|
|
|
|
private void RenderCrosshairLabelX(Graphics g, CrosshairPoint cp)
|
|
{
|
|
ChartAxis axis = cp.ChartSeries.AxisX ?? AxisX;
|
|
|
|
if (axis != null)
|
|
axis.RenderCrosshairLabel(g, cp);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabelY
|
|
|
|
private void RenderCrosshairLabelY(Graphics g, Point pt)
|
|
{
|
|
RenderAxesCrosshairLabelXy(g, pt, AncillaryAxesY, AxisY);
|
|
}
|
|
|
|
private void RenderCrosshairLabelY(Graphics g, CrosshairPoint cp)
|
|
{
|
|
ChartAxis axis = cp.ChartSeries.AxisY ?? AxisY;
|
|
|
|
if (axis != null)
|
|
axis.RenderCrosshairLabel(g, cp);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderAxesCrosshairLabelXy
|
|
|
|
private void RenderAxesCrosshairLabelXy(Graphics g,
|
|
Point pt, ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
ca.RenderCrosshairLabel(g, pt);
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
axis.RenderCrosshairLabel(g, pt);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetNearestPoint
|
|
|
|
private CrosshairPoint GetNearestPoint(
|
|
List<CrosshairPoint> cps, Point pt, AxisOrientation orientation)
|
|
{
|
|
CrosshairPoint minPoint = null;
|
|
|
|
int minValue = int.MaxValue;
|
|
int minValue2 = int.MaxValue;
|
|
|
|
bool comp = (orientation == AxisOrientation.Y);
|
|
|
|
foreach (CrosshairPoint cp in cps)
|
|
{
|
|
Point cpt = cp.Point;
|
|
|
|
int dv = Math.Abs((comp == false) ? (cpt.Y - pt.Y) : (cpt.X - pt.X));
|
|
int dv2 = Math.Abs((comp == true) ? (cpt.Y - pt.Y) : (cpt.X - pt.X));
|
|
|
|
if (dv < minValue)
|
|
{
|
|
minValue = dv;
|
|
minValue2 = dv2;
|
|
|
|
minPoint = cp;
|
|
}
|
|
else if (dv == minValue)
|
|
{
|
|
if (dv2 < minValue2)
|
|
{
|
|
minValue2 = dv2;
|
|
minPoint = cp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (minPoint);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetFirstValue
|
|
|
|
private object GetFirstValue(TickmarkLayout layout)
|
|
{
|
|
for (int i = 0; i < layout.Ticks.Length; i++)
|
|
{
|
|
TickmarkTick tick = layout.Ticks[i];
|
|
|
|
if (tick.LabelIndex >= 0)
|
|
return (tick.Value);
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetLastValue
|
|
|
|
private object GetLastValue(TickmarkLayout layout)
|
|
{
|
|
for (int i = layout.Ticks.Length - 1; i >= 0; i--)
|
|
{
|
|
TickmarkTick tick = layout.Ticks[i];
|
|
|
|
if (tick.LabelIndex >= 0)
|
|
return (tick.Value);
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabels
|
|
|
|
#region RenderCrosshairLabels
|
|
|
|
private void RenderCrosshairLabels(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
if (ChartCrosshair.ShowCrosshairLabels == true)
|
|
{
|
|
if (ChartControl.DoRenderCrosshairLabelEvent(g, this, _CrosshairSeriesPoints, bounds, pt) == false)
|
|
{
|
|
if (_CrosshairSeriesPoints != null && _CrosshairSeriesPoints.Count > 0)
|
|
{
|
|
switch (ChartCrosshair.CrosshairLabelMode)
|
|
{
|
|
case CrosshairLabelMode.Common:
|
|
RenderCrosshairLabelCommon(g, bounds, pt, cstyle);
|
|
break;
|
|
|
|
case CrosshairLabelMode.EachSeries:
|
|
RenderCrosshairLabelEach(g, bounds, pt, cstyle);
|
|
break;
|
|
|
|
case CrosshairLabelMode.NearestSeries:
|
|
if (_NearestCrosshairPoint != null)
|
|
RenderCrosshairLabel(g, bounds, cstyle, _NearestCrosshairPoint);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#region RenderCrosshairLabelCommon
|
|
|
|
private void RenderCrosshairLabelCommon(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
Hashtable gps = GetCrosshairLabelGroups(_CrosshairSeriesPoints);
|
|
|
|
MeasureCrosshairLabelGroups(g, gps, bounds, cstyle);
|
|
|
|
int markerWidth;
|
|
Rectangle cbounds = GetCrosshairCommonBounds(gps, bounds, pt, cstyle, out markerWidth);
|
|
|
|
cbounds = GetAdjustedBounds(cbounds, cstyle.Margin);
|
|
|
|
if (ChartControl.DoRenderCrosshairCalloutEvent(g, this, cbounds, pt, cstyle) == false)
|
|
DrawRoundRectangle(g, cbounds, cstyle);
|
|
|
|
cbounds = GetAdjustedBounds(cbounds, cstyle.Padding);
|
|
|
|
RenderCrosshairLabelGroups(g, gps, cbounds, cstyle);
|
|
}
|
|
|
|
#region GetCrosshairLabelGroups
|
|
|
|
private Hashtable GetCrosshairLabelGroups(List<CrosshairPoint> cps)
|
|
{
|
|
Hashtable gps = new Hashtable();
|
|
|
|
foreach (CrosshairPoint cp in cps)
|
|
{
|
|
ChartAxis axis;
|
|
|
|
if (ChartCrosshair.AxisOrientation == AxisOrientation.X)
|
|
axis = cp.ChartSeries.AxisX ?? AxisX;
|
|
else
|
|
axis = cp.ChartSeries.AxisY ?? AxisY;
|
|
|
|
CrosshairGroup group = gps[axis] as CrosshairGroup;
|
|
|
|
if (group == null)
|
|
{
|
|
group = new CrosshairGroup();
|
|
|
|
group.Cps = new List<CrosshairPoint>();
|
|
group.Text = GetCrosshairHeader(cp, cps);
|
|
|
|
gps[axis] = group;
|
|
}
|
|
|
|
group.Cps.Add(cp);
|
|
}
|
|
|
|
return (gps);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MeasureCrosshairLabelGroups
|
|
|
|
private void MeasureCrosshairLabelGroups(Graphics g,
|
|
Hashtable gps, Rectangle bounds, CrosshairVisualStyle cstyle)
|
|
{
|
|
bool groupHeader = ChartCrosshair.ShowGroupHeaders;
|
|
|
|
foreach (DictionaryEntry pair in gps)
|
|
{
|
|
CrosshairGroup group = (CrosshairGroup)pair.Value;
|
|
|
|
if (groupHeader == true)
|
|
MeasureCrosshairGroupHeader(g, group, bounds, cstyle);
|
|
|
|
MeasureCrosshairCommonLabels(g, group, bounds, cstyle, groupHeader ? null : group.Text);
|
|
}
|
|
}
|
|
|
|
#region MeasureCrosshairGroupHeader
|
|
|
|
private void MeasureCrosshairGroupHeader(Graphics g,
|
|
CrosshairGroup group, Rectangle bounds, CrosshairVisualStyle cstyle)
|
|
{
|
|
if (string.IsNullOrEmpty(group.Text) == false)
|
|
{
|
|
SizeF sizef = g.MeasureString(group.Text, cstyle.GroupHeaderFont, bounds.Width);
|
|
|
|
group.TextSize = new Size((int)sizef.Width + 1, (int)sizef.Height + 1);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MeasureCrosshairCommonLabels
|
|
|
|
private void MeasureCrosshairCommonLabels(Graphics g,
|
|
CrosshairGroup group, Rectangle bounds, CrosshairVisualStyle cstyle, string header)
|
|
{
|
|
group.LabelSize = Size.Empty;
|
|
group.MarkerWidth = 0;
|
|
|
|
foreach (CrosshairPoint cp in group.Cps)
|
|
{
|
|
Size markerSize = Size.Empty;
|
|
|
|
Image marker = cp.ChartSeries.PointMarkerImage;
|
|
|
|
if (marker != null)
|
|
{
|
|
markerSize = marker.Size;
|
|
|
|
if (marker.Width > group.MarkerWidth)
|
|
group.MarkerWidth = marker.Width;
|
|
}
|
|
|
|
MeasureCrosshairLabel(g, bounds, cp, cstyle, header, false);
|
|
|
|
if (cp.TextSize.IsEmpty == false)
|
|
{
|
|
group.LabelSize.Height += Math.Max(markerSize.Height, cp.TextSize.Height);
|
|
|
|
if (markerSize.Width + cp.TextSize.Width > group.LabelSize.Width)
|
|
group.LabelSize.Width = markerSize.Width + cp.TextSize.Width;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetCrosshairCommonBounds
|
|
|
|
private Rectangle GetCrosshairCommonBounds(Hashtable gps,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle, out int markerWidth)
|
|
{
|
|
markerWidth = 0;
|
|
|
|
Size totalSize = Size.Empty;
|
|
|
|
foreach (DictionaryEntry pair in gps)
|
|
{
|
|
CrosshairGroup group = (CrosshairGroup)pair.Value;
|
|
|
|
totalSize.Width = Math.Max(totalSize.Width, group.TextSize.Width);
|
|
totalSize.Height += (group.TextSize.Height + Dpi.Width4);
|
|
|
|
foreach (CrosshairPoint cp in group.Cps)
|
|
{
|
|
Size markerSize = Size.Empty;
|
|
|
|
Image marker = cp.ChartSeries.PointMarkerImage;
|
|
|
|
if (marker != null)
|
|
markerSize = Dpi.Size(marker.Size);
|
|
|
|
markerWidth = Math.Max(markerWidth, markerSize.Width);
|
|
|
|
totalSize.Width = Math.Max(totalSize.Width, cp.TextSize.Width);
|
|
totalSize.Height += (Math.Max(markerSize.Height, cp.TextSize.Height) + Dpi.Width4);
|
|
}
|
|
}
|
|
|
|
totalSize.Width += markerWidth;
|
|
|
|
totalSize.Width += (cstyle.Margin.Horizontal + cstyle.Padding.Horizontal + Dpi.Width4);
|
|
totalSize.Height += (cstyle.Margin.Horizontal + cstyle.Padding.Horizontal - Dpi.Width4);
|
|
|
|
return (GetCrosshairLabelBounds(bounds, pt, totalSize));
|
|
}
|
|
|
|
#region GetCrosshairLabelBounds
|
|
|
|
private Rectangle GetCrosshairLabelBounds(
|
|
Rectangle bounds, Point pt, Size size)
|
|
{
|
|
Rectangle r;
|
|
|
|
int left = pt.X - bounds.X - Dpi.Width10;
|
|
int right = bounds.Right - pt.X - Dpi.Width10;
|
|
|
|
if ((right >= size.Width) || (right > left))
|
|
{
|
|
r = new Rectangle(pt.X + 10, pt.Y - size.Height,
|
|
size.Width, size.Height);
|
|
}
|
|
else
|
|
{
|
|
r = new Rectangle(pt.X - (size.Width + Dpi.Width10),
|
|
pt.Y - size.Height, size.Width, size.Height);
|
|
}
|
|
|
|
r.Y -= Dpi.Height10;
|
|
|
|
if (r.Y < ContentBounds.Y + Dpi.Height2)
|
|
r.Y = ContentBounds.Y + Dpi.Height2;
|
|
|
|
return (r);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabelGroups
|
|
|
|
private void RenderCrosshairLabelGroups(Graphics g,
|
|
Hashtable gps, Rectangle bounds, CrosshairVisualStyle cstyle)
|
|
{
|
|
using (Brush br = new SolidBrush(cstyle.GroupHeaderTextColor))
|
|
{
|
|
foreach (DictionaryEntry pair in gps)
|
|
{
|
|
CrosshairGroup group = (CrosshairGroup)pair.Value;
|
|
|
|
Rectangle r = bounds;
|
|
r.Height = group.TextSize.Height;
|
|
|
|
if (string.IsNullOrEmpty(group.Text) == false)
|
|
{
|
|
g.DrawString(group.Text, cstyle.GroupHeaderFont, br, r);
|
|
|
|
bounds.Y += (group.TextSize.Height + Dpi.Height4);
|
|
bounds.Height -= (group.TextSize.Height + Dpi.Height4);
|
|
}
|
|
|
|
foreach (CrosshairPoint cp in group.Cps)
|
|
{
|
|
if (cp.TextSize.IsEmpty == false)
|
|
{
|
|
RenderCrosshairLabelItem(g, bounds, cp, cstyle);
|
|
|
|
bounds.Y += (cp.TextSize.Height + Dpi.Height4);
|
|
bounds.Height -= (cp.TextSize.Height + Dpi.Height4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabelEach
|
|
|
|
private void RenderCrosshairLabelEach(Graphics g,
|
|
Rectangle bounds, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
List<CrosshairPoint> cps = _CrosshairSeriesPoints;
|
|
|
|
foreach (CrosshairPoint cp in cps)
|
|
RenderCrosshairLabel(g, bounds, cstyle, cp);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabel
|
|
|
|
private void RenderCrosshairLabel(Graphics g,
|
|
Rectangle bounds, CrosshairVisualStyle cstyle, CrosshairPoint cp)
|
|
{
|
|
string arg = GetCrosshairArgument(cp);
|
|
|
|
MeasureCrosshairLabel(g, bounds, cp, cstyle, arg, true);
|
|
|
|
if (cp.TextSize.IsEmpty == false)
|
|
{
|
|
Size markerSize = Size.Empty;
|
|
|
|
if (ChartCrosshair.ShowCrosshairLabelMarkers == true)
|
|
{
|
|
Image marker = cp.ChartSeries.PointMarkerImage;
|
|
|
|
if (marker != null)
|
|
markerSize = marker.Size;
|
|
}
|
|
|
|
Size totalSize = new Size((markerSize.Width + cp.TextSize.Width),
|
|
Math.Max(markerSize.Height, cp.TextSize.Height) + Dpi.Height4);
|
|
|
|
totalSize.Width += Dpi.Width(cstyle.Padding.Horizontal + 4);
|
|
totalSize.Height += Dpi.Height(cstyle.Padding.Vertical - 4);
|
|
|
|
Point pt = cp.Point;
|
|
|
|
if (ChartCrosshair.PointIntersectMode == PointIntersectMode.Edge)
|
|
{
|
|
if (cp.ChartSeries.SeriesType == SeriesType.VerticalDot)
|
|
pt.Y -= (cp.SeriesPoint.PointSize.Height / 2);
|
|
|
|
else if (cp.ChartSeries.SeriesType == SeriesType.HorizontalDot)
|
|
pt.X += (cp.SeriesPoint.PointSize.Width / 2);
|
|
}
|
|
|
|
Rectangle cbounds = GetCrosshairPointBounds(bounds, cp, pt, totalSize);
|
|
|
|
DrawCrosshairCallout(g, cbounds, pt, cstyle);
|
|
|
|
cbounds = GetAdjustedBounds(cbounds, cstyle.Padding);
|
|
|
|
RenderCrosshairLabelItem(g, cbounds, cp, cstyle);
|
|
}
|
|
}
|
|
|
|
#region GetCrosshairPointBounds
|
|
|
|
private Rectangle GetCrosshairPointBounds(
|
|
Rectangle bounds, CrosshairPoint cp, Point pt, Size size)
|
|
{
|
|
Rectangle r;
|
|
|
|
int n = size.Width / 5;
|
|
|
|
int left = pt.X - bounds.X + n;
|
|
int right = bounds.Right - pt.X + n;
|
|
|
|
if ((right >= size.Width) || (right > left))
|
|
{
|
|
r = new Rectangle(pt.X - n, pt.Y - size.Height,
|
|
size.Width, size.Height);
|
|
}
|
|
else
|
|
{
|
|
r = new Rectangle(pt.X - (size.Width - n),
|
|
pt.Y - size.Height, size.Width, size.Height);
|
|
}
|
|
|
|
r.Y -= Dpi.Height20;
|
|
|
|
if (cp.ChartSeries.SeriesType == SeriesType.VerticalBar ||
|
|
cp.ChartSeries.SeriesType == SeriesType.VerticalHiLoBar)
|
|
{
|
|
if (cp.IsDescending == true)
|
|
{
|
|
if (pt.Y + 20 + r.Height < Bounds.Bottom)
|
|
r.Y = pt.Y + Dpi.Height20;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r.Y < Bounds.Y + 2)
|
|
r.Y = pt.Y + Dpi.Height20;
|
|
}
|
|
|
|
if (cp.ChartSeries.SeriesType == SeriesType.HorizontalBar ||
|
|
cp.ChartSeries.SeriesType == SeriesType.HorizontalHiLoBar)
|
|
{
|
|
if (cp.IsDescending == true)
|
|
r.X = pt.X - (size.Width - n);
|
|
}
|
|
|
|
if (r.Right > Bounds.Right - Dpi.Width2)
|
|
r.X = Bounds.Right - r.Width - Dpi.Width2;
|
|
|
|
if (r.Left < Bounds.X + Dpi.Width2)
|
|
r.X = Bounds.X + Dpi.Width2;
|
|
|
|
return (r);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetCrosshairHeader
|
|
|
|
private string GetCrosshairHeader(CrosshairPoint cp, List<CrosshairPoint> cps)
|
|
{
|
|
string text = GetCrosshairArgument(cp);
|
|
|
|
ChartControl.DoGetCrosshairLabelHeaderEvent(this, cp, cps, ref text);
|
|
|
|
return (text);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetCrosshairArgument
|
|
|
|
private string GetCrosshairArgument(CrosshairPoint cp)
|
|
{
|
|
AxisOrientation ao = ChartCrosshair.AxisOrientation;
|
|
|
|
if (cp.ChartSeries.IsRotated == true)
|
|
{
|
|
ao = (ChartCrosshair.AxisOrientation == AxisOrientation.X)
|
|
? AxisOrientation.Y : AxisOrientation.X;
|
|
}
|
|
|
|
if (ao == AxisOrientation.X)
|
|
return (GetValueXText(cp.ChartSeries, cp.ValueX));
|
|
|
|
return (GetValueYText(cp.ChartSeries, cp.ValueY));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetValueXText
|
|
|
|
private string GetValueXText(ChartSeries series, object value)
|
|
{
|
|
ChartAxis axis = (series.IsRotated)
|
|
? series.AxisY ?? AxisY : series.AxisX ?? AxisX;
|
|
|
|
return (axis.GetCrosshairLabel(
|
|
value, axis.EffectiveCrosshairLabelStyle));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetValueYText
|
|
|
|
private string GetValueYText(ChartSeries series, object value)
|
|
{
|
|
ChartAxis axis = (series.IsRotated)
|
|
? series.AxisX ?? AxisX : series.AxisY ?? AxisY;
|
|
|
|
return (axis.GetCrosshairLabel(
|
|
value, axis.EffectiveCrosshairLabelStyle));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MeasureCrosshairLabel
|
|
|
|
private void MeasureCrosshairLabel(Graphics g,
|
|
Rectangle bounds, CrosshairPoint cp, CrosshairVisualStyle cstyle, string arg, bool multiLine)
|
|
{
|
|
cp.Text = GetCrosshairItemText(cp, arg, multiLine);
|
|
|
|
if (string.IsNullOrEmpty(cp.Text) == false)
|
|
{
|
|
SizeF sizef = g.MeasureString(cp.Text, cstyle.Font, bounds.Width);
|
|
|
|
cp.TextSize = new Size((int)sizef.Width + 1, (int)sizef.Height + 1);
|
|
}
|
|
else
|
|
{
|
|
cp.TextSize = Size.Empty;
|
|
}
|
|
}
|
|
|
|
#region GetCrosshairItemText
|
|
|
|
private string GetCrosshairItemText(CrosshairPoint cp, string arg, bool multiLine)
|
|
{
|
|
string text = cp.ChartSeries.LegendText ?? cp.ChartSeries.Name ?? "";
|
|
|
|
if (String.IsNullOrEmpty(text) == false)
|
|
text += " ";
|
|
|
|
if (arg != null)
|
|
{
|
|
if (multiLine == true && text.Length > 0)
|
|
text += "\n";
|
|
|
|
text += (arg + " : ");
|
|
}
|
|
|
|
text += GetItemText(cp);
|
|
|
|
ChartControl.DoGetCrosshairLabelItemEvent(this, cp, ref text);
|
|
|
|
return (text);
|
|
}
|
|
|
|
#region GetItemText
|
|
|
|
private string GetItemText(CrosshairPoint cp)
|
|
{
|
|
AxisOrientation ao = ChartCrosshair.AxisOrientation;
|
|
|
|
if (cp.ChartSeries.IsRotated == true)
|
|
{
|
|
ao = (ChartCrosshair.AxisOrientation == AxisOrientation.X)
|
|
? AxisOrientation.Y : AxisOrientation.X;
|
|
}
|
|
|
|
string text = (ao == AxisOrientation.X)
|
|
? GetValueYText(cp.ChartSeries, cp.ValueY)
|
|
: GetValueXText(cp.ChartSeries, cp.ValueX);
|
|
|
|
switch (cp.ChartSeries.SeriesType)
|
|
{
|
|
case SeriesType.HorizontalBar:
|
|
case SeriesType.VerticalBar:
|
|
return (GetBarText(cp, text));
|
|
|
|
case SeriesType.HorizontalHiLoBar:
|
|
case SeriesType.VerticalHiLoBar:
|
|
return (GetHiLoBarText(cp, text));
|
|
|
|
default:
|
|
return (text);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetBarText
|
|
|
|
private string GetBarText(CrosshairPoint cp, string s1)
|
|
{
|
|
if (cp.ChartSeries.IsRotated == (ChartCrosshair.AxisOrientation == AxisOrientation.Y))
|
|
{
|
|
if (cp.SeriesPoint.ValueY.Length > 1)
|
|
{
|
|
object v1 = cp.ValueY;
|
|
|
|
ChartAxis axis = (cp.ChartSeries.IsRotated)
|
|
? cp.ChartSeries.AxisX ?? AxisX : cp.ChartSeries.AxisY ?? AxisY;
|
|
|
|
CrosshairValueVisualStyle lstyle = axis.EffectiveCrosshairLabelStyle;
|
|
|
|
object v2 = cp.SeriesPoint.ValueY[1];
|
|
|
|
if (v2 != null)
|
|
{
|
|
string s2 = axis.GetCrosshairLabel(v2, lstyle);
|
|
|
|
int y1 = GetDataPointX(axis, v1);
|
|
int y2 = GetDataPointX(axis, v2);
|
|
|
|
if (y1 < y2)
|
|
return (s1 + " — " + s2);
|
|
|
|
return (s2 + " — " + s1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (s1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetHiLoVBarText
|
|
|
|
private string GetHiLoBarText(CrosshairPoint cp, string s1)
|
|
{
|
|
if (cp.ChartSeries.IsRotated == (ChartCrosshair.AxisOrientation == AxisOrientation.Y))
|
|
{
|
|
if (cp.SeriesPoint.ValueY.Length > 1)
|
|
{
|
|
ChartAxis axis = (cp.ChartSeries.IsRotated)
|
|
? cp.ChartSeries.AxisX ?? AxisX : cp.ChartSeries.AxisY ?? AxisY;
|
|
|
|
CrosshairValueVisualStyle lstyle = axis.EffectiveCrosshairLabelStyle;
|
|
|
|
s1 = axis.GetCrosshairLabel(cp.ValueY, lstyle);
|
|
}
|
|
}
|
|
|
|
s1 = s1 + " (" + "HLCO"[cp.ValueIndex] + ")";
|
|
|
|
return (s1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region RenderCrosshairLabelItem
|
|
|
|
private void RenderCrosshairLabelItem(Graphics g,
|
|
Rectangle bounds, CrosshairPoint cp, CrosshairVisualStyle cstyle)
|
|
{
|
|
if (ChartControl.DoRenderCrosshairLabelItemEvent(g, this, cp, bounds, cstyle) == false)
|
|
{
|
|
Rectangle r = RenderCrosshairLabelMarker(g, bounds, cp);
|
|
|
|
Color color = cstyle.TextColor;
|
|
|
|
if (color.IsEmpty == true)
|
|
{
|
|
color = cp.ChartSeries.GetLegendItemColor();
|
|
|
|
color = Color.FromArgb(255, color);
|
|
}
|
|
|
|
using (Brush lbr = new SolidBrush(color))
|
|
g.DrawString(cp.Text, cstyle.Font, lbr, r);
|
|
}
|
|
}
|
|
|
|
#region RenderCrosshairLabelMarker
|
|
|
|
private Rectangle RenderCrosshairLabelMarker(
|
|
Graphics g, Rectangle r, CrosshairPoint cp)
|
|
{
|
|
if (ChartCrosshair.ShowCrosshairLabelMarkers == true)
|
|
{
|
|
Image image = cp.ChartSeries.PointMarkerImage;
|
|
|
|
if (image != null)
|
|
{
|
|
Rectangle t = r;
|
|
|
|
Size isize = image.Size;
|
|
|
|
if (cp.TextSize.Height > isize.Height)
|
|
t.Y += (cp.TextSize.Height - isize.Height) / 2;
|
|
|
|
g.DrawImageUnscaled(image, t);
|
|
|
|
r.X += (isize.Width + Dpi.Width4);
|
|
r.Width -= (isize.Width + Dpi.Width4);
|
|
}
|
|
}
|
|
|
|
return (r);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region DrawRoundRectangle
|
|
|
|
private void DrawRoundRectangle(Graphics g,
|
|
Rectangle t, CrosshairVisualStyle cstyle)
|
|
{
|
|
SmoothingMode sm = g.SmoothingMode;
|
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
using (GraphicsPath path = new GraphicsPath())
|
|
{
|
|
int dia = Dpi.Width14;
|
|
Rectangle r = t;
|
|
|
|
r.Width = dia;
|
|
r.Height = dia;
|
|
path.AddArc(r, 180, 90);
|
|
|
|
r.X += (t.Width - dia);
|
|
path.AddArc(r, 270, 90);
|
|
|
|
r.Y += (t.Height - dia);
|
|
path.AddArc(r, 0, 90);
|
|
|
|
r.X = t.X;
|
|
path.AddArc(r, 90, 90);
|
|
|
|
path.CloseFigure();
|
|
|
|
using (Brush br = cstyle.Background.GetBrush(r))
|
|
g.FillPath(br, path);
|
|
|
|
using (Pen pen = new Pen(cstyle.BorderColor, cstyle.BorderThickness))
|
|
{
|
|
pen.DashStyle = (DashStyle)cstyle.BorderPattern;
|
|
|
|
g.DrawPath(pen, path);
|
|
}
|
|
}
|
|
|
|
g.SmoothingMode = sm;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DrawCrosshairCallout
|
|
|
|
private void DrawCrosshairCallout(Graphics g,
|
|
Rectangle t, Point pt, CrosshairVisualStyle cstyle)
|
|
{
|
|
SmoothingMode sm = g.SmoothingMode;
|
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
using (GraphicsPath path = new GraphicsPath())
|
|
{
|
|
AddCalloutToPath(path, t, pt);
|
|
|
|
Rectangle rp = Rectangle.Round(path.GetBounds());
|
|
|
|
if (pt.Y > t.Y)
|
|
rp.Y -= Dpi.Height10;
|
|
|
|
rp.Height += Dpi.Height10;
|
|
|
|
using (Brush br = cstyle.Background.GetBrush(rp))
|
|
g.FillPath(br, path);
|
|
|
|
using (Pen pen = new Pen(cstyle.BorderColor, cstyle.BorderThickness))
|
|
{
|
|
pen.DashStyle = (DashStyle)cstyle.BorderPattern;
|
|
|
|
g.DrawPath(pen, path);
|
|
}
|
|
}
|
|
|
|
g.SmoothingMode = sm;
|
|
}
|
|
|
|
#region AddCalloutToPath
|
|
|
|
private void AddCalloutToPath(GraphicsPath path, Rectangle t, Point pt)
|
|
{
|
|
int dia = Dpi.Width10;
|
|
Rectangle r = t;
|
|
|
|
int n = Dpi.Width20;
|
|
|
|
r.Width = dia;
|
|
r.Height = dia;
|
|
|
|
if (pt.Y > t.Y)
|
|
{
|
|
if (pt.X < (t.X + t.Width / 2))
|
|
{
|
|
path.AddLine(new Point(pt.X + n, pt.Y - Dpi.Width20), pt);
|
|
path.AddLine(pt, new Point(pt.X + Dpi.Width5, pt.Y - Dpi.Width20));
|
|
}
|
|
else
|
|
{
|
|
path.AddLine(new Point(pt.X - Dpi.Width5, pt.Y - Dpi.Width20), pt);
|
|
path.AddLine(pt, new Point(pt.X - n, pt.Y - Dpi.Width20));
|
|
}
|
|
|
|
|
|
r.Y += (t.Height - dia);
|
|
path.AddArc(r, 90, 90);
|
|
|
|
r.Y = t.Y;
|
|
path.AddArc(r, 180, 90);
|
|
|
|
r.X += (t.Width - dia);
|
|
path.AddArc(r, 270, 90);
|
|
|
|
r.Y += (t.Height - dia);
|
|
path.AddArc(r, 0, 90);
|
|
}
|
|
else
|
|
{
|
|
if (pt.X < (t.X + t.Width / 2))
|
|
{
|
|
path.AddLine(new Point(pt.X + Dpi.Width5, pt.Y + Dpi.Width20), pt);
|
|
path.AddLine(pt, new Point(pt.X + n, pt.Y + Dpi.Width20));
|
|
}
|
|
else
|
|
{
|
|
path.AddLine(new Point(pt.X - n, pt.Y + Dpi.Width20), pt);
|
|
path.AddLine(pt, new Point(pt.X - Dpi.Width5, pt.Y + Dpi.Width20));
|
|
}
|
|
|
|
r.X += (t.Width - dia);
|
|
path.AddArc(r, 270, 90);
|
|
|
|
r.Y += (t.Height - dia);
|
|
path.AddArc(r, 0, 90);
|
|
|
|
r.X = t.X;
|
|
path.AddArc(r, 90, 90);
|
|
|
|
r.Y = t.Y;
|
|
path.AddArc(r, 180, 90);
|
|
}
|
|
|
|
path.CloseFigure();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Mouse Support
|
|
|
|
#region OnMouseMove
|
|
|
|
protected override bool OnMouseMove(MouseEventArgs e)
|
|
{
|
|
OnMouseMoveEx(e);
|
|
|
|
if (Legend.Visible == false ||
|
|
Legend.ContentBounds.Contains(e.Location) == false)
|
|
{
|
|
_CanShowCrosshairLabel = true;
|
|
}
|
|
|
|
return (base.OnMouseMove(e));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region OnMouseMoveEx
|
|
|
|
protected override void OnMouseMoveEx(MouseEventArgs e)
|
|
{
|
|
ChartCrosshair crosshair = ChartCrosshair;
|
|
|
|
bool displayCrosshair = DisplayCrosshair;
|
|
|
|
_CanShowCrosshairLabel = false;
|
|
|
|
if (crosshair.Visible == false ||
|
|
(crosshair.ShowCrosshairLabels == false &&
|
|
crosshair.ShowValueXLine == false && crosshair.ShowValueYLine == false &&
|
|
crosshair.ShowValueXLabels == false && crosshair.ShowValueYLabels == false))
|
|
{
|
|
DisplayCrosshair = false;
|
|
}
|
|
else
|
|
{
|
|
Rectangle bounds = GetScrollBounds(ContentBounds);
|
|
|
|
if (VScrollBar.Enabled == true)
|
|
bounds.Width -= (VScrollBar.Width + 1);
|
|
|
|
if (HScrollBar.Enabled == true)
|
|
bounds.Height -= (HScrollBar.Height + 1);
|
|
|
|
DisplayCrosshair = bounds.Contains(e.Location);
|
|
}
|
|
|
|
if (DisplayCrosshair == true || displayCrosshair != DisplayCrosshair)
|
|
{
|
|
if (DisplayCrosshair == false)
|
|
_CrosshairSeriesPoints = null;
|
|
|
|
InvalidateRender();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region OnMouseLeave
|
|
|
|
protected override bool OnMouseLeave(EventArgs e)
|
|
{
|
|
if (DisplayCrosshair == true)
|
|
{
|
|
DisplayCrosshair = false;
|
|
|
|
_CrosshairSeriesPoints = null;
|
|
|
|
InvalidateRender();
|
|
}
|
|
|
|
return (base.OnMouseLeave(e));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetLocalAdjustedPoint
|
|
|
|
/// <summary>
|
|
/// Gets the local, scroll adjusted point.
|
|
/// </summary>
|
|
/// <param name="pt"></param>
|
|
/// <returns></returns>
|
|
public Point GetLocalAdjustedPoint(Point pt)
|
|
{
|
|
pt.X -= ScrollOffset.X;
|
|
pt.X += (HScrollBar.Inverted == true ? HScrollOffset : -HScrollOffset);
|
|
|
|
pt.Y -= ScrollOffset.Y;
|
|
pt.Y += (VScrollBar.Inverted == true ? VScrollOffset : -VScrollOffset);
|
|
|
|
return (pt);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetGlobalAdjustedPoint
|
|
|
|
internal Point GetGlobalAdjustedPoint(Point pt)
|
|
{
|
|
pt.X -= (HScrollBar.Inverted == true ? HScrollOffset : -HScrollOffset);
|
|
pt.Y -= (VScrollBar.Inverted == true ? VScrollOffset : -VScrollOffset);
|
|
|
|
return (pt);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetScBorderThickness
|
|
|
|
internal int GetScBorderThickness(XyAlignment side)
|
|
{
|
|
ChartXyVisualStyle xyStyle = EffectiveChartStyle;
|
|
|
|
int n = 0;
|
|
|
|
switch (side)
|
|
{
|
|
case XyAlignment.Top:
|
|
n = (xyStyle.BorderThickness.Top);
|
|
break;
|
|
|
|
case XyAlignment.Left:
|
|
n = (xyStyle.BorderThickness.Left);
|
|
break;
|
|
|
|
case XyAlignment.Bottom:
|
|
n = (xyStyle.BorderThickness.Bottom +
|
|
((HScrollBar.Visible == true) ? HScrollBar.Height + 1 : 0));
|
|
break;
|
|
|
|
case XyAlignment.Right:
|
|
n = (xyStyle.BorderThickness.Right +
|
|
((VScrollBar.Visible == true) ? VScrollBar.Width + 1 : 0));
|
|
break;
|
|
}
|
|
|
|
return (Dpi.Width(n));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetPointMarker
|
|
|
|
internal Image GetPointMarker(Graphics g, PointMarkerVisualStyle style)
|
|
{
|
|
Image marker = null;
|
|
|
|
marker = style.GetPointMarkerImage();
|
|
|
|
return (marker ?? GetPointMarkerEx(g, style));
|
|
}
|
|
|
|
internal Image GetPointMarkerEx(Graphics g, PointMarkerVisualStyle style)
|
|
{
|
|
return (GetPointMarker(g, style.Type,
|
|
style.PointCount, Dpi.Size(style.Size), style.Rotation,
|
|
style.Background, style.BorderColor, style.BorderWidth));
|
|
}
|
|
|
|
internal Image GetPointMarker(Graphics g, PointMarkerType markerType, int markerPoints,
|
|
Size size, int markerRotation, Background background, Color borderColor, int borderWidth)
|
|
{
|
|
if (_PointMarker == null)
|
|
_PointMarker = new PointMarker();
|
|
|
|
if (markerType == PointMarkerType.None || markerType == PointMarkerType.NotSet)
|
|
markerType = PointMarkerType.Rectangle;
|
|
|
|
return (_PointMarker.GetMarkerBitmap(g, markerType,
|
|
markerPoints, size, markerRotation, background, borderColor, borderWidth));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IsCrosshairSeriesPoint
|
|
|
|
/// <summary>
|
|
/// Gets whether the given Point is a Crosshair displayed point.
|
|
/// </summary>
|
|
/// <param name="series"></param>
|
|
/// <param name="pt"></param>
|
|
/// <returns></returns>
|
|
public bool IsCrosshairSeriesPoint(ChartSeries series, Point pt)
|
|
{
|
|
if (_CrosshairSeriesPoints != null)
|
|
{
|
|
Rectangle r = new Rectangle(pt, Size.Empty);
|
|
r.Inflate(2, 2);
|
|
|
|
foreach (CrosshairPoint chp in _CrosshairSeriesPoints)
|
|
{
|
|
if (r.Contains(chp.Point) && (series == null || chp.ChartSeries == series))
|
|
{
|
|
if (chp == _NearestCrosshairPoint)
|
|
return (true);
|
|
|
|
if (ChartCrosshair.CrosshairLabelMode != CrosshairLabelMode.NearestSeries)
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UpdateRangeValues
|
|
|
|
private void UpdateRangeValues()
|
|
{
|
|
if (SeriesRangeChanged == true)
|
|
{
|
|
SeriesRangeChanged = false;
|
|
|
|
InvalidatePointLabelsEx();
|
|
|
|
_MinValueX = null;
|
|
_MaxValueX = null;
|
|
_MinValueY = null;
|
|
_MaxValueY = null;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (_MinValueX == null)
|
|
{
|
|
_MinValueX = series.MinValueX;
|
|
_MaxValueX = series.MaxValueX;
|
|
_MinValueY = series.MinValueY;
|
|
_MaxValueY = series.MaxValueY;
|
|
}
|
|
else
|
|
{
|
|
if (series.MinValueX != null)
|
|
{
|
|
if (DataCompare(series.MinValueX, _MinValueX) < 0)
|
|
_MinValueX = series.MinValueX;
|
|
|
|
else if (DataCompare(series.MaxValueX, _MaxValueX) > 0)
|
|
_MaxValueX = series.MaxValueX;
|
|
|
|
if (DataCompare(series.MinValueY, _MinValueY) < 0)
|
|
_MinValueY = series.MinValueY;
|
|
|
|
else if (DataCompare(series.MaxValueY, _MaxValueY) > 0)
|
|
_MaxValueY = series.MaxValueY;
|
|
}
|
|
}
|
|
|
|
ChartAxis axis = series.AxisX ?? AxisX;
|
|
|
|
if (axis != null)
|
|
axis.SeriesRangeChanged = true;
|
|
|
|
axis = series.AxisY ?? AxisY;
|
|
|
|
if (axis != null)
|
|
axis.SeriesRangeChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetPointFromValue
|
|
|
|
/// <summary>
|
|
/// Gets the chart Point, given the provided SeriesPoint.
|
|
/// </summary>
|
|
/// <param name="series"></param>
|
|
/// <param name="sp"></param>
|
|
/// <param name="dy"></param>
|
|
/// <returns></returns>
|
|
public Point GetPointFromValue(ChartSeries series, SeriesPoint sp)
|
|
{
|
|
Point pt = GetDataPointNa(series, sp, 0);
|
|
|
|
return (GetLocalAdjustedPoint(pt));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the chart Point, given the X and Y data point values.
|
|
/// </summary>
|
|
/// <param name="series"></param>
|
|
/// <param name="pointValueX"></param>
|
|
/// <param name="pointValueY"></param>
|
|
/// <returns></returns>
|
|
public Point GetPointFromValue(ChartSeries series, object pointValueX, object pointValueY)
|
|
{
|
|
Point pt = Point.Empty;
|
|
|
|
ChartAxis axisX = series.AxisX ?? AxisX;
|
|
ChartAxis axisY = series.AxisY ?? AxisY;
|
|
|
|
pt.X = GetDataPointX(axisX, pointValueX) + series.PointOffset.X;
|
|
pt.Y = GetDataPointY(axisY, pointValueY);
|
|
|
|
return (pt);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetDataPoint
|
|
|
|
internal Point GetDataPoint(ChartSeries series, SeriesPoint sp, int dy)
|
|
{
|
|
Point pt = GetDataPointNa(series, sp, dy);
|
|
|
|
return (GetLocalAdjustedPoint(pt));
|
|
}
|
|
|
|
internal Point GetDataPointNa(ChartSeries series, SeriesPoint sp, int dy)
|
|
{
|
|
if (sp.SeriesValidationCount != SeriesPointCount)
|
|
{
|
|
sp.InvalidatePoints();
|
|
|
|
sp.SeriesValidationCount = SeriesPointCount;
|
|
}
|
|
|
|
int n = (dy < 0) ? 0 : dy;
|
|
|
|
if (sp.Point.Length > n)
|
|
{
|
|
if (sp.Point[n].IsEmpty == true)
|
|
{
|
|
sp.Point[n] = GetDataPointEx(series, sp, dy);
|
|
|
|
if (sp.Point[n].IsEmpty == true)
|
|
return (Point.Empty);
|
|
}
|
|
|
|
return (sp.Point[n]);
|
|
}
|
|
|
|
return (Point.Empty);
|
|
}
|
|
|
|
internal Point GetDataPointEx(ChartSeries series, SeriesPoint sp, int dy)
|
|
{
|
|
object pointValueX = sp.ValueX;
|
|
object pointValueY = 0;
|
|
|
|
if (dy >= 0)
|
|
{
|
|
if (sp.ValueY != null && sp.ValueY.Length > dy)
|
|
pointValueY = sp.ValueY[dy];
|
|
}
|
|
|
|
if (series.IsRotated == true)
|
|
return (GetPointFromValue(series, pointValueY, pointValueX));
|
|
|
|
return (GetPointFromValue(series, pointValueX, pointValueY));
|
|
}
|
|
|
|
#region GetDataPointX
|
|
|
|
internal int GetDataPointX(ChartAxis axis, object pointValueX)
|
|
{
|
|
TickmarkLayout layoutX = axis.TickmarkLayout;
|
|
|
|
if (layoutX.Ticks != null)
|
|
{
|
|
Rectangle tmBoundsX = axis.MajorTickmarks.TickmarkBounds;
|
|
|
|
int minOffset;
|
|
int majOffset = GetDataOffsetX(axis, layoutX, pointValueX, out minOffset);
|
|
|
|
return ((tmBoundsX.X + majOffset - layoutX.MarginOffset) + minOffset);
|
|
}
|
|
else
|
|
{
|
|
Rectangle r = ScrollBounds;
|
|
|
|
return (int)(r.Left);
|
|
}
|
|
}
|
|
|
|
#region GetDataOffsetX
|
|
|
|
private int GetDataOffsetX(ChartAxis axis,
|
|
TickmarkLayout layout, object pointValue, out int minOffset)
|
|
{
|
|
double ptValue;
|
|
|
|
switch (axis.ScaleType)
|
|
{
|
|
case ScaleType.Qualitative:
|
|
ptValue = axis.QualitativeValues.IndexOf(pointValue) - Convert.ToInt32(layout.BaseValue);
|
|
break;
|
|
|
|
case ScaleType.DateTime:
|
|
ptValue = GetDateTimePointValue(axis, layout, (DateTime)pointValue);
|
|
break;
|
|
|
|
default:
|
|
try
|
|
{
|
|
ptValue = Convert.ToDouble(pointValue) - Convert.ToDouble(layout.BaseValue);
|
|
}
|
|
catch
|
|
{
|
|
minOffset = 0;
|
|
|
|
return (0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
int n = (int)(ptValue / layout.MajorSpacing);
|
|
int p1 = (int)(n * layout.MajorInterval);
|
|
int p2 = (int)((n + 1) * layout.MajorInterval);
|
|
|
|
double minorValue = (ptValue - (n * layout.MajorSpacing));
|
|
minOffset = (int)(((p2 - p1) * minorValue) / layout.MajorSpacing);
|
|
|
|
return (p1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetDataPointY
|
|
|
|
internal int GetDataPointY(ChartAxis axis, object pointValueY)
|
|
{
|
|
TickmarkLayout layoutY = axis.TickmarkLayout;
|
|
|
|
if (layoutY.Ticks != null)
|
|
{
|
|
Rectangle tmBoundsY = axis.MajorTickmarks.TickmarkBounds;
|
|
|
|
int minOffset;
|
|
int majOffset = GetDataOffsetY(axis, layoutY, pointValueY, out minOffset);
|
|
|
|
return (((tmBoundsY.Bottom - 1) - majOffset + layoutY.MarginOffset) - minOffset);
|
|
}
|
|
else
|
|
{
|
|
Rectangle r = ScrollBounds;
|
|
|
|
if (HScrollBar.Enabled == true)
|
|
r.Height -= HScrollBar.Height;
|
|
|
|
return (int)(r.Bottom - 1);
|
|
}
|
|
}
|
|
|
|
#region GetDataOffsetY
|
|
|
|
private int GetDataOffsetY(ChartAxis axis,
|
|
TickmarkLayout layout, object pointValue, out int minOffset)
|
|
{
|
|
double ptValue;
|
|
|
|
switch (axis.ScaleType)
|
|
{
|
|
case ScaleType.Qualitative:
|
|
ptValue = axis.QualitativeValues.IndexOf(pointValue) - Convert.ToInt32(layout.BaseValue);
|
|
break;
|
|
|
|
case ScaleType.DateTime:
|
|
ptValue = GetDateTimePointValue(axis, layout, (DateTime)pointValue);
|
|
break;
|
|
|
|
default:
|
|
try
|
|
{
|
|
ptValue = Convert.ToDouble(pointValue) - Convert.ToDouble(layout.BaseValue);
|
|
}
|
|
catch
|
|
{
|
|
minOffset = 0;
|
|
|
|
return (0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
int n = (int)(ptValue / layout.MajorSpacing);
|
|
int p1 = (int)Math.Ceiling(n * layout.MajorInterval);
|
|
int p2 = (int)Math.Ceiling((n + 1) * layout.MajorInterval);
|
|
|
|
double minorValue = (ptValue - (n * layout.MajorSpacing));
|
|
minOffset = (int)Math.Ceiling(((p2 - p1) * minorValue) / layout.MajorSpacing);
|
|
|
|
return (p1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetDateTimePointValue
|
|
|
|
internal double GetDateTimePointValue(
|
|
ChartAxis axis, TickmarkLayout layout, DateTime dt)
|
|
{
|
|
return (GetDateTimePointValue(axis, layout, dt, (DateTime)layout.BaseValue));
|
|
}
|
|
|
|
internal double GetDateTimePointValue(
|
|
ChartAxis axis, TickmarkLayout layout, DateTime dt, DateTime baseValue)
|
|
{
|
|
TimeSpan ts = dt - baseValue;
|
|
|
|
DateTimeUnits adtu = axis.ActualDateTimeUnits;
|
|
|
|
switch (adtu)
|
|
{
|
|
case DateTimeUnits.Ticks:
|
|
return (ts.Ticks);
|
|
|
|
case DateTimeUnits.Milliseconds:
|
|
return (ts.TotalMilliseconds);
|
|
|
|
case DateTimeUnits.Seconds:
|
|
return (ts.TotalSeconds);
|
|
|
|
case DateTimeUnits.Minutes:
|
|
return (ts.TotalMinutes);
|
|
|
|
case DateTimeUnits.Hours:
|
|
return (ts.TotalHours);
|
|
|
|
case DateTimeUnits.Days:
|
|
return (ts.TotalDays);
|
|
|
|
case DateTimeUnits.Months:
|
|
return (axis.CalcDateTimeMonths(baseValue, dt));
|
|
|
|
default:
|
|
return (axis.CalcDateTimeYears(baseValue, dt));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region GetDataPointValueX
|
|
|
|
internal object GetDataPointValueX(ChartSeries series, SeriesPoint sp)
|
|
{
|
|
ChartAxis axisX = series.AxisX ?? AxisX;
|
|
|
|
switch (axisX.ScaleType)
|
|
{
|
|
case ScaleType.Qualitative:
|
|
return (axisX.QualitativeValues.IndexOf(sp.ValueX));
|
|
|
|
default:
|
|
return (sp.ValueX);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetDataPointValueY
|
|
|
|
internal double GetDataPointValueY(ChartSeries series, SeriesPoint sp, int dy)
|
|
{
|
|
if (dy >= 0)
|
|
{
|
|
if (sp.IsQuantitativeYValue == true)
|
|
{
|
|
if (sp.ValueY != null && sp.ValueY.Length > dy)
|
|
return (Convert.ToDouble(sp.ValueY[dy]));
|
|
|
|
return (1);
|
|
}
|
|
|
|
ChartAxis axisY = series.AxisY ?? AxisY;
|
|
|
|
return (axisY.QualitativeValues.IndexOf(sp.ValueY[dy]));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetQualitativeColumnWidth
|
|
|
|
internal int GetQualitativeColumnWidth(ChartAxis axis, int groupId)
|
|
{
|
|
ChartXyVisualStyle cstyle = EffectiveChartStyle;
|
|
|
|
List<ChartSeries> slist = GetQualitativeSeriesList(axis, groupId);
|
|
|
|
int width = 0;
|
|
int swidth = 0;
|
|
|
|
int count = 0;
|
|
|
|
foreach (ChartSeries series in slist)
|
|
{
|
|
Size size;
|
|
|
|
if (series.PointMarkerImage != null)
|
|
size = series.PointMarkerImage.Size;
|
|
else
|
|
size = series.EffectiveChartSeriesStyle.MarkerVisualStyle.Size;
|
|
|
|
if (series.StackQualitativePoints == false)
|
|
{
|
|
count++;
|
|
width += size.Width;
|
|
}
|
|
else
|
|
{
|
|
swidth = Math.Max(swidth, size.Width);
|
|
}
|
|
}
|
|
|
|
width += swidth;
|
|
|
|
if (count > 1)
|
|
width += ((count - 1) * cstyle.IntraSeriesGap);
|
|
|
|
int offset = -width / 2;
|
|
|
|
if (swidth > 0)
|
|
{
|
|
Point pt = new Point(offset + swidth / 2, 0);
|
|
|
|
foreach (ChartSeries series in slist)
|
|
{
|
|
if (series.StackQualitativePoints == true)
|
|
series.PointOffset = pt;
|
|
}
|
|
|
|
offset += (swidth + cstyle.IntraSeriesGap);
|
|
}
|
|
|
|
foreach (ChartSeries series in slist)
|
|
{
|
|
if (series.StackQualitativePoints == false)
|
|
{
|
|
Size size;
|
|
|
|
if (series.PointMarkerImage != null)
|
|
size = series.PointMarkerImage.Size;
|
|
else
|
|
size = series.EffectiveChartSeriesStyle.MarkerVisualStyle.Size;
|
|
|
|
series.PointOffset = new Point(offset + size.Width / 2 + 1, 0);
|
|
|
|
offset += (size.Width + cstyle.IntraSeriesGap);
|
|
}
|
|
}
|
|
|
|
width += (cstyle.InterSeriesGap * 2);
|
|
|
|
return (width);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AdjustQualitativeOffsets
|
|
|
|
internal void AdjustQualitativeOffsets(ChartAxis axis, int groupId, int colWidth, int interval)
|
|
{
|
|
ChartXyVisualStyle cstyle = EffectiveChartStyle;
|
|
|
|
if (cstyle.AutoExpandIntraSeriesGap == Tbool.True)
|
|
{
|
|
List<ChartSeries> slist = GetQualitativeSeriesList(axis, groupId);
|
|
|
|
interval -= cstyle.InterSeriesGap;
|
|
|
|
int count = 0;
|
|
|
|
foreach (ChartSeries series in slist)
|
|
{
|
|
if (series.StackQualitativePoints == false)
|
|
count++;
|
|
}
|
|
|
|
if (count > 1)
|
|
{
|
|
if (interval > colWidth)
|
|
{
|
|
int offset = (interval - colWidth);
|
|
int dv = offset / count;
|
|
|
|
offset = offset / 2 - dv / 2;
|
|
|
|
foreach (ChartSeries series in slist)
|
|
{
|
|
Point pt = series.PointOffset;
|
|
|
|
if (axis.AxisOrientation == AxisOrientation.X)
|
|
pt.X -= offset;
|
|
else
|
|
pt.Y -= offset;
|
|
|
|
series.PointOffset = pt;
|
|
|
|
if (series.StackQualitativePoints == false)
|
|
offset -= dv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetQualitativeSeriesList
|
|
|
|
private List<ChartSeries> GetQualitativeSeriesList(ChartAxis axis, int groupId)
|
|
{
|
|
List<ChartSeries> slist = new List<ChartSeries>();
|
|
|
|
if (axis.ScaleType == ScaleType.Qualitative)
|
|
{
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (series.Visible == true && series.GroupId == groupId)
|
|
{
|
|
if (Legend.Visible == false ||
|
|
(series.ShowCheckBoxInLegend == false || series.CheckedInLegend == true))
|
|
{
|
|
if (axis.AxisOrientation == AxisOrientation.X)
|
|
{
|
|
if (series.AxisX == axis || (series.AxisX == null && axis.IsPrimaryAxis))
|
|
slist.Add(series);
|
|
}
|
|
else
|
|
{
|
|
if (series.AxisY == axis || (series.AxisY == null && axis.IsPrimaryAxis))
|
|
slist.Add(series);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (slist);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetChartBubbleData
|
|
|
|
internal int GetChartBubbleData(out double min, out double max)
|
|
{
|
|
int count = 0;
|
|
|
|
min = double.MaxValue;
|
|
max = double.MinValue;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (series.Visible == true)
|
|
{
|
|
BubblePlotData bpd = series.PlotData as BubblePlotData;
|
|
|
|
if (bpd != null)
|
|
{
|
|
int n = series.SeriesPoints.Count;
|
|
count += n;
|
|
|
|
double size = bpd.MinSize;
|
|
|
|
if (size < min)
|
|
min = size;
|
|
|
|
size = bpd.MaxSize;
|
|
|
|
if (size > max)
|
|
max = size;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (count);
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetMaxDotPlotMarkerSize
|
|
|
|
internal Size GetMaxDotPlotMarkerSize()
|
|
{
|
|
Size size = Size.Empty;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (series.Visible == true)
|
|
{
|
|
Image marker = series.PointMarkerImage;
|
|
|
|
if (marker != null)
|
|
{
|
|
if (marker.Size.Width > size.Width)
|
|
size.Width = marker.Size.Width;
|
|
|
|
if (marker.Size.Height > size.Height)
|
|
size.Height = marker.Size.Height;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (size);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetDotPlotTypes
|
|
|
|
internal DotPlotType GetDotPlotTypes()
|
|
{
|
|
DotPlotType types = DotPlotType.None;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (series.Visible == true)
|
|
{
|
|
if (series.SeriesType == SeriesType.VerticalDot)
|
|
types |= DotPlotType.Vertical;
|
|
|
|
else if (series.SeriesType == SeriesType.HorizontalDot)
|
|
types |= DotPlotType.Horizontal;
|
|
}
|
|
}
|
|
|
|
return (types);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetBaseSeries
|
|
|
|
internal override ChartBaseSeriesCollection GetBaseSeries()
|
|
{
|
|
ChartBaseSeriesCollection baseSeries = new ChartBaseSeriesCollection();
|
|
|
|
foreach (BaseSeries series in ChartSeries)
|
|
baseSeries.Add(series);
|
|
|
|
return (baseSeries);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetSeriesByName
|
|
|
|
/// <summary>
|
|
/// Gets the chart series with the given Name.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <returns>ChartSeries or null.</returns>
|
|
public ChartSeries GetSeriesByName(string name)
|
|
{
|
|
if (String.IsNullOrEmpty(name) == true)
|
|
return (null);
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
{
|
|
if (name.Equals(series.Name) == true)
|
|
return (series);
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetAutoGenSeriesType
|
|
|
|
internal override SeriesType GetAutoGenSeriesType()
|
|
{
|
|
return (AutoGenSeriesType);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetAutoGenSeriesNameCount
|
|
|
|
internal override int GetAutoGenSeriesNameCount()
|
|
{
|
|
return (AutoGenSeriesType == SeriesType.Bubble ? 2 : 1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetNewSeries
|
|
|
|
internal override BaseSeries GetNewSeries()
|
|
{
|
|
return (new ChartSeries(AutoGenSeriesType));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AddChartSeries
|
|
|
|
internal override void AddChartSeries(BaseSeries series)
|
|
{
|
|
ChartSeries.Add((ChartSeries)series);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Style handling
|
|
|
|
#region ApplyStyles
|
|
|
|
public override void ApplyStyles(BaseVisualStyle style)
|
|
{
|
|
base.ApplyStyles(style);
|
|
|
|
ChartXyVisualStyle xyStyle = style as ChartXyVisualStyle;
|
|
|
|
if (xyStyle != null)
|
|
{
|
|
ApplyParentStyles(xyStyle, Parent as ChartContainer);
|
|
|
|
xyStyle.ApplyStyle(ChartVisualStyle);
|
|
|
|
xyStyle.ApplyDefaults();
|
|
}
|
|
else if (style is ChartSeriesVisualStyle)
|
|
{
|
|
ChartSeriesVisualStyle sstyle = (ChartSeriesVisualStyle)style;
|
|
|
|
ApplyParentStyles(sstyle, Parent as ChartContainer);
|
|
|
|
sstyle.ApplyStyle(_ChartSeriesVisualStyle);
|
|
|
|
sstyle.ApplyDefaults();
|
|
}
|
|
else if (style is DataLabelVisualStyle)
|
|
{
|
|
DataLabelVisualStyle dstyle = style as DataLabelVisualStyle;
|
|
|
|
if (dstyle != null)
|
|
{
|
|
ApplyParentStyles(dstyle, Parent as ChartContainer);
|
|
|
|
dstyle.ApplyStyle(_DataLabelVisualStyle);
|
|
|
|
dstyle.ApplyDefaults();
|
|
}
|
|
}
|
|
}
|
|
|
|
#region ApplyParentStyles
|
|
|
|
private void ApplyParentStyles(ChartXyVisualStyle pstyle, ChartContainer item)
|
|
{
|
|
if (item != null)
|
|
{
|
|
ApplyParentStyles(pstyle, item.Parent as ChartContainer);
|
|
|
|
if (item is ChartPanel)
|
|
pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.ChartXyVisualStyle);
|
|
}
|
|
else
|
|
{
|
|
pstyle.ApplyStyle(ChartControl.BaseVisualStyles.ChartXyVisualStyle);
|
|
pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.ChartXyVisualStyle);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ApplyParentStyles (ChartSeriesVisualStyle)
|
|
|
|
private void ApplyParentStyles(ChartSeriesVisualStyle pstyle, ChartContainer item)
|
|
{
|
|
if (item != null)
|
|
{
|
|
ApplyParentStyles(pstyle, item.Parent as ChartContainer);
|
|
|
|
if (item is ChartPanel)
|
|
pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.ChartSeriesVisualStyle);
|
|
}
|
|
else
|
|
{
|
|
pstyle.ApplyStyle(ChartControl.BaseVisualStyles.ChartSeriesVisualStyle);
|
|
pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.ChartSeriesVisualStyle);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ApplyParentStyles (DataLabelVisualStyle)
|
|
|
|
private void ApplyParentStyles(DataLabelVisualStyle pstyle, ChartContainer item)
|
|
{
|
|
if (item != null)
|
|
{
|
|
ApplyParentStyles(pstyle, item.Parent as ChartContainer);
|
|
|
|
if (item is ChartPanel)
|
|
pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.DataLabelVisualStyle);
|
|
}
|
|
else
|
|
{
|
|
pstyle.ApplyStyle(ChartControl.BaseVisualStyles.DataLabelVisualStyle);
|
|
pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.DataLabelVisualStyle);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region ClearEffectiveStyles
|
|
|
|
protected override void ClearEffectiveStyles()
|
|
{
|
|
_EffectiveChartStyle.InvalidateStyle();
|
|
_EffectiveChartSeriesStyle.InvalidateStyle();
|
|
|
|
if (_EffectiveDataLabelStyle.InvalidateStyle() == true)
|
|
InvalidateLayout();
|
|
|
|
base.ClearEffectiveStyles();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region InvalidateLayoutBounds
|
|
|
|
public override void InvalidateLayoutBounds(ScrollBarLite sbar)
|
|
{
|
|
base.InvalidateLayoutBounds(sbar);
|
|
|
|
if (sbar != null)
|
|
{
|
|
Rectangle contentBounds = GetScrollBounds(ContentBounds);
|
|
InvalidateRender(contentBounds);
|
|
|
|
if (sbar.Orientation == Orientation.Horizontal)
|
|
InvalidateAxes(AncillaryAxesX, AxisX);
|
|
else
|
|
InvalidateAxes(AncillaryAxesY, AxisY);
|
|
}
|
|
}
|
|
|
|
#region InvalidateAxes
|
|
|
|
private void InvalidateAxes(ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
foreach (ChartAxis ca in axes)
|
|
{
|
|
if (ca.Visible == true)
|
|
{
|
|
Rectangle bounds = ca.GetScrollBounds(ca.AxisBounds);
|
|
|
|
ca.InvalidateRender(bounds);
|
|
}
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
{
|
|
Rectangle bounds = axis.GetScrollBounds(axis.AxisBounds);
|
|
|
|
axis.InvalidateRender(bounds);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region InvalidateSeriesPoints
|
|
|
|
private void InvalidateSeriesPoints()
|
|
{
|
|
SeriesPointCount++;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
series.InvalidatePoints();
|
|
|
|
InvalidatePointLabelsEx();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region InvalidatePointLabels
|
|
|
|
/// <summary>
|
|
/// Invalidate the cached PointLabels
|
|
/// </summary>
|
|
public void InvalidatePointLabels()
|
|
{
|
|
InvalidatePointLabelsEx();
|
|
InvalidateRender();
|
|
}
|
|
|
|
internal void InvalidatePointLabelsEx()
|
|
{
|
|
_PointLabels = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetLegendData
|
|
|
|
/// <summary>
|
|
/// Gets the list of chart legend data.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override List<ChartLegendItem> GetLegendData()
|
|
{
|
|
base.GetLegendData();
|
|
|
|
GetAxesLegendData(AncillaryAxesX, AxisX);
|
|
GetAxesLegendData(AncillaryAxesY, AxisY);
|
|
|
|
return (LegendData);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetAxesLegendData
|
|
|
|
private void GetAxesLegendData(ChartAxesCollection axes, ChartAxis axis)
|
|
{
|
|
for (int i = axes.Count - 1; i >= 0; i--)
|
|
{
|
|
ChartAxis ca = axes[i];
|
|
|
|
if (ca.Visible == true)
|
|
ca.GetLegendData(LegendData);
|
|
}
|
|
|
|
if (axis.Visible == true)
|
|
axis.GetLegendData(LegendData);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetHitArea
|
|
|
|
/// <summary>
|
|
/// Gets the hit area for the chart.
|
|
/// </summary>
|
|
/// <param name="pt"></param>
|
|
/// <returns>ItemHitArea</returns>
|
|
public override ItemHitArea GetHitArea(Point pt)
|
|
{
|
|
if (AxisX.Bounds.Contains(pt))
|
|
return (ItemHitArea.InPrimaryAxisX);
|
|
|
|
if (AxisY.Bounds.Contains(pt))
|
|
return (ItemHitArea.InPrimaryAxisY);
|
|
|
|
foreach (ChartAxis axis in AncillaryAxesX)
|
|
{
|
|
if (axis.Bounds.Contains(pt))
|
|
return (ItemHitArea.InAncillaryAxisX);
|
|
}
|
|
|
|
foreach (ChartAxis axis in AncillaryAxesY)
|
|
{
|
|
if (axis.Bounds.Contains(pt))
|
|
return (ItemHitArea.InAncillaryAxisY);
|
|
}
|
|
|
|
return (base.GetHitArea(pt));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Copy/CopyTo
|
|
|
|
public override ChartVisualElement Copy()
|
|
{
|
|
ChartXy copy = new ChartXy();
|
|
|
|
CopyTo(copy);
|
|
|
|
return (copy);
|
|
}
|
|
|
|
public override void CopyTo(ChartVisualElement copy)
|
|
{
|
|
ChartXy c = copy as ChartXy;
|
|
|
|
if (c != null)
|
|
{
|
|
base.CopyTo(c);
|
|
|
|
foreach (ChartAxisX axis in AncillaryAxesX)
|
|
c.AncillaryAxesX.Add((ChartAxisX)axis.Copy());
|
|
|
|
foreach (ChartAxisY axis in AncillaryAxesY)
|
|
c.AncillaryAxesY.Add((ChartAxisY)axis.Copy());
|
|
|
|
c.AutoGenSeriesType = AutoGenSeriesType;
|
|
|
|
AxisX.CopyTo(c.AxisX);
|
|
AxisY.CopyTo(c.AxisY);
|
|
|
|
c.BarFillRange = BarFillRange;
|
|
c.BarLabelPosition = BarLabelPosition;
|
|
c.BarOrigin = BarOrigin;
|
|
c.BarOverlayEnabled = BarOverlayEnabled;
|
|
c.BarShadingEnabled = BarShadingEnabled;
|
|
c.BarSpacing = BarSpacing;
|
|
c.BarSpacingRatio = BarSpacingRatio;
|
|
c.BarWidthRatio = BarWidthRatio;
|
|
|
|
c.BubbleIntensityMode = BubbleIntensityMode;
|
|
c.BubbleSizeMode = BubbleSizeMode;
|
|
|
|
c.ChartLineAreaDisplayMode = ChartLineAreaDisplayMode;
|
|
c.ChartLineDisplayMode = ChartLineDisplayMode;
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
c.ChartSeries.Add((ChartSeries)series.Copy());
|
|
|
|
c.ChartSeriesVisualStyle =
|
|
(_ChartSeriesVisualStyle != null) ? _ChartSeriesVisualStyle.Copy() : null;
|
|
|
|
c.ChartVisualStyle = (_ChartVisualStyle != null) ? ChartVisualStyle.Copy() : null;
|
|
|
|
c.ConvexHullDisplayMode = ConvexHullDisplayMode;
|
|
|
|
ChartCrosshair.CopyTo(c.ChartCrosshair);
|
|
|
|
c.DataLabelOverlapMode = DataLabelOverlapMode;
|
|
|
|
c.DataLabelVisualStyle =
|
|
(_DataLabelVisualStyle != null) ? _DataLabelVisualStyle.Copy() : null;
|
|
|
|
c.PointLabelDisplayMode = PointLabelDisplayMode;
|
|
c.SeriesDisplayOrder = SeriesDisplayOrder;
|
|
|
|
c.StepLines = StepLines;
|
|
c.StepLineMode = StepLineMode;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetSerialData
|
|
|
|
internal override SerialElementCollection GetSerialData(string serialName)
|
|
{
|
|
SerialElementCollection sec = new SerialElementCollection();
|
|
|
|
if (serialName != null)
|
|
{
|
|
if (serialName.Equals("") == true)
|
|
serialName = "ChartXy";
|
|
|
|
sec.AddStartElement(serialName);
|
|
}
|
|
|
|
if (_AncillaryAxesX != null && _AncillaryAxesX.Count > 0)
|
|
{
|
|
sec.AddStartElement("AncillaryAxesX count=\"" + _AncillaryAxesX.Count + "\"");
|
|
|
|
foreach (ChartAxisX axis in _AncillaryAxesX)
|
|
sec.AddElement(axis.GetSerialData("ChartAxisX"));
|
|
|
|
sec.AddEndElement("AncillaryAxesX");
|
|
}
|
|
|
|
if (_AncillaryAxesY != null && _AncillaryAxesY.Count > 0)
|
|
{
|
|
sec.AddStartElement("AncillaryAxesY count=\"" + _AncillaryAxesY.Count + "\"");
|
|
|
|
foreach (ChartAxisY axis in _AncillaryAxesY)
|
|
sec.AddElement(axis.GetSerialData("ChartAxisY"));
|
|
|
|
sec.AddEndElement("AncillaryAxesY");
|
|
}
|
|
|
|
sec.AddValue("AutoGenSeriesType", AutoGenSeriesType, SeriesType.Point);
|
|
|
|
if (AxisX != null)
|
|
sec.AddElement(AxisX.GetSerialData("AxisX"));
|
|
|
|
if (AxisY != null)
|
|
sec.AddElement(AxisY.GetSerialData("AxisY"));
|
|
|
|
sec.AddValue("BarFillRange", BarFillRange, BarFillRange.NotSet);
|
|
sec.AddValue("BarLabelPosition", BarLabelPosition, BarLabelPosition.NotSet);
|
|
|
|
sec.AddDataValue("BarOrigin", BarOrigin, null);
|
|
|
|
sec.AddValue("BarOverlayEnabled", BarOverlayEnabled, false);
|
|
sec.AddValue("BarShadingEnabled", BarShadingEnabled, Tbool.NotSet);
|
|
sec.AddValue("BarShowAsHistogram", BarShowAsHistogram, Tbool.NotSet);
|
|
sec.AddValue("BarSpacing", BarSpacing, 0);
|
|
sec.AddValue("BarSpacingRatio", BarSpacingRatio, 0.2d);
|
|
sec.AddValue("BarWidthRatio", BarWidthRatio, 0d);
|
|
|
|
sec.AddValue("BubbleIntensityMode", BubbleIntensityMode, BubbleIntensityMode.NotSet);
|
|
sec.AddValue("BubbleSizeMode", BubbleSizeMode, BubbleSizeMode.NotSet);
|
|
|
|
sec.AddValue("ChartLineAreaDisplayMode", ChartLineAreaDisplayMode, ChartLineAreaDisplayMode.NotSet);
|
|
sec.AddValue("ChartLineDisplayMode", ChartLineDisplayMode, ChartLineDisplayMode.NotSet);
|
|
|
|
if (ChartSeries.Count > 0)
|
|
{
|
|
sec.AddStartElement("ChartSeries count=\"" + ChartSeries.Count + "\"");
|
|
|
|
foreach (ChartSeries series in ChartSeries)
|
|
sec.AddElement(series.GetSerialData("ChartSeries"));
|
|
|
|
sec.AddEndElement("ChartSeries");
|
|
}
|
|
|
|
if (_ChartSeriesVisualStyle != null && _ChartSeriesVisualStyle.IsEmpty == false)
|
|
sec.AddElement(_ChartSeriesVisualStyle.GetSerialData("ChartSeriesVisualStyle"));
|
|
|
|
if (_ChartVisualStyle != null && _ChartVisualStyle.IsEmpty == false)
|
|
sec.AddElement(_ChartVisualStyle.GetSerialData("ChartVisualStyle"));
|
|
|
|
sec.AddValue("ConvexHullDisplayMode", ConvexHullDisplayMode, ConvexHullDisplayMode.NotSet);
|
|
|
|
sec.AddElement(ChartCrosshair.GetSerialData("ChartCrosshair"));
|
|
|
|
sec.AddValue("DataLabelOverlapMode", DataLabelOverlapMode, DataLabelOverlapMode.NotSet);
|
|
|
|
if (_DataLabelVisualStyle != null && _DataLabelVisualStyle.IsEmpty == false)
|
|
sec.AddElement(_DataLabelVisualStyle.GetSerialData("DataLabelVisualStyle"));
|
|
|
|
sec.AddValue("PointLabelDisplayMode", PointLabelDisplayMode, PointLabelDisplayMode.NotSet);
|
|
sec.AddValue("SeriesDisplayOrder", SeriesDisplayOrder, SeriesDisplayOrder.NotSet);
|
|
sec.AddValue("StepLines", StepLines, StepLines.NotSet);
|
|
sec.AddValue("StepLineMode", StepLineMode, StepLineMode.NotSet);
|
|
|
|
sec.AddElement(base.GetSerialData(null));
|
|
|
|
if (serialName != null)
|
|
sec.AddEndElement(serialName);
|
|
|
|
return (sec);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PutSerialData
|
|
|
|
#region ProcessValue
|
|
|
|
internal override void ProcessValue(SerialElement se)
|
|
{
|
|
switch (se.Name)
|
|
{
|
|
case "AutoGenSeriesType":
|
|
AutoGenSeriesType = (SeriesType)se.GetValueEnum(typeof(SeriesType));
|
|
break;
|
|
|
|
case "BarFillRange":
|
|
BarFillRange = (BarFillRange)se.GetValueEnum(typeof(BarFillRange));
|
|
break;
|
|
|
|
case "BarLabelPosition":
|
|
BarLabelPosition = (BarLabelPosition)se.GetValueEnum(typeof(BarLabelPosition));
|
|
break;
|
|
|
|
case "BarOrigin":
|
|
BarOrigin = se.DataValue;
|
|
break;
|
|
|
|
case "BarOverlayEnabled":
|
|
BarOverlayEnabled = bool.Parse(se.StringValue);
|
|
break;
|
|
|
|
case "BarShadingEnabled":
|
|
BarShadingEnabled = (Tbool)se.GetValueEnum(typeof(Tbool));
|
|
break;
|
|
|
|
case "BarShowAsHistogram":
|
|
BarShowAsHistogram = (Tbool)se.GetValueEnum(typeof(Tbool));
|
|
break;
|
|
|
|
case "BarSpacing":
|
|
BarSpacing = int.Parse(se.StringValue);
|
|
break;
|
|
|
|
case "BarSpacingRatio":
|
|
BarSpacingRatio = double.Parse(se.StringValue);
|
|
break;
|
|
|
|
case "BarWidthRatio":
|
|
BarWidthRatio = double.Parse(se.StringValue);
|
|
break;
|
|
|
|
case "BubbleIntensityMode":
|
|
BubbleIntensityMode = (BubbleIntensityMode)se.GetValueEnum(typeof(BubbleIntensityMode));
|
|
break;
|
|
|
|
case "BubbleSizeMode":
|
|
BubbleSizeMode = (BubbleSizeMode)se.GetValueEnum(typeof(BubbleSizeMode));
|
|
break;
|
|
|
|
case "ChartLineAreaDisplayMode":
|
|
ChartLineAreaDisplayMode = (ChartLineAreaDisplayMode)se.GetValueEnum(typeof(ChartLineAreaDisplayMode));
|
|
break;
|
|
|
|
case "ChartLineDisplayMode":
|
|
ChartLineDisplayMode = (ChartLineDisplayMode)se.GetValueEnum(typeof(ChartLineDisplayMode));
|
|
break;
|
|
|
|
case "ConvexHullDisplayMode":
|
|
ConvexHullDisplayMode = (ConvexHullDisplayMode)se.GetValueEnum(typeof(ConvexHullDisplayMode));
|
|
break;
|
|
|
|
case "DataLabelOverlapMode":
|
|
DataLabelOverlapMode = (DataLabelOverlapMode)se.GetValueEnum(typeof(DataLabelOverlapMode));
|
|
break;
|
|
|
|
case "PointLabelDisplayMode":
|
|
PointLabelDisplayMode = (PointLabelDisplayMode)se.GetValueEnum(typeof(PointLabelDisplayMode));
|
|
break;
|
|
|
|
case "SeriesDisplayOrder":
|
|
SeriesDisplayOrder = (SeriesDisplayOrder)se.GetValueEnum(typeof(SeriesDisplayOrder));
|
|
break;
|
|
|
|
case "StepLines":
|
|
StepLines = (StepLines)se.GetValueEnum(typeof(StepLines));
|
|
break;
|
|
|
|
case "StepLineMode":
|
|
StepLineMode = (StepLineMode)se.GetValueEnum(typeof(StepLineMode));
|
|
break;
|
|
|
|
default:
|
|
base.ProcessValue(se);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessCollection
|
|
|
|
internal override void ProcessCollection(SerialElement se)
|
|
{
|
|
SerialElementCollection sec = se.Sec;
|
|
|
|
switch (se.Name)
|
|
{
|
|
case "AncillaryAxesX":
|
|
case "AncillaryAxesY":
|
|
sec.PutSerialData(this);
|
|
break;
|
|
|
|
case "AxisX":
|
|
sec.PutSerialData(AxisX);
|
|
break;
|
|
|
|
case "AxisY":
|
|
sec.PutSerialData(AxisY);
|
|
break;
|
|
|
|
case "ChartAxisX":
|
|
string nameX = sec.GetItemValue("Name");
|
|
|
|
ChartAxis axisX = AncillaryAxesX[nameX];
|
|
|
|
if (axisX != null)
|
|
AncillaryAxesX.Remove(axisX);
|
|
|
|
axisX = new ChartAxisX(nameX);
|
|
|
|
sec.PutSerialData(axisX);
|
|
|
|
AncillaryAxesX.Add(axisX);
|
|
break;
|
|
|
|
case "ChartAxisY":
|
|
string nameY = sec.GetItemValue("Name");
|
|
|
|
ChartAxis axisY = AncillaryAxesY[nameY];
|
|
|
|
if (axisY != null)
|
|
AncillaryAxesY.Remove(axisY);
|
|
|
|
axisY = new ChartAxisY(nameY);
|
|
|
|
sec.PutSerialData(axisY);
|
|
|
|
AncillaryAxesY.Add(axisY);
|
|
break;
|
|
|
|
case "ChartSeries":
|
|
if (se.ArrayCount > 0)
|
|
{
|
|
sec.PutSerialData(this);
|
|
}
|
|
else
|
|
{
|
|
string name = sec.GetItemValue("Name");
|
|
|
|
ChartSeries series = new ChartSeries(name);
|
|
|
|
ChartSeries.Add(series);
|
|
|
|
sec.PutSerialData(series);
|
|
}
|
|
break;
|
|
|
|
case "ChartSeriesVisualStyle":
|
|
sec.PutSerialData(ChartSeriesVisualStyle);
|
|
break;
|
|
|
|
case "ChartVisualStyle":
|
|
sec.PutSerialData(ChartVisualStyle);
|
|
break;
|
|
|
|
case "ChartCrosshair":
|
|
sec.PutSerialData(ChartCrosshair);
|
|
break;
|
|
|
|
case "DataLabelVisualStyle":
|
|
sec.PutSerialData(DataLabelVisualStyle);
|
|
break;
|
|
|
|
default:
|
|
base.ProcessCollection(se);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region States
|
|
|
|
[Flags]
|
|
private enum States : uint
|
|
{
|
|
BarOverlayEnabled = (1U << 0),
|
|
|
|
DisplayCrosshair = (1U << 1),
|
|
DropShadowDisplayed = (1U << 2),
|
|
}
|
|
|
|
#region TestState
|
|
|
|
private bool TestState(States state)
|
|
{
|
|
return ((_States & state) == state);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetState
|
|
|
|
private void SetState(States state, bool value)
|
|
{
|
|
if (value == true)
|
|
_States |= state;
|
|
else
|
|
_States &= ~state;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
public override void Dispose()
|
|
{
|
|
AncillaryAxesX = null;
|
|
AncillaryAxesY = null;
|
|
|
|
AxisX = null;
|
|
AxisY = null;
|
|
|
|
ChartSeriesVisualStyle = null;
|
|
ChartVisualStyle = null;
|
|
ChartCrosshair = null;
|
|
DataLabelVisualStyle = null;
|
|
|
|
base.Dispose();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region CrosshairPoint
|
|
|
|
/// <summary>
|
|
/// Defines a Crosshair Point
|
|
/// </summary>
|
|
public class CrosshairPoint
|
|
{
|
|
#region Public variables
|
|
|
|
private ChartSeries _ChartSeries;
|
|
private SeriesPoint _SeriesPoint;
|
|
|
|
private int _PointIndex;
|
|
private int _ValueIndex;
|
|
private SortedSeriesPoints _SortedSeriesPoints;
|
|
|
|
private Point _Point;
|
|
|
|
private string _Text;
|
|
private Size _TextSize;
|
|
|
|
private bool _IsDescending;
|
|
|
|
#endregion
|
|
|
|
public CrosshairPoint(ChartSeries chartSeries, SortedSeriesPoints ssp,
|
|
int pointIndex, SeriesPoint seriesPoint, int valueIndex, Point point, bool isDescending)
|
|
{
|
|
_ChartSeries = chartSeries;
|
|
_IsDescending = isDescending;
|
|
_SortedSeriesPoints = ssp;
|
|
_PointIndex = pointIndex;
|
|
_SeriesPoint = seriesPoint;
|
|
_ValueIndex = valueIndex;
|
|
|
|
_Point = point;
|
|
}
|
|
|
|
#region Public properties
|
|
|
|
#region ChartSeries
|
|
|
|
/// <summary>
|
|
/// Gets the associated chart series.
|
|
/// </summary>
|
|
public ChartSeries ChartSeries
|
|
{
|
|
get { return (_ChartSeries); }
|
|
internal set { _ChartSeries = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SeriesPoint
|
|
|
|
/// <summary>
|
|
/// Gets the associated SeriesPoint.
|
|
/// </summary>
|
|
public SeriesPoint SeriesPoint
|
|
{
|
|
get { return (_SeriesPoint); }
|
|
internal set { _SeriesPoint = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValueX
|
|
|
|
/// <summary>
|
|
/// Gets the X-Value
|
|
/// </summary>
|
|
public object ValueX
|
|
{
|
|
get { return (SeriesPoint.ValueX); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValueY
|
|
|
|
/// <summary>
|
|
/// Gets the associated ValueY.
|
|
/// </summary>
|
|
public object ValueY
|
|
{
|
|
get
|
|
{
|
|
if (ChartSeries.SeriesType == SeriesType.VerticalDot ||
|
|
ChartSeries.SeriesType == SeriesType.HorizontalDot)
|
|
{
|
|
return (SortedSeriesPoints.CountArray[PointIndex]);
|
|
}
|
|
|
|
if (SeriesPoint.ValueY != null && (uint)_ValueIndex < SeriesPoint.ValueY.Length)
|
|
return (SeriesPoint.ValueY[_ValueIndex]);
|
|
|
|
return (string.Empty);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Internal properties
|
|
|
|
#region IsDescending
|
|
|
|
internal bool IsDescending
|
|
{
|
|
get { return (_IsDescending); }
|
|
set { _IsDescending = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Point
|
|
|
|
internal Point Point
|
|
{
|
|
get { return (_Point); }
|
|
set { _Point = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PointIndex
|
|
|
|
internal int PointIndex
|
|
{
|
|
get { return (_PointIndex); }
|
|
set { _PointIndex = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SortedSeriesPoints
|
|
|
|
internal SortedSeriesPoints SortedSeriesPoints
|
|
{
|
|
get { return (_SortedSeriesPoints); }
|
|
set { _SortedSeriesPoints = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Text
|
|
|
|
internal string Text
|
|
{
|
|
get { return (_Text); }
|
|
set { _Text = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TextSize
|
|
|
|
internal Size TextSize
|
|
{
|
|
get { return (_TextSize); }
|
|
set { _TextSize = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValueIndex
|
|
|
|
internal int ValueIndex
|
|
{
|
|
get { return (_ValueIndex); }
|
|
set { _ValueIndex = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CrosshairGroup
|
|
|
|
internal class CrosshairGroup
|
|
{
|
|
public string Text;
|
|
public Size TextSize;
|
|
|
|
public Size LabelSize;
|
|
public int MarkerWidth;
|
|
|
|
public List<CrosshairPoint> Cps;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region enums
|
|
|
|
#region BarFillRange
|
|
|
|
public enum BarFillRange
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is ByBar)
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Bars are filled according to each individual
|
|
/// bar's defined range.
|
|
/// </summary>
|
|
ByBar,
|
|
|
|
/// <summary>
|
|
/// Bars are filled according to the associated series
|
|
/// minimum and maximum values.
|
|
/// </summary>
|
|
BySeries,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BubbleIntensityMode
|
|
|
|
public enum BubbleIntensityMode
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is None)
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Bubble intensity not used.
|
|
/// </summary>
|
|
None,
|
|
|
|
/// <summary>
|
|
/// Bubble intensity is expressed as an alpha value (0 - 255).
|
|
/// </summary>
|
|
Alpha,
|
|
|
|
/// <summary>
|
|
/// Bubble intensity is expressed as a data value (as is the size of the bubble).
|
|
/// </summary>
|
|
Value,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region BubbleSizeMode
|
|
|
|
public enum BubbleSizeMode
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is Area)
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Bubble size is proportional to the bubble area.
|
|
/// </summary>
|
|
Area,
|
|
|
|
/// <summary>
|
|
/// Bubble size is proportional to the bubble diameter.
|
|
/// </summary>
|
|
Diameter,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartLineDisplayMode
|
|
|
|
[Flags]
|
|
public enum ChartLineDisplayMode
|
|
{
|
|
/// <summary>
|
|
/// Not set
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Display defined points on line
|
|
/// </summary>
|
|
DisplayPoints = (1 << 0),
|
|
|
|
/// <summary>
|
|
/// Display a straight line through defined series points
|
|
/// </summary>
|
|
DisplayLine = (1 << 1),
|
|
|
|
/// <summary>
|
|
/// Display a spline through defined series points
|
|
/// </summary>
|
|
DisplaySpline = (1 << 2),
|
|
|
|
/// <summary>
|
|
/// Display a Step Line through defined series points
|
|
/// </summary>
|
|
DisplayStepLine = (1 << 3),
|
|
|
|
/// <summary>
|
|
/// Points are displayed unsorted
|
|
/// </summary>
|
|
DisplayUnsorted = (1 << 4),
|
|
|
|
/// <summary>
|
|
/// Start and end Points are connected.
|
|
/// </summary>
|
|
DisplayClosed = (1 << 5),
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ChartLineAreaDisplayMode
|
|
|
|
[Flags]
|
|
public enum ChartLineAreaDisplayMode
|
|
{
|
|
/// <summary>
|
|
/// Not set
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Not set
|
|
/// </summary>
|
|
None = (1 << 0),
|
|
|
|
/// <summary>
|
|
/// Display background area under series Line.
|
|
/// </summary>
|
|
DisplayLine = (1 << 1),
|
|
|
|
/// <summary>
|
|
/// Display background area under series Spline.
|
|
/// </summary>
|
|
DisplaySpline = (1 << 2),
|
|
|
|
/// <summary>
|
|
/// Display background area under series StepLine.
|
|
/// </summary>
|
|
DisplayStepLine = (1 << 3),
|
|
|
|
/// <summary>
|
|
/// Display background area under series EmptyLines.
|
|
/// </summary>
|
|
DisplayEmptyLine = (1 << 4),
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DotPlotType
|
|
|
|
[Flags]
|
|
internal enum DotPlotType
|
|
{
|
|
None = 0,
|
|
Horizontal = (1 << 0),
|
|
Vertical = (1 << 1),
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DataLabelOverlapMode
|
|
|
|
/// <summary>
|
|
/// Specifies how overlapping Series Data Labels are resolved
|
|
/// </summary>
|
|
public enum DataLabelOverlapMode
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is None).
|
|
/// </summary>
|
|
NotSet = -1,
|
|
|
|
/// <summary>
|
|
/// Overlapping labels will be shown.
|
|
/// </summary>
|
|
ShowOverlapping,
|
|
|
|
/// <summary>
|
|
/// Overlapping labels will be hidden.
|
|
/// </summary>
|
|
HideOverlapping,
|
|
|
|
/// <summary>
|
|
/// Overlapping labels will be rotated around point (when applicable).
|
|
/// </summary>
|
|
RotateAroundPoint,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SeriesDisplayOrder
|
|
|
|
public enum SeriesDisplayOrder
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is Forward).
|
|
/// </summary>
|
|
NotSet,
|
|
|
|
/// <summary>
|
|
/// Series are displayed in the normal collection order.
|
|
/// </summary>
|
|
Forward,
|
|
|
|
/// <summary>
|
|
/// Series are displayed in the reverse collection order.
|
|
/// </summary>
|
|
Reverse,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region XyAlignment
|
|
|
|
public enum XyAlignment
|
|
{
|
|
NotSet = -1,
|
|
|
|
Top,
|
|
Left,
|
|
Bottom,
|
|
Right,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|