using System;
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 the collection of Chart AxesX.
    /// 
    [Editor("DevComponents.Charts.Design.ChartAxesCollectionEditor, DevComponents.Charts.Design, " +
            "Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
    public class ChartAxesXCollection : ChartAxesCollection
    {
        public ChartAxesXCollection()
            : base(AxisOrientation.X)
        {
        }
        #region GetUniqueName
        public string GetUniqueName()
        {
            return (GetUniqueName("AncAxisX"));
        }
        #endregion
    }
    /// 
    /// Represents the collection of Chart AxesY.
    /// 
    [Editor("DevComponents.Charts.Design.ChartAxesCollectionEditor, DevComponents.Charts.Design, " +
            "Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
    public class ChartAxesYCollection : ChartAxesCollection
    {
        public ChartAxesYCollection()
            : base(AxisOrientation.Y)
        {
        }
        #region GetUniqueName
        public string GetUniqueName()
        {
            return (GetUniqueName("AncAxisY"));
        }
        #endregion
    }
    /// 
    /// Represents the collection of Chart Axes.
    /// 
    public class ChartAxesCollection : CustomNamedCollection
    {
        #region Private variables
        private AxisOrientation _AxisOrientation;
        #endregion
        public ChartAxesCollection(AxisOrientation axisOrientation)
        {
            _AxisOrientation = axisOrientation;
        }
        #region Public properties
        #region AxisOrientation
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public AxisOrientation AxisOrientation
        {
            get { return (_AxisOrientation); }
        }
        #endregion
        #endregion
        #region InsertItem
        protected override void InsertItem(int index, ChartAxis item)
        {
            if (item.IsPrimaryAxis == true)
                throw new Exception("Primary Axes cannot be added as Ancillary Axes.");
 	        base.InsertItem(index, item);
        }
        #endregion
    }
    #region ChartAxisX
    /// 
    /// Represents an X-Axis element.
    /// 
    [TypeConverter(typeof(BlankExpandableObjectConverter))]
    public class ChartAxisX : ChartAxis
    {
        #region Constructors
        public ChartAxisX()
            : base(AxisOrientation.X)
        {
        }
        public ChartAxisX(string name)
            : base(AxisOrientation.X)
        {
            Name = name;
        }
        public ChartAxisX(string name, object minValue, object maxValue)
            : base(AxisOrientation.X)
        {
            Name = name;
            MinValue = minValue;
            MaxValue = MaxValue;
        }
        #endregion
        #region Public properties
        #region MajorGridLines
        ///
        /// Gets the axis MajorGridLines element.
        ///
        [Description("Indicates the MajorGridLines element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override ChartMajorGridLines MajorGridLines
        {
            get
            {
                if (_MajorGridLines == null)
                {
                    _MajorGridLines = new ChartMajorGridLinesX();
                    _MajorGridLines.Parent = this;
                }
                return (_MajorGridLines);
            }
        }
        #endregion
        #region MajorTickmarks
        ///
        /// Gets the axis MajorTickmarks element.
        ///
        [Description("Indicates the MajorTickmarks element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override MajorTickmarks MajorTickmarks
        {
            get
            {
                if (_MajorTickmarks == null)
                {
                    _MajorTickmarks = new MajorTickmarksX();
                    _MajorTickmarks.Parent = this;
                }
                return (_MajorTickmarks);
            }
        }
        #endregion
        #region MinorGridLines
        ///
        /// Gets the axis MinorGridLines element.
        ///
        [Description("Indicates the MinorGridLines element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override ChartMinorGridLines MinorGridLines
        {
            get
            {
                if (_MinorGridLines == null)
                {
                    _MinorGridLines = new ChartMinorGridLinesX();
                    _MinorGridLines.Parent = this;
                }
                return (_MinorGridLines);
            }
        }
        #endregion
        #region MinorTickmarks
        ///
        /// Gets the axis MinorTickmarks element.
        ///
        [Description("Indicates the MinorTickmarks element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override MinorTickmarks MinorTickmarks
        {
            get
            {
                if (_MinorTickmarks == null)
                {
                    _MinorTickmarks = new MinorTickmarksX();
                    _MinorTickmarks.Parent = this;
                }
                return (_MinorTickmarks);
            }
        }
        #endregion
        #endregion
        #region MeasureOverride
        protected override void MeasureOverride(ChartLayoutInfo layoutInfo)
        {
            ChartXy chartXy = Parent as ChartXy;
            ChartAxisVisualStyle astyle = EffectiveAxisStyle;
            AxisAlignment axisAlignment = GetAxisAlignment();
            
            Rectangle bounds = layoutInfo.LayoutBounds;
            if (Visible == true)
            {
                MeasureTitle(layoutInfo, axisAlignment);
                int width = Math.Max(layoutInfo.LayoutBounds.Width, Dpi.Width(chartXy.MinContentSize.Width));
                int tickmarkLen = MeasureTickmarks(layoutInfo, width);
                bounds.Height = GetMeasuredHeight();
                layoutInfo.LayoutBounds.Height -= tickmarkLen;
                if (axisAlignment == AxisAlignment.Far)
                {
                    layoutInfo.LayoutBounds.Y += tickmarkLen;
                }
                else
                {
                    bounds.Y = layoutInfo.LayoutBounds.Bottom;
                    if (EdgeAxis == true)
                    {
                        bounds.Y--;
                        if (chartXy.DropShadowDisplayed == true)
                            layoutInfo.LayoutBounds.Height += 3;
                    }
                }
            }
            else
            {
                int width = Math.Max(layoutInfo.LayoutBounds.Width, Dpi.Width(chartXy.MinContentSize.Width));
                MeasureTickmarks(layoutInfo, width);
                bounds.Height = 0;
            }
            CalcAxisBounds(layoutInfo, axisAlignment, bounds);
            BoundsRelative = bounds;
            base.MeasureOverride(layoutInfo);
        }
        #region MeasureTitle
        protected void MeasureTitle(
            ChartLayoutInfo layoutInfo, AxisAlignment axisAlignment)
        {
            if (Title.Visible == true)
            {
                Title.XyAlignment = (axisAlignment == AxisAlignment.Near)
                    ? XyAlignment.Bottom : XyAlignment.Top;
                Title.Measure(layoutInfo);
            }
        }
        #endregion
        #region MeasureTickmarks
        protected virtual int MeasureTickmarks(ChartLayoutInfo layoutInfo, int width)
        {
            CalcMajorSpacing(layoutInfo, width);
            MajorTickmarks.TickmarkLayout = TickmarkLayout;
            MinorTickmarks.TickmarkLayout = TickmarkLayout;
            MajorTickmarks.Measure(layoutInfo);
            MinorTickmarks.Measure(layoutInfo);
            return (Math.Max(MajorTickmarks.Size.Height,
                             MinorTickmarks.Size.Height));
        }
        #endregion
        #region GetMeasuredHeight
        private int GetMeasuredHeight()
        {
            int height = Math.Max(MajorTickmarks.Size.Height,
                MinorTickmarks.Size.Height);
            if (Title.Visible == true)
                height += Title.Size.Height;
            return (height);
        }
        #endregion
        #region CalcAxisBounds
        private void CalcAxisBounds(ChartLayoutInfo layoutInfo,
            AxisAlignment axisAlignment, Rectangle bounds)
        {
            ChartXy chartXy = Parent as ChartXy;
            int labelHeight = 0;
            int tmInnerLength = 0;
            int tmOuterLength = 0;
            if (TickmarkLayout.MajorCount > 0)
            {
                labelHeight = MajorTickmarks.GetTotalLabelHeight();
                tmOuterLength = Dpi.Width(GetMaxTickMarkLength(false));
                tmInnerLength = Dpi.Width(GetMaxTickMarkLength(true));
                if (axisAlignment == AxisAlignment.Near)
                {
                    if (EdgeAxis == true)
                    {
                        if (chartXy.HScrollBar.Visible == true)
                            tmInnerLength += (chartXy.HScrollBar.Height + 1);
                        bounds.Y -= tmInnerLength;
                    }
                }
                else
                {
                    if (Title.Visible == true)
                        bounds.Y += Title.Size.Height;
                }
                bounds.Height = (labelHeight + tmOuterLength + tmInnerLength + 1);
                AxisBounds = bounds;
                Rectangle r = bounds;
                r.Height = 1;
                if (axisAlignment == AxisAlignment.Near)
                {
                    r.Y += tmInnerLength;
                    if (EdgeAxis == false)
                        r.Y += 1;
                }
                else
                {
                    r.Y = bounds.Bottom - tmInnerLength - 1;
                }
                AxisLineBounds = r;
            }
        }
        #endregion
        #endregion
        #region ArrangeOverride
        protected override void ArrangeOverride(ChartLayoutInfo layoutInfo)
        {
            ChartXy chartXy = Parent as ChartXy;
            // Let the axis bounds extend into the chart's frame
            // area as much as possible for better label display.
            Rectangle r = AxisBounds;
            r.Width = chartXy.ContentBounds.Width;
            int n1 = r.X - chartXy.FrameBounds.X;
            int n2 = chartXy.FrameBounds.Right - r.Right;
            r.X = chartXy.FrameBounds.X;
            r.Width += (n1 + n2);
            AxisBounds = r;
            r = AxisLineBounds;
            r.Width = chartXy.ContentBounds.Width;
            AxisLineBounds = r;
            base.ArrangeOverride(layoutInfo);
        }
        #endregion
        #region RenderOverride
        protected override void RenderOverride(ChartRenderInfo renderInfo)
        {
            if (Visible == true)
            {
                base.RenderOverride(renderInfo);
                Graphics g = renderInfo.Graphics;
                ChartAxisVisualStyle astyle = EffectiveAxisStyle;
                if (TickmarkLayout.MajorCount > 0)
                {
                    Rectangle axisBounds = GetScrollBounds(AxisBounds);
                    Region clip = g.Clip;
                    g.SetClip(axisBounds, CombineMode.Intersect);
                    MajorTickmarks.Render(renderInfo);
                    MinorTickmarks.Render(renderInfo);
                    RenderAxisLine(g, astyle);
                    g.Clip = clip;
                }
            }
        }
        #region RenderAxisLine
        private void RenderAxisLine(Graphics g, ChartAxisVisualStyle astyle)
        {
            if (astyle.AxisColor.IsEmpty == false &&
                astyle.AxisColor.Equals(Color.Transparent) == false)
            {
                Rectangle alBounds = GetScrollBounds(AxisLineBounds);
                using (Pen pen = new Pen(astyle.AxisColor, Dpi.Height1))
                    g.DrawLine(pen, alBounds.X, alBounds.Y, alBounds.Right - 1, alBounds.Y);
            }
        }
        #endregion
        #endregion
        #region RenderCrosshairLabel (Point)
        internal override void RenderCrosshairLabel(Graphics g, Point pt)
        {
            TickmarkLayout layout = TickmarkLayout;
            if (layout != null && layout.Ticks != null && layout.Ticks.Length > 0)
            {
                TickmarkTick tick = layout.Ticks[0];
                CrosshairValueVisualStyle lstyle = EffectiveCrosshairLabelStyle;
                string text = GetCrosshairLabel(pt, layout, tick, lstyle);
                if (string.IsNullOrEmpty(text) == false)
                {
                    Rectangle r = GetCrosshairBounds(g, text, pt, lstyle);
                    lstyle.RenderBackground(g, r);
                    lstyle.RenderBorder(g, r);
                    if (lstyle.DropShadow.Enabled == Tbool.True)
                        lstyle.DropShadow.RenderDropShadow(g, r, true, true);
                    using (StringFormat sf = new StringFormat())
                    {
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;
                        using (Brush br = new SolidBrush(lstyle.TextColor))
                            g.DrawString(text, lstyle.Font, br, r, sf);
                    }
                }
            }
        }
        #region GetCrosshairLabel
        private string GetCrosshairLabel(Point pt,
            TickmarkLayout layout, TickmarkTick tick, CrosshairValueVisualStyle lstyle)
        {
            object value = null;
            switch (ScaleType)
            {
                case ScaleType.DateTime:
                    value = GetValueFromPoint(layout, pt);
                    break;
                case ScaleType.Qualitative:
                    pt = GetChartPoint(pt);
                    int n = (int)Math.Floor((pt.X - (layout.NearMargin + layout.MarginOffset -
                        layout.MajorInterval / 2 + tick.TickPoint.X)) / (layout.MajorInterval * layout.MajorSpacing));
                    n += tick.Index;
                    if ((uint)n < QualitativeValues.Count)
                        value = QualitativeValues[n];
                    break;
                case ScaleType.Quantitative:
                    double dvalue = Convert.ToDouble(GetValueFromPoint(layout, pt));
                    if (DotPlotAxis == true && Math.Floor(layout.MajorSpacing) == layout.MajorSpacing)
                        dvalue = Math.Floor(dvalue + .5);
                    value = dvalue;
                    break;
            }
            return (GetCrosshairLabel(value, lstyle));
        }
        #endregion
        #region GetCrosshairBounds
        private Rectangle GetCrosshairBounds(Graphics g,
            string text, Point pt, CrosshairValueVisualStyle lstyle)
        {
            Rectangle bounds = GetScrollBounds(MajorTickmarks.LabelBounds);
            Rectangle r = bounds; 
            SizeF sizef = g.MeasureString(text, lstyle.Font);
            r.Width = (int)(sizef.Width + 2);
            r.Height = (int)(sizef.Height + 2);
            r.X = pt.X - (int)(sizef.Width / 2);
            r.Y--;
            AxisAlignment axisalignment = GetAxisAlignment();
            if (lstyle.Margin.IsEmpty == false)
            {
                if (axisalignment == AxisAlignment.Near)
                    r.Y += lstyle.Margin.Top;
                else
                    r.Y -= lstyle.Margin.Bottom;
            }
            if (axisalignment == AxisAlignment.Near)
                r.Y += (lstyle.BorderThickness.Vertical);
            else
                r.Y -= (lstyle.BorderThickness.Vertical);
            r.Height += lstyle.BorderThickness.Vertical;
            if (lstyle.Padding.IsEmpty == false)
            {
                if (axisalignment == AxisAlignment.Far)
                    r.Y -= lstyle.Padding.Vertical;
                r.Height += lstyle.Padding.Vertical;
                r.X -= lstyle.Padding.Left;
                r.Width += lstyle.Padding.Horizontal;
            }
            if (r.Bottom > bounds.Bottom)
                r.Y -= (r.Bottom - bounds.Bottom);
            if (r.Right > bounds.Right)
                r.X -= (r.Right - bounds.Right);
            return (r);
        }
        #endregion
        #endregion
        #region RenderCrosshairLabel (CrosshairPoint)
        internal override void RenderCrosshairLabel(Graphics g, CrosshairPoint cp)
        {
            CrosshairValueVisualStyle lstyle = EffectiveCrosshairLabelStyle;
            string text = (cp.ChartSeries.IsRotated == false)
                ? GetCrosshairLabel(cp.SeriesPoint.ValueX, lstyle)
                : GetCrosshairLabel(cp.SeriesPoint.ValueY[cp.ValueIndex], lstyle);
            if (string.IsNullOrEmpty(text) == false)
            {
                Rectangle r = GetCrosshairBounds(g, text, cp.Point, lstyle);
                lstyle.RenderBackground(g, r);
                lstyle.RenderBorder(g, r);
                if (lstyle.DropShadow.Enabled == Tbool.True)
                    lstyle.DropShadow.RenderDropShadow(g, r, true, true);
                using (StringFormat sf = new StringFormat())
                {
                    sf.Alignment = StringAlignment.Center;
                    sf.LineAlignment = StringAlignment.Center;
                    using (Brush br = new SolidBrush(lstyle.TextColor))
                        g.DrawString(text, lstyle.Font, br, r, sf);
                }
            }
        }
        #endregion
        #region RenderBackground
        internal override void RenderBackground(ChartRenderInfo renderInfo, Rectangle scContentBounds)
        {
            if (UseAlternateBackground == true)
            {
                if (TickmarkLayout.Ticks != null)
                {
                    Graphics g = renderInfo.Graphics;
                    ChartXy chartXy = Parent as ChartXy;
                    Point pt = chartXy.GetLocalAdjustedPoint(Point.Empty);
                    if (ScaleType == ScaleType.Qualitative)
                        pt.X -= (int)(TickmarkLayout.MajorInterval / 2);
                    ChartAxisVisualStyle astyle = EffectiveAxisStyle;
                    if (astyle.AlternateBackground.IsSolidBrush == true)
                    {
                        using (Brush br = astyle.AlternateBackground.GetBrush(Rectangle.Empty))
                        {
                            RenderAltBackground(g,
                                scContentBounds, pt, TickmarkLayout, astyle, br);
                        }
                    }
                    else
                    {
                        RenderAltBackground(g,
                            scContentBounds, pt, TickmarkLayout, astyle, null);
                    }
                }
            }
        }
        #region RenderAltBackground
        private void RenderAltBackground(Graphics g, Rectangle scContentBounds, 
            Point pt, TickmarkLayout TickmarkLayout, ChartAxisVisualStyle astyle, Brush br)
        {
            foreach (TickmarkTick tmi in TickmarkLayout.Ticks)
            {
                double value = GetTickmarkAltValue(tmi);
                int n = (int)(Math.Ceiling(value / TickmarkLayout.MajorSpacing));
                if (n % 2 == 0)
                {
                    Rectangle r = scContentBounds;
                    r.X = tmi.TickPoint.X + pt.X;
                    r.Width = (int)TickmarkLayout.MajorInterval + 1;
                    if (br == null)
                    {
                        using (Brush br2 = astyle.AlternateBackground.GetBrush(r))
                            g.FillRectangle(br2, r);
                    }
                    else
                    {
                        g.FillRectangle(br, r);
                    }
                }
            }
        }
        #endregion
        #endregion
        #region RenderStripes
        internal override void RenderStripes(ChartRenderInfo renderInfo, Rectangle scContentBounds)
        {
            if (AxisStripes.Count > 0)
            {
                Graphics g = renderInfo.Graphics;
                ChartXy chartXy = Parent as ChartXy;
                Point pt = chartXy.GetLocalAdjustedPoint(Point.Empty);
                foreach (AxisStripe stripe in AxisStripes)
                {
                    if (stripe.IsDisplayed == true)
                    {
                        if (stripe.MinValue != stripe.MaxValue)
                        {
                            int x1 = chartXy.GetDataPointX(this, stripe.MinValue);
                            int x2 = chartXy.GetDataPointX(this, stripe.MaxValue);
                            Rectangle r = scContentBounds;
                            r.X = (x1 + pt.X);
                            r.Width = (x2 - x1);
                            RenderStripe(g, r, stripe);
                        }
                    }
                }
            }
        }
        #endregion
        #region UpdateRangeValues
        internal override void UpdateRangeValues(ChartXy chartXy)
        {
            bool seriesSeen = false;
            foreach (ChartSeries series in chartXy.ChartSeries)
            {
                if (series.AxisX == this)
                {
                    if (series.SeriesPoints.Count > 0)
                        UpdateRangeValuesEx(chartXy, series, ref seriesSeen);
                }
            }
            if (seriesSeen == false)
            {
                foreach (ChartSeries series in chartXy.ChartSeries)
                {
                    if (series.AxisX == null)
                    {
                        if (series.SeriesPoints.Count > 0)
                            UpdateRangeValuesEx(chartXy, series, ref seriesSeen);
                    }
                }
            }
        }
        #region UpdateRangeValuesEx
        private void UpdateRangeValuesEx(ChartXy chartXy, ChartSeries series, ref bool seriesSeen)
        {
            if (ActualMinValue == null)
            {
                ActualMinValue = series.MinValueX;
                ActualMaxValue = series.MaxValueX;
            }
            else
            {
                if (chartXy.DataCompare(series.MinValueX, ActualMinValue) < 0)
                    ActualMinValue = series.MinValueX;
                if (chartXy.DataCompare(series.MaxValueX, ActualMaxValue) > 0)
                    ActualMaxValue = series.MaxValueX;
            }
            if (seriesSeen == false)
            {
                seriesSeen = true;
                ScaleType = series.ActualScaleTypeX;
            }
            else
            {
                if (series.ActualScaleTypeX != ScaleType)
                {
                    string s = "XAxis cannot contain series with differing ScaleTypes";
                    if (String.IsNullOrEmpty(Name) == false)
                        s += " (" + Name + ")";
                    throw new Exception(s);
                }
            }
            if (ScaleType == ScaleType.Qualitative)
            {
                if (series.IsRotated == true)
                {
                    foreach (object o in series.QualitativeYValues)
                    {
                        if (QualitativeValues.Contains(o) == false)
                            QualitativeValues.Add(o);
                    }
                }
                else
                {
                    foreach (object o in series.QualitativeXValues)
                    {
                        if (QualitativeValues.Contains(o) == false)
                            QualitativeValues.Add(o);
                    }
                }
                if (QualitativeValues.Count - 1 > (int)ActualMaxValue)
                    ActualMaxValue = QualitativeValues.Count - 1;
            }
        }
        #endregion
        #endregion
        #region GetValueFromPoint
        internal override object GetValueFromPoint(TickmarkLayout layout, Point pt)
        {
            if (layout.Ticks != null)
            {
                pt = GetChartPoint(pt);
                TickmarkTick lastTick = null;
                foreach (TickmarkTick tick in layout.Ticks)
                {
                    if (tick.TickPoint.X == pt.X)
                        return (tick.Value);
                    if (tick.TickPoint.X > pt.X)
                    {
                        if (lastTick != null)
                        {
                            double d = ((double)tick.TickPoint.X - lastTick.TickPoint.X) / (layout.MinorCount + 1);
                            for (int i = 1; i <= layout.MinorCount; i++)
                            {
                                int x = (int)(lastTick.TickPoint.X + (d * i));
                                if (x >= pt.X)
                                    break;
                            }
                        }
                        break;
                    }
                    lastTick = tick;
                }
                int dx = pt.X - Bounds.X;
                switch (ScaleType)
                {
                    case ScaleType.Qualitative:
                        return (layout.MajorSpacing * ((dx + layout.MarginOffset) /
                            layout.MajorInterval) + (int)layout.BaseValue);
                    case ScaleType.DateTime:
                        return (GetDateTimePointValue(layout, dx));
                    default:
                        return (layout.MajorSpacing * ((dx + layout.MarginOffset) /
                            layout.MajorInterval) + Convert.ToDouble(layout.BaseValue));
                }
            }
            return (null);
        }
        #endregion
        #region GetPointFromValue
        /// 
        /// Gets the chart Point from the given axis data value.
        /// 
        /// 
        /// 
        public override Point GetPointFromValue(object value)
        {
            return (new Point(GetDisplayValue(value), 0));
        }
        #endregion
        #region GetDisplayValue
        internal override int GetDisplayValue(object axisValue)
        {
            ChartXy chartXy = Parent as ChartXy;
            return (chartXy.GetDataPointX(this, axisValue));
        }
        #endregion
        #region EnsureVisible
        /// 
        /// Ensures the given data value is visible and
        /// optionally centered on screen.
        /// 
        /// 
        public override void EnsureVisible(object value, bool center)
        {
            ChartXy chartXy = Parent as ChartXy;
            if (chartXy != null)
            {
                if (chartXy.ChartControl != null)
                {
                    if (chartXy.ChartControl.LayoutValid == false)
                        chartXy.ChartControl.UpdateLayout();
                    Rectangle cbounds = chartXy.ContentBounds;
                    int x = GetDisplayValue(value);
                    if (center == true)
                    {
                        int n = (x - cbounds.Width / 2) - cbounds.X;
                        chartXy.HScrollOffset = n;
                    }
                    else
                    {
                        int n = cbounds.X + chartXy.HScrollOffset + 20;
                        if (x < n)
                        {
                            chartXy.HScrollOffset += (x - n);
                        }
                        else
                        {
                            n = cbounds.Right + chartXy.HScrollOffset - 20;
                            if (x > n)
                                chartXy.HScrollOffset += (x - n);
                        }
                    }
                }
            }
        }
        #endregion
        #region Copy/CopyTo
        /// 
        /// Creates a copy of the axis.
        /// 
        /// 
        public override ChartVisualElement Copy()
        {
            ChartAxisX copy = new ChartAxisX();
            CopyTo(copy);
            return (copy);
        }
        /// 
        /// Copies the current axis properties to the
        /// given "copy" axis.
        /// 
        /// 
        public override void CopyTo(ChartVisualElement copy)
        {
            ChartAxisX c = copy as ChartAxisX;
            if (c != null)
            {
                base.CopyTo(c);
                MajorGridLines.CopyTo(c.MajorGridLines);
                MinorGridLines.CopyTo(c.MinorGridLines);
                MajorTickmarks.CopyTo(c.MajorTickmarks);
                MinorTickmarks.CopyTo(c.MinorTickmarks);
            }
        }
        #endregion
        #region GetSerialData
        internal override SerialElementCollection GetSerialData(string serialName)
        {
            SerialElementCollection sec = new SerialElementCollection();
            if (serialName != null)
            {
                if (serialName.Equals("") == true)
                    serialName = "ChartAxisX";
                sec.AddStartElement(serialName);
            }
            sec.AddElement(MajorGridLines.GetSerialData("MajorGridLines"));
            sec.AddElement(MinorGridLines.GetSerialData("MinorGridLines"));
            sec.AddElement(MajorTickmarks.GetSerialData("MajorTickmarks"));
            sec.AddElement(MinorTickmarks.GetSerialData("MinorTickmarks"));
            sec.AddElement(base.GetSerialData(null));
            if (serialName != null)
                sec.AddEndElement(serialName);
            return (sec);
        }
        #endregion
        #region PutSerialData
        #region ProcessCollection
        internal override void ProcessCollection(SerialElement se)
        {
            SerialElementCollection sec = se.Sec;
            switch (se.Name)
            {
                case "MajorGridLines":
                    sec.PutSerialData(MajorGridLines);
                    break;
                case "MinorGridLines":
                    sec.PutSerialData(MinorGridLines);
                    break;
                case "MajorTickmarks":
                    sec.PutSerialData(MajorTickmarks);
                    break;
                case "MinorTickmarks":
                    sec.PutSerialData(MinorTickmarks);
                    break;
                default:
                    base.ProcessCollection(se);
                    break;
            }
        }
        #endregion
        #endregion
    }
    #endregion
    #region ChartAxisY
    /// 
    /// Represents a Y-Axis element.
    /// 
    [TypeConverter(typeof(BlankExpandableObjectConverter))]
    public class ChartAxisY : ChartAxis
    {
        #region Constructors
        public ChartAxisY()
            : base(AxisOrientation.Y)
        {
        }
        public ChartAxisY(string name)
            : base(AxisOrientation.Y)
        {
            Name = name;
        }
        public ChartAxisY(string name, object minValue, object maxValue)
            : base(AxisOrientation.Y)
        {
            Name = name;
            MinValue = minValue;
            MaxValue = MaxValue;
        }
        #endregion
        #region Public properties
        #region MajorGridLines
        ///
        /// Gets the axis MajorGridLines element.
        ///
        [Description("Indicates the MajorGridLines element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override ChartMajorGridLines MajorGridLines
        {
            get
            {
                if (_MajorGridLines == null)
                {
                    _MajorGridLines = new ChartMajorGridLinesY();
                    _MajorGridLines.Parent = this;
                }
                return (_MajorGridLines);
            }
        }
        #endregion
        #region MajorTickmarks
        ///
        /// Gets the axis MajorTickmarks element.
        ///
        [Description("Indicates the MajorTickmarks element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override MajorTickmarks MajorTickmarks
        {
            get
            {
                if (_MajorTickmarks == null)
                {
                    _MajorTickmarks = new MajorTickmarksY();
                    _MajorTickmarks.Parent = this;
                }
                return (_MajorTickmarks);
            }
        }
        #endregion
        #region MinorGridLines
        ///
        /// Gets the axis MinorGridLines element.
        ///
        [Description("Indicates the MinorGridLines element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override ChartMinorGridLines MinorGridLines
        {
            get
            {
                if (_MinorGridLines == null)
                {
                    _MinorGridLines = new ChartMinorGridLinesY();
                    _MinorGridLines.Parent = this;
                }
                return (_MinorGridLines);
            }
        }
        #endregion
        #region MinorTickmarks
        ///
        /// Gets the axis MinorTickmarks element.
        ///
        [Description("Indicates the MinorTickmarks element.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public override MinorTickmarks MinorTickmarks
        {
            get
            {
                if (_MinorTickmarks == null)
                {
                    _MinorTickmarks = new MinorTickmarksY();
                    _MinorTickmarks.Parent = this;
                }
                return (_MinorTickmarks);
            }
        }
        #endregion
        #endregion
        #region MeasureOverride
        protected override void MeasureOverride(ChartLayoutInfo layoutInfo)
        {
            ChartXy chartXy = Parent as ChartXy;
            ChartAxisVisualStyle astyle = EffectiveAxisStyle;
            AxisAlignment axisAlignment = GetAxisAlignment();
            Rectangle bounds = layoutInfo.LayoutBounds;
            if (Visible == true)
            {
                MeasureTitle(layoutInfo, axisAlignment);
                int height = Math.Max(layoutInfo.LayoutBounds.Height, Dpi.Height((chartXy.MinContentSize.Height)));
                int tickmarkLen = MeasureTickmarks(layoutInfo, height);
                bounds.Width = GetMeasuredWidth();
                layoutInfo.LayoutBounds.Width -= tickmarkLen;
                if (axisAlignment == AxisAlignment.Far)
                {
                    layoutInfo.LayoutBounds.X += tickmarkLen;
                }
                else
                {
                    bounds.X = layoutInfo.LayoutBounds.Right;
                    if (EdgeAxis == true)
                    {
                        bounds.X -= 1;
                        if (chartXy.DropShadowDisplayed == true)
                            layoutInfo.LayoutBounds.Width += 3;
                    }
                }
            }
            else
            {
                int height = Math.Max(layoutInfo.LayoutBounds.Height, Dpi.Height(chartXy.MinContentSize.Height));
                int tickmarkLen = MeasureTickmarks(layoutInfo, height);
                bounds.Width = 0;
            }
            CalcAxisBounds(layoutInfo, axisAlignment, bounds);
            BoundsRelative = bounds;
            base.MeasureOverride(layoutInfo);
        }
        #region MeasureTitle
        protected void MeasureTitle(
            ChartLayoutInfo layoutInfo, AxisAlignment axisAlignment)
        {
            if (Title.Visible == true)
            {
                Title.XyAlignment = (axisAlignment == AxisAlignment.Near)
                    ? XyAlignment.Right : XyAlignment.Left;
                Title.Measure(layoutInfo);
            }
        }
        #endregion
        #region MeasureTickmarks
        protected virtual int MeasureTickmarks(ChartLayoutInfo layoutInfo, int height)
        {
            CalcMajorSpacing(layoutInfo, height);
            MajorTickmarks.TickmarkLayout = TickmarkLayout;
            MinorTickmarks.TickmarkLayout = TickmarkLayout;
            MajorTickmarks.Measure(layoutInfo);
            MinorTickmarks.Measure(layoutInfo);
            return (Math.Max(MajorTickmarks.Size.Width,
                             MinorTickmarks.Size.Width));
        }
        #endregion
        #region GetMeasuredWidth
        private int GetMeasuredWidth()
        {
            int width = Math.Max(MajorTickmarks.Size.Width,
                MinorTickmarks.Size.Width);
            if (Title.Visible == true)
                width += Title.Size.Width;
            return (width);
        }
        #endregion
        #region CalcAxisBounds
        private void CalcAxisBounds(ChartLayoutInfo layoutInfo,
            AxisAlignment axisAlignment, Rectangle bounds)
        {
            int labelWidth = 0;
            int tmInnerLength = 0;
            int tmOuterLength = 0;
            if (TickmarkLayout.MajorCount > 0)
            {
                labelWidth = MajorTickmarks.GetTotalLabelWidth();
                tmOuterLength = Dpi.Width(GetMaxTickMarkLength(false));
                tmInnerLength = Dpi.Width(GetMaxTickMarkLength(true));
                if (axisAlignment == AxisAlignment.Near)
                {
                    if (EdgeAxis == true)
                    {
                        ChartXy chartXy = Parent as ChartXy;
                        if (chartXy.VScrollBar.Visible == true)
                            tmInnerLength += (chartXy.VScrollBar.Width + 1);
                        bounds.X -= tmInnerLength;
                    }
                }
                else
                {
                    if (Title.Visible == true)
                        bounds.X += Title.Size.Width;
                }
                bounds.Width = (labelWidth + tmOuterLength + tmInnerLength + 1);
                AxisBounds = bounds;
                Rectangle r = bounds;
                r.Width = 1;
                if (axisAlignment == AxisAlignment.Near)
                {
                    r.X += tmInnerLength;
                    if (EdgeAxis == false)
                        r.X += 1;
                }
                else
                {
                    r.X = bounds.Right - tmInnerLength - 1;
                }
                AxisLineBounds = r;
            }
        }
        #endregion
        #endregion
        #region ArrangeOverride
        protected override void ArrangeOverride(ChartLayoutInfo layoutInfo)
        {
            ChartXy chartXy = Parent as ChartXy;
            // Let the axis bounds extend into the chart's frame
            // area as much as possible for better label display.
            Rectangle r = AxisBounds;
            r.Height = chartXy.ContentBounds.Height;
            int n1 = r.Y - chartXy.FrameBounds.Y;
            int n2 = chartXy.FrameBounds.Bottom - r.Bottom;
            r.Y = chartXy.FrameBounds.Y;
            r.Height += (n1 + n2);
            AxisBounds = r;
            r = AxisLineBounds;
            r.Height = chartXy.ContentBounds.Height;
            AxisLineBounds = r;
            base.ArrangeOverride(layoutInfo);
        }
        #endregion
        #region RenderOverride
        protected override void RenderOverride(ChartRenderInfo renderInfo)
        {
            base.RenderOverride(renderInfo);
            Graphics g = renderInfo.Graphics;
            ChartAxisVisualStyle astyle = EffectiveAxisStyle;
            if (TickmarkLayout.MajorCount > 0)
            {
                Rectangle axisBounds = GetScrollBounds(AxisBounds);
                Region clip = g.Clip;
                g.SetClip(axisBounds, CombineMode.Intersect);
                MajorTickmarks.Render(renderInfo);
                MinorTickmarks.Render(renderInfo);
                RenderAxisLine(g, astyle);
                g.Clip = clip;
            }
        }
        #region RenderAxisLine
        private void RenderAxisLine(Graphics g, ChartAxisVisualStyle astyle)
        {
            if (astyle.AxisColor.IsEmpty == false &&
                astyle.AxisColor.Equals(Color.Transparent) == false)
            {
                Rectangle alBounds = GetScrollBounds(AxisLineBounds);
                using (Pen pen = new Pen(astyle.AxisColor, Dpi.Width1))
                    g.DrawLine(pen, alBounds.X, alBounds.Y, alBounds.X, alBounds.Bottom - 1);
            }
        }
        #endregion
        #endregion
        #region RenderCrosshairLabel (Point)
        internal override void RenderCrosshairLabel(Graphics g, Point pt)
        {
            TickmarkLayout layout = TickmarkLayout;
            if (layout != null && layout.Ticks != null && layout.Ticks.Length > 0)
            {
                TickmarkTick tick = layout.Ticks[0];
                CrosshairValueVisualStyle lstyle = EffectiveCrosshairLabelStyle;
                string text = GetCrosshairLabel(pt, layout, tick, lstyle);
                if (string.IsNullOrEmpty(text) == false)
                {
                    Rectangle r = GetCrosshairBounds(g, text, pt, lstyle);
                    lstyle.RenderBackground(g, r);
                    lstyle.RenderBorder(g, r);
                    if (lstyle.DropShadow.Enabled == Tbool.True)
                        lstyle.DropShadow.RenderDropShadow(g, r, true, true);
                    using (StringFormat sf = new StringFormat())
                    {
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;
                        using (Brush br = new SolidBrush(lstyle.TextColor))
                            g.DrawString(text, lstyle.Font, br, r, sf);
                    }
                }
            }
        }
        #region GetCrosshairLabel
        private string GetCrosshairLabel(Point pt,
            TickmarkLayout layout, TickmarkTick tick, CrosshairValueVisualStyle lstyle)
        {
            object value = null;
            switch (ScaleType)
            {
                case ScaleType.DateTime:
                    value = GetValueFromPoint(layout, pt);
                    break;
                case ScaleType.Qualitative:
                    pt = GetChartPoint(pt);
                    int n = (int)Math.Floor(((tick.TickPoint.Y - layout.NearMargin - layout.MarginOffset +
                        layout.MajorInterval / 2) - pt.Y) / (layout.MajorInterval * layout.MajorSpacing));
                    n += tick.Index;
                    if ((uint)n < QualitativeValues.Count)
                        value = QualitativeValues[n];
                    break;
                case ScaleType.Quantitative:
                    double dvalue = (Double)GetValueFromPoint(layout, pt);
                    if (DotPlotAxis == true && Math.Floor(layout.MajorSpacing) == layout.MajorSpacing)
                        dvalue = Math.Floor(dvalue + .5);
                    value = dvalue;
                    break;
            }
            return (GetCrosshairLabel(value, lstyle));
        }
        #endregion
        #region GetCrosshairBounds
        private Rectangle GetCrosshairBounds(Graphics g,
            string text, Point pt, CrosshairValueVisualStyle lstyle)
        {
            ChartXy chartXy = (ChartXy)Parent;
            AxisAlignment axisalignment = GetAxisAlignment();
            Rectangle r = GetScrollBounds(MajorTickmarks.LabelBounds);
            ChartXyVisualStyle cstyle = chartXy.EffectiveChartStyle;
            if (axisalignment == AxisAlignment.Far)
            {
                r.X -= cstyle.Padding.Left;
                r.Width += cstyle.Padding.Left;
            }
            else
            {
                r.Width += cstyle.Padding.Right;
            }
            SizeF sizef = g.MeasureString(text, lstyle.Font);
            int width = (int) sizef.Width + 2;
            if (Visible == true)
                width = Math.Min(r.Width, width);
            
            if (axisalignment == AxisAlignment.Far)
                r.X = Math.Max(r.X, r.Right - width);
            r.Width = width;
            r.Y = pt.Y - (int)(sizef.Height / 2) - 2 - lstyle.BorderThickness.Top;
            r.Height = (int)sizef.Height;
            if (lstyle.Margin.IsEmpty == false)
            {
                if (axisalignment == AxisAlignment.Near)
                    r.Y += lstyle.Margin.Top;
                else
                    r.Y -= lstyle.Margin.Bottom;
            }
            if (axisalignment == AxisAlignment.Near)
                r.X += (lstyle.BorderThickness.Horizontal);
            else
                r.X -= (lstyle.BorderThickness.Horizontal);
            r.Width += lstyle.BorderThickness.Horizontal;
            r.Height += lstyle.BorderThickness.Vertical;
            if (lstyle.Padding.IsEmpty == false)
            {
                if (axisalignment == AxisAlignment.Far)
                    r.Y -= lstyle.Padding.Vertical;
                r.Height += lstyle.Padding.Vertical;
                r.X -= lstyle.Padding.Left;
                r.Width += lstyle.Padding.Horizontal;
            }
            return (r);
        }
        #endregion
        #endregion
        #region RenderCrosshairLabel (CrosshairPoint)
        internal override void RenderCrosshairLabel(Graphics g, CrosshairPoint cp)
        {
            if ((DotPlotAxis == true) ||
                (cp.SeriesPoint.ValueY == null || cp.SeriesPoint.ValueY.Length == 0))
            {
                RenderCrosshairLabel(g, cp.Point);
            }
            else
            {
                CrosshairValueVisualStyle lstyle = EffectiveCrosshairLabelStyle;
                string text = (cp.ChartSeries.IsRotated == true)
                    ? GetCrosshairLabel(cp.SeriesPoint.ValueX, lstyle)
                    : GetCrosshairLabel(cp.SeriesPoint.ValueY[cp.ValueIndex], lstyle);
                if (string.IsNullOrEmpty(text) == false)
                {
                    Rectangle r = GetCrosshairBounds(g, text, cp.Point, lstyle);
                    lstyle.RenderBackground(g, r);
                    lstyle.RenderBorder(g, r);
                    if (lstyle.DropShadow.Enabled == Tbool.True)
                        lstyle.DropShadow.RenderDropShadow(g, r, true, true);
                    using (StringFormat sf = new StringFormat())
                    {
                        sf.Alignment = StringAlignment.Center;
                        sf.LineAlignment = StringAlignment.Center;
                        using (Brush br = new SolidBrush(lstyle.TextColor))
                            g.DrawString(text, lstyle.Font, br, r, sf);
                    }
                }
            }
        }
        #endregion
        #region RenderBackground
        internal override void RenderBackground(ChartRenderInfo renderInfo, Rectangle scContentBounds)
        {
            if (UseAlternateBackground == true)
            {
                if (TickmarkLayout.Ticks != null)
                {
                    Graphics g = renderInfo.Graphics;
                    ChartXy chartXy = Parent as ChartXy;
                    Point pt = chartXy.GetLocalAdjustedPoint(Point.Empty);
                    if (ScaleType == ScaleType.Qualitative)
                        pt.Y -= (int)(TickmarkLayout.MajorInterval / 2);
                    ChartAxisVisualStyle astyle = EffectiveAxisStyle;
                    if (astyle.AlternateBackground.IsSolidBrush == true)
                    {
                        using (Brush br = astyle.AlternateBackground.GetBrush(Rectangle.Empty))
                        {
                            RenderAltBackground(g,
                                scContentBounds, pt, TickmarkLayout, astyle, br);
                        }
                    }
                    else
                    {
                        RenderAltBackground(g,
                            scContentBounds, pt, TickmarkLayout, astyle, null);
                    }
                }
            }
        }
        #region RenderAltBackground
        private void RenderAltBackground(Graphics g, Rectangle scContentBounds,
            Point pt, TickmarkLayout TickmarkLayout, ChartAxisVisualStyle astyle, Brush br)
        {
            foreach (TickmarkTick tmi in TickmarkLayout.Ticks)
            {
                double value = GetTickmarkAltValue(tmi);
                int n = (int)(Math.Ceiling(value / TickmarkLayout.MajorSpacing));
                if (n % 2 == 0)
                {
                    Rectangle r = scContentBounds;
                    r.Y = tmi.TickPoint.Y + pt.Y;
                    r.Height = (int)TickmarkLayout.MajorInterval + 1;
                    if (br == null)
                    {
                        using (Brush br2 = astyle.AlternateBackground.GetBrush(r))
                            g.FillRectangle(br2, r);
                    }
                    else
                    {
                        g.FillRectangle(br, r);
                    }
                }
            }
        }
        #endregion
        #endregion
        #region RenderStripes
        internal override void RenderStripes(ChartRenderInfo renderInfo, Rectangle scContentBounds)
        {
            if (AxisStripes.Count > 0)
            {
                Graphics g = renderInfo.Graphics;
                ChartXy chartXy = Parent as ChartXy;
                Point pt = chartXy.GetLocalAdjustedPoint(Point.Empty);
                foreach (AxisStripe stripe in AxisStripes)
                {
                    if (stripe.IsDisplayed == true)
                    {
                        int y2 = chartXy.GetDataPointY(this, stripe.MinValue);
                        int y1 = chartXy.GetDataPointY(this, stripe.MaxValue);
                        Rectangle r = scContentBounds;
                        r.Y = (y1 + pt.Y);
                        r.Height = (y2 - y1) + 1;
                        RenderStripe(g, r, stripe);
                    }
                }
            }
        }
        #endregion
        #region UpdateRangeValues
        internal override void UpdateRangeValues(ChartXy chartXy)
        {
            bool seriesSeen = false;
            foreach (ChartSeries series in chartXy.ChartSeries)
            {
                if (series.AxisY == this)
                {
                    if (series.SeriesPoints.Count > 0)
                        UpdateRangeValuesEx(chartXy, series, ref seriesSeen);
                }
            }
            if (seriesSeen == false)
            {
                foreach (ChartSeries series in chartXy.ChartSeries)
                {
                    if (series.AxisY == null)
                    {
                        if (series.SeriesPoints.Count > 0)
                            UpdateRangeValuesEx(chartXy, series, ref seriesSeen);
                    }
                }
            }
        }
        #region UpdateRangeValuesEx
        private void UpdateRangeValuesEx(ChartXy chartXy, ChartSeries series, ref bool seriesSeen)
        {
            if (ActualMinValue == null)
            {
                ActualMinValue = series.MinValueY;
                ActualMaxValue = series.MaxValueY;
            }
            else
            {
                if (chartXy.DataCompare(series.MinValueY, ActualMinValue) < 0)
                    ActualMinValue = series.MinValueY;
                if (chartXy.DataCompare(series.MaxValueY, ActualMaxValue) > 0)
                    ActualMaxValue = series.MaxValueY;
            }
            if (seriesSeen == false)
            {
                seriesSeen = true;
                ScaleType = series.ActualScaleTypeY;
            }
            else
            {
                if (series.ActualScaleTypeY != ScaleType)
                {
                    string s = "YAxis cannot contain series with differing ScaleTypes";
                    if (String.IsNullOrEmpty(Name) == false)
                        s += " (" + Name + ")";
                    throw new Exception(s);
                }
            }
            if (ScaleType == ScaleType.Qualitative)
            {
                if (series.IsRotated == true)
                {
                    foreach (object o in series.QualitativeXValues)
                    {
                        if (QualitativeValues.Contains(o) == false)
                            QualitativeValues.Add(o);
                    }
                }
                else
                {
                    foreach (object o in series.QualitativeYValues)
                    {
                        if (QualitativeValues.Contains(o) == false)
                            QualitativeValues.Add(o);
                    }
                }
                if (QualitativeValues.Count - 1 > (int)ActualMaxValue)
                    ActualMaxValue = QualitativeValues.Count - 1;
            }
        }
        #endregion
        #endregion
        #region GetValueFromPoint
        internal override object GetValueFromPoint(TickmarkLayout layout, Point pt)
        {
            pt = GetChartPoint(pt);
            TickmarkTick lastTick = null;
            foreach (TickmarkTick tick in layout.Ticks)
            {
                if (tick.TickPoint.Y == pt.Y)
                    return (Convert.ToDouble(tick.Value));
                if (tick.TickPoint.Y < pt.Y)
                {
                    if (lastTick != null)
                    {
                        double d = ((double)lastTick.TickPoint.Y - tick.TickPoint.Y) / (layout.MinorCount + 1);
                        for (int i = 1; i <= layout.MinorCount; i++)
                        {
                            int y = (int)(lastTick.TickPoint.Y - (d * i));
                            if (y == pt.Y)
                                return (Convert.ToDouble(lastTick.Value) + (layout.MajorSpacing / (layout.MinorCount + 1) * i));
                            if (y < pt.Y)
                                break;
                        }
                    }
                    break;
                }
                lastTick = tick;
            }
            int dy = Bounds.Bottom - pt.Y;
            switch (ScaleType)
            {
                case ScaleType.Qualitative:
                    return (layout.MajorSpacing * ((dy + layout.MarginOffset) / 
                        layout.MajorInterval) + (int)layout.BaseValue);
                case ScaleType.DateTime:
                    return (GetDateTimePointValue(layout, dy));
                default:
                    return (layout.MajorSpacing * ((dy + layout.MarginOffset) / 
                        layout.MajorInterval) + Convert.ToDouble(layout.BaseValue));
            }
        }
        #endregion
        #region GetPointFromValue
        /// 
        /// Gets the chart Point from the given axis data value.
        /// 
        /// 
        /// 
        public override Point GetPointFromValue(object value)
        {
            return (new Point(0, GetDisplayValue(value)));
        }
        #endregion
        #region GetDisplayValue
        internal override int GetDisplayValue(object axisValue)
        {
            ChartXy chartXy = Parent as ChartXy;
            return (chartXy.GetDataPointY(this, axisValue));
        }
        #endregion
        #region EnsureVisible
        /// 
        /// Ensures the given data value is visible and
        /// optionally centered on screen.
        /// 
        /// 
        public override void EnsureVisible(object value, bool center)
        {
            ChartXy chartXy = Parent as ChartXy;
            if (chartXy != null)
            {
                if (chartXy.ChartControl != null)
                {
                    if (chartXy.ChartControl.LayoutValid == false)
                        chartXy.ChartControl.UpdateLayout();
                    Rectangle cbounds = chartXy.ContentBounds;
                    int y = GetDisplayValue(value);
                    if (center == true)
                    {
                        int n = cbounds.Y - (y - cbounds.Height / 2);
                        chartXy.VScrollOffset = n;
                    }
                    else
                    {
                        int n = cbounds.Y - chartXy.VScrollOffset + 20;
                        if (y < n)
                        {
                            chartXy.VScrollOffset += (n - y);
                        }
                        else
                        {
                            n = cbounds.Bottom - chartXy.VScrollOffset - 20;
                            if (y > n)
                                chartXy.VScrollOffset -= (y - n);
                        }
                    }
                }
            }
        }
        #endregion
        #region Copy/CopyTo
        /// 
        /// Creates a copy of the axis.
        /// 
        /// 
        public override ChartVisualElement Copy()
        {
            ChartAxisY copy = new ChartAxisY();
            CopyTo(copy);
            return (copy);
        }
        /// 
        /// Copies each axis property to the given "copy".
        /// 
        /// 
        public override void CopyTo(ChartVisualElement copy)
        {
            ChartAxisY c = copy as ChartAxisY;
            if (c != null)
            {
                base.CopyTo(c);
                MajorGridLines.CopyTo(c.MajorGridLines);
                MinorGridLines.CopyTo(c.MinorGridLines);
                MajorTickmarks.CopyTo(c.MajorTickmarks);
                MinorTickmarks.CopyTo(c.MinorTickmarks);
            }
        }
        #endregion
        #region GetSerialData
        internal override SerialElementCollection GetSerialData(string serialName)
        {
            SerialElementCollection sec = new SerialElementCollection();
            if (serialName != null)
            {
                if (serialName.Equals("") == true)
                    serialName = "ChartAxisY";
                sec.AddStartElement(serialName);
            }
            sec.AddElement(MajorGridLines.GetSerialData("MajorGridLines"));
            sec.AddElement(MinorGridLines.GetSerialData("MinorGridLines"));
            sec.AddElement(MajorTickmarks.GetSerialData("MajorTickmarks"));
            sec.AddElement(MinorTickmarks.GetSerialData("MinorTickmarks"));
            sec.AddElement(base.GetSerialData(null));
            if (serialName != null)
                sec.AddEndElement(serialName);
            return (sec);
        }
        #endregion
        #region PutSerialData
        #region ProcessCollection
        internal override void ProcessCollection(SerialElement se)
        {
            SerialElementCollection sec = se.Sec;
            switch (se.Name)
            {
                case "MajorGridLines":
                    sec.PutSerialData(MajorGridLines);
                    break;
                case "MinorGridLines":
                    sec.PutSerialData(MinorGridLines);
                    break;
                case "MajorTickmarks":
                    sec.PutSerialData(MajorTickmarks);
                    break;
                case "MinorTickmarks":
                    sec.PutSerialData(MinorTickmarks);
                    break;
                default:
                    base.ProcessCollection(se);
                    break;
            }
        }
        #endregion
        #endregion
    }
    #endregion
    #region ChartAxis
    [TypeConverter(typeof(BlankExpandableObjectConverter))]
    abstract public class ChartAxis : ChartVisualElement
    {
        #region Private variables
        private States _States;
        private AxisOrientation _AxisOrientation = AxisOrientation.X;
        private AxisAlignment _AxisAlignment = AxisAlignment.NotSet;
        private SortDirection _QualitativeSortDirection = SortDirection.None;
        private int _AxisMargins = -1;
        private int _AxisNearMargin = -1;
        private int _AxisFarMargin = -1;
        private AxisStripeCollection _AxisStripes;
        private object _MinValue = null;
        private object _MaxValue = null;
        private object _ActualMinValue;
        private object _ActualMaxValue;
        private double _GridSpacing;
        private double _GridInterval;
        private int _MinGridInterval = 10;
        private AxisTitle _Title;
        private ReferenceLineCollection _ReferenceLines;
        private TickmarkLayout _TickmarkLayout;
        private TickmarkLayout _LastTickmarkLayout;
        private Rectangle _AxisBounds;
        private Rectangle _AxisLineBounds;
        private ChartAxisVisualStyle _ChartAxisVisualStyle;
        private EffectiveStyle _EffectiveAxisStyle;
        private CrosshairValueVisualStyle _CrosshairLabelVisualStyle;
        private EffectiveStyle _EffectiveCrosshairLabelStyle;
        private List