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
}