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 { /// /// Represents an X/Y oriented chart. /// 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 _EffectiveDataLabelStyle; private ChartXyVisualStyle _ChartVisualStyle; private EffectiveStyle _EffectiveChartStyle; private ChartSeriesVisualStyle _ChartSeriesVisualStyle; private EffectiveStyle _EffectiveChartSeriesStyle; private ChartCrosshair _ChartCrosshair; private List _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 _PointLabels; #endregion public ChartXy(string name) : this() { Name = name; } public ChartXy() { InitDefaultStates(); _EffectiveChartStyle = new EffectiveStyle(this); _EffectiveChartSeriesStyle = new EffectiveStyle(this); _EffectiveDataLabelStyle = new EffectiveStyle(this); } #region InitDefaultStates private void InitDefaultStates() { } #endregion #region Public properties #region AncillaryAxesX /// /// Gets a reference to the collection of Ancillary X Axes (Axes that /// can be presented in addition to the default X Axis) /// [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 /// /// Gets a reference to the collection of Ancillary Y Axes (Axes that /// can be presented in addition to the default Y Axis) /// [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 /// /// Gets or sets the default SeriesType assigned to auto-generated Series. /// [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 /// /// Gets a reference to the default, primary X Axis. /// [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 /// /// Gets a reference to the default, primary Y Axis. /// [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 /// /// Gets or sets how series bars are filled by default (either according to /// each individual bar range, or the entire series range). /// [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 /// /// Gets or sets the default position of bar series labels (default is Center). /// [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 /// /// Gets or sets the bar 'origin'. This value is used as the base, or /// starting Value, from which each bar originates. /// [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 /// /// 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). /// [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 /// /// Gets or sets whether Bar shading is enabled by /// default for Horizontal and Vertical Bar series. /// [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 /// /// Gets or sets whether the bars will be shown as a Histogram. /// Note that this will only be honored for single series bar displays. /// [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 /// /// 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. /// [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 /// /// Gets or sets the intra-bar spacing ratio (bar spacing between /// multiple series bars associated with the same value. Default is .2). /// [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 /// /// Gets or sets the default ratio of bar width to bar /// group spacing (defaults to 1 - bar width matches spacing). /// [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 /// /// Gets or sets the default mode used to determine series bubble intensities. /// [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 /// /// Gets or sets the default mode used to calculate series bubble sizes. /// [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 /// /// Gets or sets the default Line 'Area' display mode. /// [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 /// /// Gets or sets the default display mode for SeriesType.Line series. /// [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 /// /// Gets a reference to the collection of Chart Series /// [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 /// /// Gets or sets the default visual style for each chart series. /// [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 /// /// Gets or sets the visual style for the Chart. /// [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 /// /// Gets a reference to the Crosshair element for the chart. /// [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 /// /// Gets or sets the default ConvexHull display mode. /// [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 /// /// Gets or sets the mode for resolving overlapping series data labels. /// [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 /// /// Gets or sets the default visual style for the chart DataLabel's. /// [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 /// /// Gets a reference to the ChartSeries effective (cached, composite) style. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ChartSeriesVisualStyle EffectiveChartSeriesStyle { get { return (_EffectiveChartSeriesStyle.Style); } } #endregion #region EffectiveChartStyle /// /// Gets a reference to the Chart's Effective (cached, composite) style. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ChartXyVisualStyle EffectiveChartStyle { get { return (_EffectiveChartStyle.Style); } } #endregion #region EffectiveDataLabelStyle /// /// Gets a reference to the DataLabel Effective (cached, composite) style. /// [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 /// /// Gets the calculated maximum X value (composite value of all associated series). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object MaxValueX { get { UpdateRangeValues(); return (_MaxValueX); } } #endregion #region MaxValueY /// /// Gets the calculated maximum Y value (composite value of all associated series). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object MaxValueY { get { UpdateRangeValues(); return (_MaxValueY); } } #endregion #region MinValueX /// /// Gets the calculated minimum X value (composite value of all associated series). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object MinValueX { get { UpdateRangeValues(); return (_MinValueX); } } #endregion #region MinValueY /// /// Gets the calculated minimum Y value (composite value of all associated series). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object MinValueY { get { UpdateRangeValues(); return (_MinValueY); } } #endregion #region NearestCrosshairPoint /// /// Gets the last calculated CrosshairPoint nearest to the mouse cursor. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CrosshairPoint NearestCrosshairPoint { get { return (_NearestCrosshairPoint); } } #endregion #region PointLabelDisplayMode /// /// Gets or sets the default display mode for the chart PointLabels. /// [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 /// /// Gets the Scrollable bounds of the chart. /// [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 /// /// Gets the Scrollable, extended bounds of the chart. /// [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 /// /// Gets or sets which 'Step lines' are displayed by default. /// [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 /// /// Gets or sets the default mode (or order of rendered Step Lines) /// used to display "Step Lines" in the defined Line series. /// [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 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 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 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 UpdatePointLabels(Graphics g) { if (_PointLabels == null) { List labelGroups = new List(); foreach (ChartSeries series in ChartSeries) { if (series.IsDisplayed == true) { List 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 labelGroups) { ChartControl control = ChartControl; if (control.IsPointLabelUpdateHooked == true) { for (int i = 0; i < labelGroups.Count; i++) { PointLabelGroup lg = labelGroups[i]; List 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 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 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 GetCrosshairPoints(Rectangle bounds, Point pt) { List 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 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 GetCrosshairPointsX(Point pt, Rectangle bounds) { List cps = new List(); 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 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 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 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 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 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 GetCrosshairPointsY(Point pt, Rectangle bounds) { List cps = new List(); 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 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 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 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 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 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 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(); 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 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 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 /// /// Gets the local, scroll adjusted point. /// /// /// 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 /// /// Gets whether the given Point is a Crosshair displayed point. /// /// /// /// 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 /// /// Gets the chart Point, given the provided SeriesPoint. /// /// /// /// /// public Point GetPointFromValue(ChartSeries series, SeriesPoint sp) { Point pt = GetDataPointNa(series, sp, 0); return (GetLocalAdjustedPoint(pt)); } /// /// Gets the chart Point, given the X and Y data point values. /// /// /// /// /// 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 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 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 GetQualitativeSeriesList(ChartAxis axis, int groupId) { List slist = new List(); 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 /// /// Gets the chart series with the given Name. /// /// /// ChartSeries or null. 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 /// /// Invalidate the cached PointLabels /// public void InvalidatePointLabels() { InvalidatePointLabelsEx(); InvalidateRender(); } internal void InvalidatePointLabelsEx() { _PointLabels = null; } #endregion #region GetLegendData /// /// Gets the list of chart legend data. /// /// public override List 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 /// /// Gets the hit area for the chart. /// /// /// ItemHitArea 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 /// /// Defines a Crosshair Point /// 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 /// /// Gets the associated chart series. /// public ChartSeries ChartSeries { get { return (_ChartSeries); } internal set { _ChartSeries = value; } } #endregion #region SeriesPoint /// /// Gets the associated SeriesPoint. /// public SeriesPoint SeriesPoint { get { return (_SeriesPoint); } internal set { _SeriesPoint = value; } } #endregion #region ValueX /// /// Gets the X-Value /// public object ValueX { get { return (SeriesPoint.ValueX); } } #endregion #region ValueY /// /// Gets the associated ValueY. /// 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 Cps; } #endregion #region enums #region BarFillRange public enum BarFillRange { /// /// Not set (default is ByBar) /// NotSet = 0, /// /// Bars are filled according to each individual /// bar's defined range. /// ByBar, /// /// Bars are filled according to the associated series /// minimum and maximum values. /// BySeries, } #endregion #region BubbleIntensityMode public enum BubbleIntensityMode { /// /// Not set (default is None) /// NotSet = 0, /// /// Bubble intensity not used. /// None, /// /// Bubble intensity is expressed as an alpha value (0 - 255). /// Alpha, /// /// Bubble intensity is expressed as a data value (as is the size of the bubble). /// Value, } #endregion #region BubbleSizeMode public enum BubbleSizeMode { /// /// Not set (default is Area) /// NotSet = 0, /// /// Bubble size is proportional to the bubble area. /// Area, /// /// Bubble size is proportional to the bubble diameter. /// Diameter, } #endregion #region ChartLineDisplayMode [Flags] public enum ChartLineDisplayMode { /// /// Not set /// NotSet = 0, /// /// Display defined points on line /// DisplayPoints = (1 << 0), /// /// Display a straight line through defined series points /// DisplayLine = (1 << 1), /// /// Display a spline through defined series points /// DisplaySpline = (1 << 2), /// /// Display a Step Line through defined series points /// DisplayStepLine = (1 << 3), /// /// Points are displayed unsorted /// DisplayUnsorted = (1 << 4), /// /// Start and end Points are connected. /// DisplayClosed = (1 << 5), } #endregion #region ChartLineAreaDisplayMode [Flags] public enum ChartLineAreaDisplayMode { /// /// Not set /// NotSet = 0, /// /// Not set /// None = (1 << 0), /// /// Display background area under series Line. /// DisplayLine = (1 << 1), /// /// Display background area under series Spline. /// DisplaySpline = (1 << 2), /// /// Display background area under series StepLine. /// DisplayStepLine = (1 << 3), /// /// Display background area under series EmptyLines. /// DisplayEmptyLine = (1 << 4), } #endregion #region DotPlotType [Flags] internal enum DotPlotType { None = 0, Horizontal = (1 << 0), Vertical = (1 << 1), } #endregion #region DataLabelOverlapMode /// /// Specifies how overlapping Series Data Labels are resolved /// public enum DataLabelOverlapMode { /// /// Not set (default is None). /// NotSet = -1, /// /// Overlapping labels will be shown. /// ShowOverlapping, /// /// Overlapping labels will be hidden. /// HideOverlapping, /// /// Overlapping labels will be rotated around point (when applicable). /// RotateAroundPoint, } #endregion #region SeriesDisplayOrder public enum SeriesDisplayOrder { /// /// Not set (default is Forward). /// NotSet, /// /// Series are displayed in the normal collection order. /// Forward, /// /// Series are displayed in the reverse collection order. /// Reverse, } #endregion #region XyAlignment public enum XyAlignment { NotSet = -1, Top, Left, Bottom, Right, } #endregion #endregion }