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 _QualitativeValues; private DateTimeUnits _DateTimeUnits = DateTimeUnits.Days; private DateTimeUnits _DateTimeUnitsLimit = DateTimeUnits.Years; private DateTimeUnits _ActualDateTimeUnits = DateTimeUnits.Days; private ScaleType _ScaleType; private AxisBar[] _AxisBars; #endregion #region Protected variables protected MajorTickmarks _MajorTickmarks; protected MinorTickmarks _MinorTickmarks; protected ChartMajorGridLines _MajorGridLines; protected ChartMinorGridLines _MinorGridLines; #endregion public ChartAxis(AxisOrientation orientation) { InitDefaultStates(); _AxisOrientation = orientation; _EffectiveAxisStyle = new EffectiveStyle(this); _EffectiveCrosshairLabelStyle = new EffectiveStyle(this); _AxisBars = new AxisBar[(int)AxisBarType.MaxBarCount]; for (int i = 0; i < _AxisBars.Length; i++) _AxisBars[i] = new AxisBar(); } #region InitDefaultStates private void InitDefaultStates() { SetState(States.AutoCalcBarMargins, true); SetState(States.SeriesRangeChanged, true); SetState(States.UseAutoMinGridInterval, true); } #endregion #region Public properties #region ActualDateTimeUnits /// /// Gets the actual axis display units for DateTime data (as determined by /// either the DateTimeUnits property or actual inspection of the associated data). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public DateTimeUnits ActualDateTimeUnits { get { return (_ActualDateTimeUnits); } internal set { _ActualDateTimeUnits = value; } } #endregion #region ActualMaxValue /// /// Gets the actual, axis max data value (as determined by /// either the MaxValue property or actual inspection of the associated data). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object ActualMaxValue { get { return (_ActualMaxValue); } internal set { _ActualMaxValue = value; } } #endregion #region ActualMinValue /// /// Gets the actual, axis min data value (as determined by /// either the MinValue property or actual inspection of the associated data). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object ActualMinValue { get { return (_ActualMinValue); } internal set { _ActualMinValue = value; } } #endregion #region AutoCalcBarMargins /// /// Gets or sets whether axis Bar Margins are calculated and used. /// [DefaultValue(true), Category("Appearance")] [Description("Indicates whether axis Bar Margins are calculated and used.")] public bool AutoCalcBarMargins { get { return (TestState(States.AutoCalcBarMargins)); } set { if (value != AutoCalcBarMargins) { SetState(States.AutoCalcBarMargins, value); OnPropertyChangedEx("AutoCalcBarMargins", VisualChangeType.Layout); } } } #endregion #region AxisAlignment /// /// Gets or sets the axis alignment. /// [DefaultValue(AxisAlignment.NotSet), Category("Layout")] [Description("Indicates the axis alignment.")] public AxisAlignment AxisAlignment { get { return (_AxisAlignment); } set { if (value != _AxisAlignment) { _AxisAlignment = value; OnPropertyChangedEx("AxisAlignment", VisualChangeType.Layout); } } } #endregion #region AxisFarMargin /// /// Gets or sets the far/ending margin for the axis. When set ( >= 0 ), the /// value overrides the general AxisMargins setting. /// [DefaultValue(-1), Category("Layout")] [Description("Indicates the far/ending margin for the axis. When set ( >= 0 ), the value overrides the general AxisMargins setting.")] public int AxisFarMargin { get { return (_AxisFarMargin); } set { if (value != _AxisFarMargin) { _AxisFarMargin = value; OnPropertyChangedEx("AxisFarMargin", Style.VisualChangeType.Layout); } } } #endregion #region AxisMargins /// /// Gets or sets the near/beginning and far/ending pixel margins for the axis /// (ignored when set to < 0). /// [DefaultValue(-1), Category("Layout")] [Description("Indicates the near/beginning and far/ending margins for the axis (ignored when set to < 0).")] public int AxisMargins { get { return (_AxisMargins); } set { if (value != _AxisMargins) { _AxisMargins = value; OnPropertyChangedEx("AxisMargins", Style.VisualChangeType.Layout); } } } #endregion #region AxisNearMargin /// /// Gets or sets the near/beginning margin for the axis. When set ( >= 0 ), the /// value overrides the general AxisMargins setting. /// [DefaultValue(-1), Category("Layout")] [Description("Indicates the near/beginning margin for the axis. When set ( >= 0 ), the value overrides the general AxisMargins setting.")] public int AxisNearMargin { get { return (_AxisNearMargin); } set { if (value != _AxisNearMargin) { _AxisNearMargin = value; OnPropertyChangedEx("AxisNearMargin", Style.VisualChangeType.Layout); } } } #endregion #region AxisOrientation /// /// Gets the axis orientation (X/Y). /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public AxisOrientation AxisOrientation { get { return (_AxisOrientation); } } #endregion #region AxisStripes /// /// Gets a reference to the collection of AxisStripes associated with the axis. /// [Category("Appearance")] [Description("Indicates the collection of AxisStripes associated with the axis.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public AxisStripeCollection AxisStripes { get { if (_AxisStripes == null) { _AxisStripes = new AxisStripeCollection(); _AxisStripes.CollectionChanged += AxisStripeCollectionChanged; } return (_AxisStripes); } internal set { if (_AxisStripes != null) _AxisStripes.CollectionChanged -= AxisStripeCollectionChanged; _AxisStripes = value; if (_AxisStripes != null) _AxisStripes.CollectionChanged += AxisStripeCollectionChanged; } } #region AxisStripeCollectionChanged void AxisStripeCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (ChartElement item in e.NewItems) item.Parent = this; break; case NotifyCollectionChangedAction.Replace: foreach (ChartElement item in e.OldItems) item.Parent = null; foreach (ChartElement item in e.NewItems) item.Parent = this; break; case NotifyCollectionChangedAction.Remove: foreach (ChartElement item in e.OldItems) item.Parent = null; break; } InvalidateLayout(); } #endregion #endregion #region ChartAxisVisualStyle /// /// Gets or sets the visual style for the Axis. /// [Category("Style")] [Description("Indicates the visual style for the Axis.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public ChartAxisVisualStyle ChartAxisVisualStyle { get { if (_ChartAxisVisualStyle == null) { _ChartAxisVisualStyle = new ChartAxisVisualStyle(); StyleVisualChangeHandler(null, _ChartAxisVisualStyle); } return (_ChartAxisVisualStyle); } set { if (_ChartAxisVisualStyle != value) { ChartAxisVisualStyle oldValue = _ChartAxisVisualStyle; _ChartAxisVisualStyle = value; OnStyleChanged("ChartAxisVisualStyle", oldValue, value); if (oldValue != null) oldValue.Dispose(); } } } #endregion #region CrosshairLabelVisualStyle /// /// Gets or sets the visual style to be used for Crosshair elements associated with the axis. /// [Category("Style")] [Description("Indicates the visual style to be used for Crosshair elements associated with the axis.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public CrosshairValueVisualStyle CrosshairLabelVisualStyle { get { if (_CrosshairLabelVisualStyle == null) { _CrosshairLabelVisualStyle = new CrosshairValueVisualStyle(); StyleVisualChangeHandler(null, _CrosshairLabelVisualStyle); } return (_CrosshairLabelVisualStyle); } set { if (_CrosshairLabelVisualStyle != value) { CrosshairValueVisualStyle oldValue = _CrosshairLabelVisualStyle; _CrosshairLabelVisualStyle = value; OnStyleChanged("CrosshairLabelVisualStyle", oldValue, value); } } } #endregion #region DateTimeUnits /// /// Gets or sets the axis display units for DateTime data. /// [DefaultValue(DateTimeUnits.Days), Category("Data")] [Description("Indicates the axis display units for DateTime data.")] public DateTimeUnits DateTimeUnits { get { return (_DateTimeUnits); } set { if (value != _DateTimeUnits) { _DateTimeUnits = value; OnPropertyChangedEx("DateTimeUnits", VisualChangeType.Layout); } } } #endregion #region DateTimeUnitsLimit /// /// Gets or sets the limiting axis display units for DateTime data (the /// max DateTime units the chart will, if needed, promote the display to). /// [DefaultValue(DateTimeUnits.Years), Category("Data")] [Description("Indicates the limiting axis display units for DateTime data (the max DateTime units the chart will, if needed, promote the display to).")] public DateTimeUnits DateTimeUnitsLimit { get { return (_DateTimeUnitsLimit); } set { if (value != _DateTimeUnitsLimit) { _DateTimeUnitsLimit = value; OnPropertyChangedEx("DateTimeUnitsLimit", VisualChangeType.Layout); } } } #endregion #region EffectiveAxisStyle /// /// Gets a reference to the Axis' Effective (cached, composite) style. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ChartAxisVisualStyle EffectiveAxisStyle { get { return (_EffectiveAxisStyle.Style); } } #endregion #region EffectiveCrosshairLabelStyle /// /// Gets a reference to the CrosshairLabel's Effective (cached, composite) style. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CrosshairValueVisualStyle EffectiveCrosshairLabelStyle { get { return (_EffectiveCrosshairLabelStyle.Style); } } #endregion #region GridInterval /// /// Gets or sets the grid interval (the distance between MajorTickmarks, in pixels) /// [Description("Indicates the grid interval (the distance between MajorTickmarks, in pixels).")] [DefaultValue(0.0d), Category("Layout")] public double GridInterval { get { return (_GridInterval); } set { if (value != _GridInterval) { _GridInterval = value; OnPropertyChangedEx("GridInterval", VisualChangeType.Layout); } } } #endregion #region GridSpacing /// /// Gets or sets the numerical spacing between MajorTickmarks (such as 1, 10, 100, ...). /// [Description("Indicates the numerical spacing between MajorTickmarks (such as 1, 10, 100, ...).")] [DefaultValue(0.0d), Category("Layout")] public double GridSpacing { get { return (_GridSpacing); } set { if (value != _GridSpacing) { _GridSpacing = value; OnPropertyChangedEx("GridSpacing", VisualChangeType.Layout); } } } #endregion #region IsPrimaryAxis /// /// Gets whether the axis is the Primary axis. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsPrimaryAxis { get { return (TestState(States.PrimaryAxis)); } internal set { SetState(States.PrimaryAxis, value); } } #endregion #region MajorGridLines /// /// Gets the axis MajorGridLines element. /// [Description("Indicates the MajorGridLines element."), Category("Appearance")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public virtual ChartMajorGridLines MajorGridLines { get { return (_MajorGridLines); } } #endregion #region MajorTickmarks /// /// Gets the axis MajorTickmarks element. /// [Description("Indicates the MajorTickmarks element."), Category("Appearance")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public virtual MajorTickmarks MajorTickmarks { get { return (_MajorTickmarks); } } #endregion #region MaxValue /// /// Gets or sets the maximum axis data value. /// [DefaultValue(null), Category("Data")] [Description("Indicates the maximum axis data value.")] [TypeConverter("DevComponents.Charts.Design.PointValueConverter," + "DevComponents.Charts.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")] public object MaxValue { get { return (_MaxValue); } set { if (value != _MaxValue) { _MaxValue = value; OnPropertyChangedEx("MaxValue", Style.VisualChangeType.Layout); } } } #endregion #region MinorGridLines /// /// Gets the axis MinorGridLines element. /// [Description("Indicates the MinorGridLines element."), Category("Appearance")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public virtual ChartMinorGridLines MinorGridLines { get { return (_MinorGridLines); } } #endregion #region MinGridInterval /// /// Gets or sets the minimum grid interval size. This is the distance (in pixels) /// between tickmark layout items. /// [DefaultValue(10), Category("Layout")] [Description("Indicates the minimum grid interval size. This is the distance (in pixels) between tickmark layout items.")] public int MinGridInterval { get { return (_MinGridInterval); } set { if (value != _MinGridInterval) { _MinGridInterval = value; OnPropertyChangedEx("MinGridInterval", Style.VisualChangeType.Layout); } } } #endregion #region MinorTickmarks /// /// Gets the axis MinorTickmarks element. /// [Description("Indicates the MinorTickmarks element."), Category("Appearance")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public virtual MinorTickmarks MinorTickmarks { get { return (_MinorTickmarks); } } #endregion #region MinValue /// /// Gets or sets the minimum axis data value. /// [DefaultValue(null), Category("Data")] [Description("Indicates the minimum axis data value.")] [TypeConverter("DevComponents.Charts.Design.PointValueConverter," + "DevComponents.Charts.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")] public object MinValue { get { return (_MinValue); } set { if (value != _MinValue) { _MinValue = value; OnPropertyChangedEx("MinValue", Style.VisualChangeType.Layout); } } } #endregion #region QualitativeSortDirection /// /// Gets or sets the sort direction for Qualitative axis values. /// [DefaultValue(SortDirection.None), Category("Data")] [Description("Indicates the sort direction for Qualitative axis values.")] public SortDirection QualitativeSortDirection { get { return (_QualitativeSortDirection); } set { if (value != _QualitativeSortDirection) { _QualitativeSortDirection = value; RefreshRangeValues(); OnPropertyChangedEx("QualitativeSortDirection", VisualChangeType.Layout); } } } #endregion #region ReferenceLines /// /// Gets a reference to the collection of axis Reference Lines /// [Category("Appearance")] [Description("Indicates the collection of axis Reference Lines.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public ReferenceLineCollection ReferenceLines { get { if (_ReferenceLines == null) { _ReferenceLines = new ReferenceLineCollection(); _ReferenceLines.CollectionChanged += ReferenceLineCollectionChanged; } return (_ReferenceLines); } internal set { if (_ReferenceLines != null) _ReferenceLines.CollectionChanged -= ReferenceLineCollectionChanged; _ReferenceLines = value; if (_ReferenceLines != null) _ReferenceLines.CollectionChanged += ReferenceLineCollectionChanged; } } #region ReferenceLineCollectionChanged void ReferenceLineCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (ChartElement item in e.NewItems) item.Parent = this; break; case NotifyCollectionChangedAction.Replace: foreach (ChartElement item in e.OldItems) item.Parent = null; foreach (ChartElement item in e.NewItems) item.Parent = this; break; case NotifyCollectionChangedAction.Remove: foreach (ChartElement item in e.OldItems) item.Parent = null; break; } InvalidateRender(); } #endregion #endregion #region Title /// /// Gets or sets the Axis Title. /// [Category("Layout")] [Description("Indicates the Axis Title.")] [TypeConverter(typeof(BlankExpandableObjectConverter))] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public AxisTitle Title { get { if (_Title == null) { _Title = new AxisTitle(); _Title.Parent = this; } return (_Title); } set { if (value != _Title) { _Title = value; _Title.Parent = (value != null) ? this : null; OnPropertyChangedEx("Title", Style.VisualChangeType.Layout); } } } #endregion #region UseAlternateBackground /// /// Gets or sets whether the axis utilizes the alternate background color. /// [DefaultValue(false), Category("Appearance")] [Description("Indicates whether the axis utilizes the alternate background color.")] public bool UseAlternateBackground { get { return (TestState(States.UseAlternateBackground)); } set { if (value != UseAlternateBackground) { SetState(States.UseAlternateBackground, value); OnPropertyChangedEx("UseAlternateBackground", VisualChangeType.Render); } } } #endregion #region UseAutoMinGridInterval /// /// Gets or sets whether the auto-calculated minimum grid interval /// will be used in determining the axis major tickmark interval. /// [DefaultValue(true), Category("Appearance")] [Description("Indicates whether the auto-calculated minimum grid interval will be used in determining the axis major tickmark interval.")] public bool UseAutoMinGridInterval { get { return (TestState(States.UseAutoMinGridInterval)); } set { if (value != UseAutoMinGridInterval) { SetState(States.UseAutoMinGridInterval, value); OnPropertyChangedEx("UseAutoMinGridInterval", VisualChangeType.Layout); } } } #endregion #endregion #region Internal properties #region AxisBars internal AxisBar[] AxisBars { get { return (_AxisBars); } } #endregion #region AxisBounds internal Rectangle AxisBounds { get { return (_AxisBounds); } set { _AxisBounds = value; } } #endregion #region AxisLineBounds internal Rectangle AxisLineBounds { get { return (_AxisLineBounds); } set { _AxisLineBounds = value; } } #endregion #region DotPlotAxis internal bool DotPlotAxis { get { return (TestState(States.DotPlotAxis)); } set { SetState(States.DotPlotAxis, value); } } #endregion #region EdgeAxis internal bool EdgeAxis { get { return (TestState(States.EdgeAxis)); } set { SetState(States.EdgeAxis, value); } } #endregion #region LastTickmarkLayout internal TickmarkLayout LastTickmarkLayout { get { return (_LastTickmarkLayout); } set { _LastTickmarkLayout = value; } } #endregion #region QualitativeValues internal List QualitativeValues { get { if (_QualitativeValues == null) _QualitativeValues = new List(); return (_QualitativeValues); } set { _QualitativeValues = value; } } #endregion #region ScaleType internal ScaleType ScaleType { get { return (_ScaleType); } set { _ScaleType = value; } } #endregion #region SeriesRangeChanged internal bool SeriesRangeChanged { get { return (TestState(States.SeriesRangeChanged)); } set { SetState(States.SeriesRangeChanged, value); } } #endregion #region TickmarkLayout internal TickmarkLayout TickmarkLayout { get { return(_TickmarkLayout); } set { _TickmarkLayout = value; } } #endregion #endregion #region MeasureOverride protected override void MeasureOverride(ChartLayoutInfo layoutInfo) { MeasureReferenceLines(layoutInfo); } protected void MeasureReferenceLines(ChartLayoutInfo layoutInfo) { Rectangle layoutBounds = layoutInfo.LayoutBounds; foreach (ReferenceLine line in ReferenceLines) { layoutInfo.LayoutBounds = BoundsRelative; line.Measure(layoutInfo); } layoutInfo.LayoutBounds = layoutBounds; } #endregion #region ArrangeOverride protected override void ArrangeOverride(ChartLayoutInfo layoutInfo) { if (Title.Visible == true) Title.Arrange(layoutInfo); MajorTickmarks.Arrange(layoutInfo); MinorTickmarks.Arrange(layoutInfo); MajorGridLines.TickmarkLayout = TickmarkLayout; MinorGridLines.TickmarkLayout = TickmarkLayout; MajorGridLines.Arrange(layoutInfo); MinorGridLines.Arrange(layoutInfo); ArrangeReferenceLines(layoutInfo); } #region ArrangeReferenceLines private void ArrangeReferenceLines(ChartLayoutInfo layoutInfo) { Rectangle layoutBounds = layoutInfo.LayoutBounds; foreach (ReferenceLine line in ReferenceLines) { layoutInfo.LayoutBounds = BoundsRelative; line.Arrange(layoutInfo); } layoutInfo.LayoutBounds = layoutBounds; } #endregion #endregion #region RenderOverride protected override void RenderOverride(ChartRenderInfo renderInfo) { ChartAxisVisualStyle astyle = EffectiveAxisStyle; if (Title.Visible == true) Title.Render(renderInfo); } #endregion #region Render internal override void Render(ChartRenderInfo renderInfo) { if (Displayed == true) { Rectangle bounds = GetScrollBounds(AxisBounds); if (renderInfo.ClipRectangle.IntersectsWith(bounds)) RenderOverride(renderInfo); } } #endregion #region RenderCrosshairLabel internal virtual void RenderCrosshairLabel(Graphics g, Point pt) { } internal virtual void RenderCrosshairLabel(Graphics g, CrosshairPoint cp) { } #region GetCrosshairLabel internal string GetCrosshairLabel(object value, CrosshairValueVisualStyle lstyle) { string label = ""; if (value != null) { TickmarkLabelVisualStyle tstyle = MajorTickmarks.EffectiveLabelStyle; try { switch (ScaleType) { case ScaleType.DateTime: DateTime dtvalue = (DateTime)value; if (string.IsNullOrEmpty(lstyle.TextFormat) == false) label = dtvalue.ToString(lstyle.TextFormat); else if (string.IsNullOrEmpty(tstyle.TextFormat) == false) label = dtvalue.ToString(tstyle.TextFormat); else label = GetDateTimeLabel(dtvalue); break; case ScaleType.Qualitative: if (string.IsNullOrEmpty(lstyle.TextFormat) == false) label = string.Format(lstyle.TextFormat, value); else if (string.IsNullOrEmpty(tstyle.TextFormat) == false) label = string.Format(tstyle.TextFormat, value); else label = value.ToString(); break; default: double d = Convert.ToDouble(value); if (string.IsNullOrEmpty(lstyle.TextFormat) == false) label = d.ToString(lstyle.TextFormat); else if (string.IsNullOrEmpty(tstyle.TextFormat) == false) label = d.ToString(tstyle.TextFormat); else if (value is int) label = ((int)value).ToString(); else label = d.ToString("F6"); break; } } catch { } } ChartControl chartControl = ChartControl; if (chartControl != null) chartControl.DoGetCrosshairAxisLabelEvent(this, value, ref label); return (label); } #endregion #endregion #region RenderBackground internal virtual void RenderBackground( ChartRenderInfo renderInfo, Rectangle scContentBounds) { } #endregion #region RenderStripes internal virtual void RenderStripes( ChartRenderInfo renderInfo, Rectangle scContentBounds) { } #endregion #region RenderStripe protected void RenderStripe( Graphics g, Rectangle bounds, AxisStripe stripe) { if (bounds.Width > 0 && bounds.Height > 0) { AxisStripeVisualStyle astyle = stripe.AxisStripeVisualStyle; if (astyle.Background.IsEmpty == true) { g.FillRectangle(Brushes.LightPink, bounds); } else { using (Brush br = astyle.Background.GetBrush(bounds)) g.FillRectangle(br, bounds); } } } #endregion #region CalcMajorSpacing protected void CalcMajorSpacing(ChartLayoutInfo layoutInfo, int width) { ChartXy chartXy = Parent as ChartXy; if (chartXy.DropShadowDisplayed == true) width -= 3; if (AxisOrientation == AxisOrientation.X) { if (chartXy.VScrollBar.Visible == true) width -= chartXy.VScrollBar.Width; } else { if (chartXy.HScrollBar.Visible == true) width -= chartXy.HScrollBar.Height; } TickmarkLayout layout = new TickmarkLayout(); layout.MinValue = GetMinValue(); layout.MaxValue = GetMaxValue(); if (layout.MinValue != null && layout.MaxValue != null) { ScaleType scaleType = ScaleType; if (ScaleType == ScaleType.NotSet) ScaleType = SeriesPoint.GetScaleType(layout.MinValue.GetType()); layout.MinorCount = MinorTickmarks.TickmarkCount; if (ScaleType != ScaleType.NotSet) { switch (ScaleType) { case ScaleType.DateTime: CalcMajorDateTimeSpacing(chartXy, layout, width); break; case ScaleType.Qualitative: CalcMajorQualitativeSpacing(layout, width); break; default: CalcMajorQuantitativeSpacing(chartXy, layout, width); break; } } if (AxisOrientation == AxisOrientation.Y) { if (chartXy.HScrollBar.Visible == true) layout.MarginOffset -= chartXy.HScrollBarHeight; } } TickmarkLayout = layout; } #region CalcMajorDateTimeSpacing private void CalcMajorDateTimeSpacing( ChartXy chartXy, TickmarkLayout layout, int width) { DateTime dtMinValue = (DateTime)layout.MinValue; DateTime dtMaxValue = (DateTime)layout.MaxValue; DateTime odtMinValue = dtMinValue; if (width > 0 && dtMinValue < dtMaxValue) { DateTime ominValue = dtMinValue; width = SetLayoutMargin(layout, width) - 2; CalcDateTimeSpacing(chartXy, layout, dtMinValue, dtMaxValue, width); if (AutoCalcBarMargins == true) { ChartSeries barSeries = GetBarSeries(chartXy); if (barSeries != null) { if ((barSeries.IsRotated == false && AxisOrientation == AxisOrientation.X) || (barSeries.IsRotated == true && AxisOrientation == AxisOrientation.Y)) { int n = (int)(width / layout.MajorCount / 2 / layout.MajorSpacing); layout.NearMargin += n; layout.FarMargin += n; width -= (n * 2); layout.MajorInterval = (double)width / layout.MajorCount; } } } else { layout.MajorInterval = (double)width / layout.MajorCount; } if (UseAutoMinGridInterval == true) { if (layout.MajorInterval <= 3) layout.MajorInterval = 3; } dtMinValue = AdjustDateTimeBase(layout, dtMinValue); UpdateDateTimeMargins(layout, ref dtMinValue, ref dtMaxValue); double offset = GetDateTimeRange(ActualDateTimeUnits, dtMinValue, ominValue); int moff = (int)((layout.MajorInterval * offset) / layout.MajorSpacing - layout.NearMargin - 1); layout.MarginOffset = moff; layout.MinValue = dtMinValue; layout.MaxValue = dtMaxValue; } } #region AdjustDateTimeBase private DateTime AdjustDateTimeBase(TickmarkLayout layout, DateTime dtValue) { switch (ActualDateTimeUnits) { case DateTimeUnits.Milliseconds: dtValue = dtValue.AddMilliseconds(-dtValue.Millisecond % layout.GridSpacing); break; case DateTimeUnits.Seconds: dtValue = new DateTime(dtValue.Year, dtValue.Month, dtValue.Day, dtValue.Hour, dtValue.Minute, dtValue.Second); dtValue = dtValue.AddSeconds(-dtValue.Second % layout.GridSpacing); break; case DateTimeUnits.Minutes: dtValue = new DateTime(dtValue.Year, dtValue.Month, dtValue.Day, dtValue.Hour, dtValue.Minute, 0); dtValue = dtValue.AddMinutes(-dtValue.Minute % layout.GridSpacing); break; case DateTimeUnits.Hours: dtValue = new DateTime(dtValue.Year, dtValue.Month, dtValue.Day, dtValue.Hour, 0, 0); dtValue = dtValue.AddHours(-dtValue.Hour % layout.GridSpacing); break; case DateTimeUnits.Days: dtValue = new DateTime(dtValue.Year, dtValue.Month, dtValue.Day); dtValue = dtValue.AddDays(-dtValue.Day % layout.GridSpacing); break; case DateTimeUnits.Months: dtValue = new DateTime(dtValue.Year, dtValue.Month, 1); dtValue = dtValue.AddMonths(-(int)(dtValue.Month % layout.GridSpacing)); break; case DateTimeUnits.Years: dtValue = new DateTime(dtValue.Year, 1, 1); dtValue = dtValue.AddYears(-(int)(dtValue.Year % layout.GridSpacing)); break; default: dtValue = dtValue.AddTicks(-(long)(dtValue.Millisecond % layout.GridSpacing)); break; } return (dtValue); } #endregion #region CalcDateTimeSpacing private void CalcDateTimeSpacing(ChartXy chartXy, TickmarkLayout layout, DateTime dtMinValue, DateTime dtMaxValue, int width) { DateTimeUnits adtu = DateTimeUnits; double range = GetDateTimeRange(adtu, dtMinValue, dtMaxValue); if (GridSpacing <= 0) { layout.GridSpacing = 1; layout.MajorSpacing = layout.GridSpacing; } else { layout.GridSpacing = GridSpacing; layout.MajorSpacing = layout.GridSpacing; } CalcDateTimeInterval(layout, range, width); double minorInterval = layout.MajorInterval / (MinorTickmarks.TickmarkCount + 1); if (minorInterval > 0) { ChartSeries barSeries = GetBarSeries(chartXy); while (minorInterval < MinGridInterval) { if ((barSeries == null) && (int)adtu < (int)DateTimeUnitsLimit) { adtu = GetNextDateTimeUnit(adtu, dtMinValue, dtMaxValue); range = GetDateTimeRange(adtu, dtMinValue, dtMaxValue); CalcDateTimeInterval(layout, range, width); minorInterval = layout.MajorInterval / (MinorTickmarks.TickmarkCount + 1); } else { if (GridSpacing > 0) break; minorInterval *= 2; layout.MajorSpacing *= 2; } } ActualDateTimeUnits = adtu; CalcDateTimeInterval(layout, range, width); } } #region CalcDateTimeInterval private void CalcDateTimeInterval(TickmarkLayout layout, double range, int width) { double d = range / layout.MajorSpacing; layout.MajorCount = (int)Math.Ceiling(d); layout.MajorInterval = (double)width / d; if (layout.MajorCount <= 0) layout.MajorCount = 1; } #endregion #region GetNextDateTimeUnit private DateTimeUnits GetNextDateTimeUnit( DateTimeUnits adtu, DateTime dtMinValue, DateTime dtMaxValue) { switch (adtu) { case DateTimeUnits.Ticks: return (DateTimeUnits.Milliseconds); case DateTimeUnits.Milliseconds: return (DateTimeUnits.Seconds); case DateTimeUnits.Seconds: return (DateTimeUnits.Minutes); case DateTimeUnits.Minutes: return (DateTimeUnits.Hours); case DateTimeUnits.Hours: return (DateTimeUnits.Days); case DateTimeUnits.Days: return (DateTimeUnits.Months); default: return (DateTimeUnits.Years); } } #endregion #endregion #region AddDateTimeOffset private DateTime AddDateTimeOffset(DateTime dtValue, int offset) { switch (ActualDateTimeUnits) { case DateTimeUnits.Milliseconds: dtValue = dtValue.AddMilliseconds(offset); break; case DateTimeUnits.Seconds: dtValue = dtValue.AddSeconds(offset); break; case DateTimeUnits.Minutes: dtValue = dtValue.AddMinutes(offset); break; case DateTimeUnits.Hours: dtValue = dtValue.AddHours(offset); break; case DateTimeUnits.Days: dtValue = dtValue.AddDays(offset); break; case DateTimeUnits.Months: dtValue = dtValue.AddMonths(offset); break; case DateTimeUnits.Years: dtValue = dtValue.AddYears(offset); break; default: dtValue = dtValue.AddTicks(offset); break; } return (dtValue); } #endregion #region UpdateDateTimeMargins private void UpdateDateTimeMargins( TickmarkLayout layout, ref DateTime dtMinValue, ref DateTime dtMaxValue) { if (layout.NearMargin > 0) { int marginCount = (int)Math.Ceiling(layout.NearMargin / layout.MajorInterval); layout.MajorCount += marginCount; dtMinValue = AddDateTimeOffset(dtMinValue, -(int)(marginCount * 1)); //layout.MajorSpacing)); } if (layout.FarMargin > 0) { int marginCount = (int)Math.Ceiling(layout.FarMargin / layout.MajorInterval); layout.MajorCount += marginCount; dtMaxValue = AddDateTimeOffset(dtMaxValue, (int)(marginCount * 1)); //layout.MajorSpacing)); } } #endregion #region GetDateTimeRange private double GetDateTimeRange( DateTimeUnits dtu, DateTime dtMinValue, DateTime dtMaxValue) { TimeSpan ts = dtMaxValue - dtMinValue; switch (dtu) { 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 (CalcDateTimeMonths(dtMinValue, dtMaxValue)); case DateTimeUnits.Years: return (CalcDateTimeYears(dtMinValue, dtMaxValue)); default: return (dtMaxValue.Ticks - dtMinValue.Ticks); } } #region CalcDateTimeYears internal double CalcDateTimeYears(DateTime dtMinValue, DateTime dtMaxValue) { int years = dtMaxValue.Year - dtMinValue.Year; DateTime last = dtMinValue.AddYears(years); if (last > dtMaxValue) { last = last.AddYears(-1); years--; } DateTime next = last.AddYears(1); double yearDays = (next - last).Days; double days = (dtMaxValue - last).Days; return ((double)years + (days / yearDays)); } #endregion #region CalcDateTimeMonths internal double CalcDateTimeMonths(DateTime dtMinValue, DateTime dtMaxValue) { // Rough estimation... TimeSpan ts = dtMaxValue - dtMinValue; return ((double)ts.Days / 30); } #endregion #endregion #endregion #region CalcMajorQuantitativeSpacing private void CalcMajorQuantitativeSpacing( ChartXy chartXy, TickmarkLayout layout, int width) { Type otype = layout.MinValue.GetType(); double dminValue = Convert.ToDouble(layout.MinValue); double dmaxValue = Convert.ToDouble(layout.MaxValue); if (AutoCalcBarMargins == true) { ChartSeries barSeries = GetFullBarSeries(chartXy); if (barSeries != null) { if (barSeries.IsRotated == true && AxisOrientation == AxisOrientation.X || barSeries.IsRotated == false && AxisOrientation == AxisOrientation.Y) { double origin = Convert.ToDouble((chartXy.BarOrigin ?? 0)); dminValue = Math.Min(origin, dminValue); } } } double ominValue = dminValue; if (dminValue == dmaxValue) dmaxValue++; if (width > 0 && dminValue <= dmaxValue) { width = SetLayoutMargin(layout, width); double range = Math.Abs(dmaxValue - dminValue); CalcQuantitativeSpacing(chartXy, layout, range, width); if (UseAutoMinGridInterval == true) { if (layout.MajorInterval < 3) layout.MajorInterval = 3; } UpdateQuantitativeMargins(layout, otype, ref dminValue, ref dmaxValue); // Make sure zero is included on the axis int n = dminValue < 0 ? -1 : 1; if (dminValue < 0) dminValue -= ((dminValue % layout.MajorSpacing) + layout.MajorSpacing); else if (dminValue > 0) dminValue -= (dminValue % layout.MajorSpacing); int moff = 0; double delta = Math.Floor(Math.Abs(dminValue) / layout.MajorSpacing); double value = (int)delta * layout.MajorSpacing * n; double offset = Math.Abs(value - ominValue); moff = (int)((layout.MajorInterval * offset) / layout.MajorSpacing - layout.NearMargin); layout.MarginOffset = moff; layout.MinValue = Convert.ChangeType(dminValue, otype); layout.MaxValue = Convert.ChangeType(dmaxValue, otype); } } #region CalcQuantitativeSpacing void CalcQuantitativeSpacing( ChartXy chartXy, TickmarkLayout layout, double range, int width) { if (GridSpacing <= 0) { // User has not specified a spacing, so calculate it for them based // upon powers of 10 (10, 100, 1000, etc) // // MajorSpacing is set to a multiple of the GridSpacing (based upon // the calculated minor tickmark interval) layout.GridSpacing = Math.Pow(10, Math.Floor(Math.Log10(range / 10))); if (layout.GridSpacing < 1) layout.GridSpacing = 1; if (layout.MinValue is int) layout.GridSpacing = Math.Ceiling(layout.GridSpacing); layout.MajorSpacing = layout.GridSpacing; layout.MajorCount = (int)Math.Ceiling(range / layout.MajorSpacing); layout.MajorInterval = (double)width / layout.MajorCount; } else { layout.GridSpacing = GridSpacing; layout.MajorSpacing = layout.GridSpacing; layout.MajorCount = (int)Math.Ceiling(range / layout.MajorSpacing); layout.MajorInterval = (double)width / layout.MajorCount; } double gridInterval = GridInterval; double pxp = (range < 1) ? width : ((double)width / range); ChartSeries dpSeries = GetChartDotPlotSeries(chartXy); if (dpSeries != null) { // Calculate the default dot plot spacing gridInterval = CalcDotPlotSpacing( chartXy, layout, range, ref width, dpSeries); double count = range / layout.MajorSpacing; layout.MajorCount = (int)Math.Ceiling(count); pxp = (double)width / range; DotPlotAxis = true; } else { ChartSeries barSeries = GetBarSeries(chartXy); if (barSeries != null) { if ((barSeries.IsRotated == false && AxisOrientation == AxisOrientation.X) || (barSeries.IsRotated == true && AxisOrientation == AxisOrientation.Y)) { int n = (int)(pxp / 2) + 1; layout.NearMargin += n; layout.FarMargin += n; width -= (n * 2); layout.MajorInterval = (double)width / layout.MajorCount; pxp = (double)width / (range); } } double minorInterval = layout.MajorInterval / (MinorTickmarks.TickmarkCount + 1); if (minorInterval > 0) { while (minorInterval < MinGridInterval) { minorInterval *= 2; layout.MajorSpacing *= 2; } } gridInterval = layout.MajorInterval; double dminValue = Convert.ToDouble(layout.MinValue); double dmaxValue = Convert.ToDouble(layout.MaxValue); double maxp = (int)(dmaxValue / layout.MajorSpacing) * layout.MajorSpacing; if (maxp < dmaxValue) maxp += layout.MajorSpacing; double minp = (int)(dminValue / layout.MajorSpacing) * layout.MajorSpacing; if (minp > dminValue) minp -= layout.MajorSpacing; layout.MajorCount = (int)Math.Ceiling((maxp - minp) / layout.MajorSpacing); layout.MinValue = minp; layout.MaxValue = maxp; DotPlotAxis = false; } if (GridInterval > 0) { layout.MajorInterval = GridInterval; } else { if (AxisCanExpand(dpSeries) == true) gridInterval = Math.Max(gridInterval, pxp * layout.MajorSpacing); layout.MajorInterval = Math.Max(gridInterval, 1); } } #region GetChartDotPlotSeries private ChartSeries GetChartDotPlotSeries(ChartXy chartXy) { foreach (ChartSeries series in chartXy.ChartSeries) { if ((series.SeriesType == SeriesType.VerticalDot) || (series.SeriesType == SeriesType.HorizontalDot)) { return (series); } } return (null); } #endregion #region CalcDotPlotSpacing private double CalcDotPlotSpacing(ChartXy chartXy, TickmarkLayout layout, double range, ref int width, ChartSeries dpSeries) { // If we previously added a default margin, back it back out // since we are going to adjust the margin based upon the orientation // and size of the marker if (AxisMargins < 0) { width += ((layout.NearMargin * 2) - Dpi.Width10); layout.NearMargin = Dpi.Width5; } Size dotSize = chartXy.GetMaxDotPlotMarkerSize(); DotPlotType dotTypes = chartXy.GetDotPlotTypes(); return (AxisOrientation == AxisOrientation.X) ? CalcXDotPlotSpacing(chartXy, layout, range, ref width, dpSeries, dotTypes, dotSize) : CalcYDotPlotSpacing(chartXy, layout, range, ref width, dpSeries, dotTypes, dotSize); } #region CalcXDotPlotSpacing private double CalcXDotPlotSpacing(ChartXy chartXy, TickmarkLayout layout, double range, ref int width, ChartSeries dpSeries, DotPlotType dotTypes, Size dotSize) { if ((dotTypes & DotPlotType.Vertical) == DotPlotType.Vertical) { int n = dotSize.Width / 2; layout.NearMargin += n; width -= (n * 2); } layout.MajorSpacing = (GridSpacing <= 0) ? 1 : GridSpacing; double gridInterval = Math.Max(layout.MajorSpacing, 1) * dotSize.Width; if (MinGridInterval > 0) { while (gridInterval < Dpi.Width(MinGridInterval)) { gridInterval *= 2; layout.MajorSpacing *= 2; } } Size size = chartXy.MinQualitativeSize; double count = range; if ((dotTypes & DotPlotType.Vertical) == DotPlotType.Vertical) count = range / dpSeries.DotPlotIndexValue; size.Width = (int)(dotSize.Width * count + layout.NearMargin * 2); if (chartXy.VScrollBar.Enabled == true) size.Width += chartXy.VScrollBar.Width; chartXy.MinQualitativeSize = size; return (gridInterval); } #endregion #region CalcYDotPlotSpacing private double CalcYDotPlotSpacing(ChartXy chartXy, TickmarkLayout layout, double range, ref int width, ChartSeries dpSeries, DotPlotType dotTypes, Size dotSize) { if ((dotTypes & DotPlotType.Horizontal) == DotPlotType.Horizontal) { int n = dotSize.Height / 2; layout.NearMargin += n; width -= (n * 2); } layout.MajorSpacing = (GridSpacing <= 0) ? 1 : GridSpacing; double gridInterval = layout.MajorSpacing * dotSize.Height; if (MinGridInterval > 0) { while (gridInterval < MinGridInterval) { gridInterval *= 2; layout.MajorSpacing *= 2; } } Size size = chartXy.MinQualitativeSize; double count = range; if ((dotTypes & DotPlotType.Horizontal) == DotPlotType.Horizontal) count = range / dpSeries.DotPlotIndexValue; size.Height = (int)(dotSize.Height * count + layout.NearMargin * 2); if (chartXy.HScrollBar.Enabled == true) size.Height += chartXy.HScrollBar.Height; chartXy.MinQualitativeSize = size; return (gridInterval); } #endregion #endregion #region AxisCanExpand private bool AxisCanExpand(ChartSeries dpSeries) { return ((dpSeries == null) || (AxisOrientation == AxisOrientation.X && dpSeries.SeriesType == SeriesType.VerticalDot) || (AxisOrientation == AxisOrientation.Y && dpSeries.SeriesType == SeriesType.HorizontalDot)); } #endregion #endregion #region UpdateQuantitativeMargins private void UpdateQuantitativeMargins( TickmarkLayout layout, Type otype, ref double dminValue, ref double dmaxValue) { if (layout.NearMargin > 0) { int marginCount = (int)Math.Ceiling(layout.NearMargin / layout.MajorInterval); layout.MajorCount += marginCount; if (otype == typeof(Int32)) { if (dminValue - (marginCount * layout.MajorSpacing) > Int32.MinValue) dminValue -= (marginCount * layout.MajorSpacing); } else { dminValue -= (marginCount * layout.MajorSpacing); } } if (layout.FarMargin > 0) { int marginCount = (int)Math.Ceiling(layout.FarMargin / layout.MajorInterval); layout.MajorCount += marginCount; if (otype == typeof(Int32)) { if (dmaxValue + (marginCount * layout.MajorSpacing) < Int32.MaxValue) dmaxValue += (marginCount * layout.MajorSpacing); } else { dmaxValue += (marginCount * layout.MajorSpacing); } } } #endregion #endregion #region CalcMajorQualitativeSpacing private void CalcMajorQualitativeSpacing(TickmarkLayout layout, int width) { int iminValue = (int)layout.MinValue; int imaxValue = (int)layout.MaxValue; int ominValue = iminValue; if (width > 0 && iminValue <= imaxValue) { width = SetLayoutMargin(layout, width); double range = Math.Abs(imaxValue - iminValue) + 1; CalcQualitativeSpacing(layout, range, width); if (UseAutoMinGridInterval == true) { if (layout.MajorInterval <= 3) layout.MajorInterval = 3; } UpdateQualitativeMargins(layout, ref iminValue, ref imaxValue); // Make sure zero is included on the axis iminValue -= (int)(iminValue % layout.MajorSpacing); int moff = 0; int n = iminValue < 0 ? -1 : 1; double delta = Math.Floor(Math.Abs(iminValue) / layout.GridSpacing); double value = (int)delta * layout.GridSpacing * n; double offset = Math.Abs(value - ominValue); moff = (int)((layout.MajorInterval * offset) / layout.MajorSpacing - layout.NearMargin); layout.MarginOffset = moff; layout.MinValue = iminValue; layout.MaxValue = imaxValue; } } #region CalcQualitativeSpacing private void CalcQualitativeSpacing( TickmarkLayout layout, double range, int width) { ChartXy chartXy = Parent as ChartXy; layout.GridSpacing = 1; layout.MajorSpacing = layout.GridSpacing; layout.MajorCount = (int)range; layout.MajorInterval = (double)width / layout.MajorCount; if (layout.MinorCount > 0) { double minorInterval = layout.MajorInterval / (layout.MinorCount + 1); if (minorInterval > 0) { while (minorInterval < MinGridInterval) { minorInterval *= 2; layout.MinorCount /= 2; } } } List groupIds = GetSeriesGroupIds(chartXy); int colWidth = int.MinValue; foreach (int groupId in groupIds) { int n = chartXy.GetQualitativeColumnWidth(this, groupId); if (n > colWidth) colWidth = n; } if (UseAutoMinGridInterval == false) { if (colWidth > layout.MajorInterval) colWidth = (int)layout.MajorInterval; } if (colWidth < MinGridInterval) colWidth = MinGridInterval; int minWidth = colWidth * (layout.MajorCount); if (AxisOrientation == AxisOrientation.X) { if (chartXy.MinQualitativeSize.Width != minWidth) { Size size = chartXy.MinQualitativeSize; size.Width = minWidth + layout.NearMargin; chartXy.MinQualitativeSize = size; // Force a layout change when the next // layout invalidation arrives LastTickmarkLayout = null; } } else { if (chartXy.MinQualitativeSize.Height != minWidth) { Size size = chartXy.MinQualitativeSize; size.Height = minWidth + layout.NearMargin; chartXy.MinQualitativeSize = size; LastTickmarkLayout = null; } } width += layout.NearMargin; minWidth = Math.Max(width, minWidth) - layout.NearMargin; layout.MajorInterval = (double)minWidth / layout.MajorCount; if (layout.MajorInterval < MinGridInterval) { layout.MajorInterval = MinGridInterval; minWidth = (int)(layout.MajorInterval * (layout.MajorCount - 0)); } layout.MajorInterval = (double)minWidth / (layout.MajorCount - 0); foreach (int groupId in groupIds) { chartXy.AdjustQualitativeOffsets(this, groupId, colWidth, (int)(layout.MajorInterval)); } layout.NearMargin += (int)(layout.MajorInterval / 2); } #region GetSeriesGroupIds private List GetSeriesGroupIds(ChartXy chartXy) { List groupIds = new List(); foreach (ChartSeries series in chartXy.ChartSeries) { if (groupIds.Contains(series.GroupId) == false) groupIds.Add(series.GroupId); } return (groupIds); } #endregion #endregion #region UpdateQualitativeMargins private void UpdateQualitativeMargins( TickmarkLayout layout, ref int iminValue, ref int imaxValue) { if (layout.NearMargin > 0) { int marginCount = (int)Math.Ceiling(layout.NearMargin / layout.MajorInterval); layout.MajorCount += marginCount; iminValue -= (int)(marginCount * layout.MajorSpacing); } if (layout.FarMargin > 0) { int marginCount = (int)Math.Ceiling(layout.FarMargin / layout.MajorInterval); layout.MajorCount += marginCount; imaxValue += (int)(marginCount * layout.MajorSpacing); } } #endregion #endregion #region SetLayoutMargin private int SetLayoutMargin(TickmarkLayout layout, int width) { layout.NearMargin = (AxisNearMargin >= 0) ? AxisNearMargin : AxisMargins >= 0 ? AxisMargins : 20; layout.FarMargin = (AxisFarMargin >= 0) ? AxisFarMargin : AxisMargins >= 0 ? AxisMargins : 20; layout.NearMargin = Dpi.Width(layout.NearMargin); layout.FarMargin = Dpi.Width(layout.FarMargin); layout.NearMargin = Math.Min(layout.NearMargin, width / 3); layout.FarMargin = Math.Min(layout.FarMargin, width / 3); if (layout.NearMargin > 0) width -= (layout.NearMargin + 1); if (layout.FarMargin > 0) width -= (layout.FarMargin + 1); return (width); } #endregion #region GetBarSeries private ChartSeries GetBarSeries(ChartXy chartXy) { foreach (ChartSeries series in chartXy.ChartSeries) { if (series.Visible == true) { if (series.IsBarSeries == true) return (series); } } return (null); } #endregion #region GetFullBarSeries private ChartSeries GetFullBarSeries(ChartXy chartXy) { foreach (ChartSeries series in chartXy.ChartSeries) { if (series.Visible == true) { if (series.SeriesType == SeriesType.HorizontalBar || series.SeriesType == SeriesType.VerticalBar) { if (AxisOrientation == AxisOrientation.X) { if (series.AxisX == this || (series.AxisX == null && chartXy.AxisX == this)) return (series); } else { if (series.AxisY == this || (series.AxisY == null && chartXy.AxisY == this)) return (series); } } } } return (null); } #endregion #endregion #region GetMaxTickMarkLength protected int GetMaxTickMarkLength(bool inner) { int majorLength = MajorTickmarks.GetMaxTickmarkLength(this, inner); int minorLength = MinorTickmarks.GetMaxTickmarkLength(this, inner); int maxlen = Math.Max(majorLength, minorLength); return (maxlen); } #endregion #region GetAxisAlignment internal AxisAlignment GetAxisAlignment() { if (AxisAlignment == AxisAlignment.NotSet) return (IsPrimaryAxis ? AxisAlignment.Near : AxisAlignment.Far); return (AxisAlignment); } #endregion #region GetMinValue internal object GetMinValue() { UpdateRangeValues(); if (MinValue != null) { if (ScaleType != ScaleType.NotSet) { if (SeriesPoint.GetScaleType(MinValue.GetType()) != ScaleType) { if (IsDesignerHosted == false) throw new Exception("MinValue ScaleType must match Axis ScaleType"); return (ActualMinValue); } } return (MinValue); } return (ActualMinValue); } #endregion #region GetMaxValue internal object GetMaxValue() { UpdateRangeValues(); if (MaxValue != null) { if (ScaleType != ScaleType.NotSet) { if (SeriesPoint.GetScaleType(MaxValue.GetType()) != ScaleType) { if (IsDesignerHosted == false) throw new Exception("MaxValue ScaleType must match Axis ScaleType"); return (ActualMaxValue); } } return (MaxValue); } return (ActualMaxValue); } #endregion #region UpdateRangeValues internal void UpdateRangeValues() { if (SeriesRangeChanged == true) { SeriesRangeChanged = false; ActualMinValue = null; ActualMaxValue = null; QualitativeValues.Clear(); ChartXy chartXy = Parent as ChartXy; if (chartXy != null) { if (chartXy.ChartSeries.Count > 0) { UpdateRangeValues(chartXy); if (QualitativeValues.Count > 1) { if (QualitativeSortDirection != SortDirection.None) { QualitativeValues.Sort(new ObjComparer(this, QualitativeSortDirection == SortDirection.Ascending)); } } } } } } internal virtual void UpdateRangeValues(ChartXy chartXy) { } #region ObjComparer private class ObjComparer : IComparer { private ChartVisualElement _ChartElement; private ChartControl _ChartControl; private bool _Ascending; public ObjComparer(ChartVisualElement chartElement, bool ascending) { _ChartElement = chartElement; _ChartControl = chartElement.ChartControl; _Ascending = ascending; } public int Compare(object v1, object v2) { int sval = 0; if (_ChartControl.DoCompareElementsEvent(_ChartElement, v1, v2, ref sval) == true) return (sval); if (v1 is IComparable) { sval = ((IComparable)v1).CompareTo(v2); if (sval != 0) return (_Ascending == true) ? sval : -sval; } return (sval); } } #endregion #endregion #region ValidateAxis internal bool ValidateAxis() { bool altered = false; if (LastTickmarkLayout == null || TickmarkLayout == null) altered = true; else if ((TickmarkLayout.MinValue != LastTickmarkLayout.MinValue) || (TickmarkLayout.MaxValue != LastTickmarkLayout.MaxValue) || (TickmarkLayout.MajorInterval != LastTickmarkLayout.MajorInterval) || (TickmarkLayout.MajorSpacing != LastTickmarkLayout.MajorSpacing) || (TickmarkLayout.MarginOffset != LastTickmarkLayout.MarginOffset)) { altered = true; } LastTickmarkLayout = TickmarkLayout; return (altered); } #endregion #region GetChartPoint protected Point GetChartPoint(Point pt) { ChartXy chartXy = Parent as ChartXy; pt.X += ScrollOffset.X; pt.X -= (chartXy.HScrollBar.Inverted == true ? chartXy.HScrollOffset : -chartXy.HScrollOffset); pt.Y += ScrollOffset.Y; pt.Y -= (chartXy.VScrollBar.Inverted == true ? chartXy.VScrollOffset : -chartXy.VScrollOffset); return (pt); } #endregion #region GetDisplayValue virtual internal int GetDisplayValue(object axisValue) { return (0); } #endregion #region GetDateTime internal DateTime GetDateTime(DateTime dt, double delta) { switch (ActualDateTimeUnits) { case DateTimeUnits.Milliseconds: return (dt.AddMilliseconds(delta)); case DateTimeUnits.Seconds: return (dt.AddSeconds(delta)); case DateTimeUnits.Minutes: return (dt.AddMinutes(delta)); case DateTimeUnits.Hours: return (dt.AddHours(delta)); case DateTimeUnits.Days: return (dt.AddDays(delta)); case DateTimeUnits.Months: dt = dt.AddMonths((int)delta); dt = dt.AddDays((delta - (int)delta) * 31); return (dt); case DateTimeUnits.Years: return (dt.AddYears((int)delta)); default: return (dt.AddTicks((long)delta)); } } #endregion #region GetDateTimeValue internal long GetDateTimeValue(DateTime dtValue) { switch (ActualDateTimeUnits) { case DateTimeUnits.Milliseconds: return (dtValue.Millisecond); case DateTimeUnits.Seconds: return (dtValue.Second); case DateTimeUnits.Minutes: return (dtValue.Minute); case DateTimeUnits.Hours: return (dtValue.Hour); case DateTimeUnits.Days: return (dtValue.Day); case DateTimeUnits.Months: return (dtValue.Month); case DateTimeUnits.Years: return (dtValue.Year); default: return (dtValue.Ticks); } } #endregion #region GetDateTimePointValue internal DateTime GetDateTimePointValue(TickmarkLayout layout, int dz) { double delta = (double)(layout.MajorSpacing * ((dz + layout.MarginOffset) / layout.MajorInterval)); return (GetDateTime((DateTime)layout.BaseValue, delta)); } #endregion #region GetDateTimeLabel internal string GetDateTimeLabel(DateTime dt) { switch (ActualDateTimeUnits) { case DateTimeUnits.Ticks: return (dt.Ticks.ToString()); case DateTimeUnits.Milliseconds: return (dt.Millisecond.ToString()); case DateTimeUnits.Seconds: return (dt.Second.ToString()); case DateTimeUnits.Minutes: return (dt.Minute.ToString()); case DateTimeUnits.Hours: return (dt.ToShortTimeString()); case DateTimeUnits.Days: return (dt.ToShortDateString()); case DateTimeUnits.Months: return (dt.Month + "/" + dt.Year); default: return (dt.Year.ToString()); } } #endregion #region GetTickmarkAltValue internal double GetTickmarkAltValue(TickmarkTick tick) { switch (ScaleType) { case ScaleType.DateTime: DateTime dt = AddDateTimeOffset((DateTime)tick.Value, (int)(TickmarkLayout.MajorSpacing / 2)); return (GetDateTimeValue(dt)); case ScaleType.Qualitative: return ((int)tick.Value + TickmarkLayout.MajorSpacing / 2); default: return (Convert.ToDouble(tick.Value) + TickmarkLayout.MajorSpacing / 2); } } #endregion #region GetLegendData internal void GetLegendData(List legendData) { foreach (ReferenceLine line in ReferenceLines) { if (line.Visible == true) { List items = line.GetLegendItems(); if (items != null && items.Count > 0) legendData.AddRange(items); } } foreach (AxisStripe stripe in AxisStripes) { if (stripe.Visible == true) { List items = stripe.GetLegendItems(); if (items != null && items.Count > 0) legendData.AddRange(items); } } } #endregion #region GetPointFromValue /// /// Gets the chart Point from the given data value. /// /// /// public virtual Point GetPointFromValue(object value) { return (Point.Empty); } #endregion #region GetValueFromPoint /// /// Gets the "Data" value from the given Chart Point. /// /// /// public object GetValueFromPoint(Point pt) { TickmarkLayout layout = TickmarkLayout; return (GetValueFromPoint(layout, pt)); } internal virtual object GetValueFromPoint(TickmarkLayout layout, Point pt) { return (null); } #endregion #region GetReferenceLineByName /// /// Gets the ReferenceLine from the given name. /// /// /// public ReferenceLine GetReferenceLineByName(string name) { if (String.IsNullOrEmpty(name) == true) return (null); if (_ReferenceLines != null) { foreach (ReferenceLine line in _ReferenceLines) { if (name.Equals(line.Name) == true) return (line); } } return (null); } #endregion #region GetStripeByName /// /// Gets the AxisStripe from the given name. /// /// /// public AxisStripe GetStripeByName(string name) { if (String.IsNullOrEmpty(name) == true) return (null); if (_AxisStripes != null) { foreach (AxisStripe stripe in _AxisStripes) { if (name.Equals(stripe.Name) == true) return (stripe); } } return (null); } #endregion #region EnsureVisible /// /// Ensures the given data value is visible on screen. /// /// public void EnsureVisible(object value) { EnsureVisible(value, false); } /// /// Ensures the given data value is visible and /// optionally centered on screen. /// /// public virtual void EnsureVisible(object value, bool center) { } #endregion #region Style Support #region ApplyStyles public override void ApplyStyles(BaseVisualStyle style) { ChartAxisVisualStyle astyle = style as ChartAxisVisualStyle; if (astyle != null) { ApplyParentStyles(astyle, Parent as ChartContainer); astyle.ApplyStyle(ChartAxisVisualStyle); astyle.ApplyDefaults(); } else if (style is CrosshairValueVisualStyle) { CrosshairValueVisualStyle cstyle = (CrosshairValueVisualStyle)style; ApplyParentStyles(cstyle, Parent as ChartContainer); cstyle.ApplyStyle(CrosshairLabelVisualStyle); cstyle.ApplyDefaults(); } } #region ApplyParentStyles (ChartAxisVisualStyle) private void ApplyParentStyles( ChartAxisVisualStyle astyle, ChartContainer item) { if (item != null) { ApplyParentStyles(astyle, item.Parent as ChartContainer); ChartPanel cp = item as ChartPanel; if (cp != null) { if (_AxisOrientation == AxisOrientation.X) astyle.ApplyStyle(cp.DefaultVisualStyles.ChartXAxisVisualStyle); else astyle.ApplyStyle(cp.DefaultVisualStyles.ChartYAxisVisualStyle); } } else { if (_AxisOrientation == AxisOrientation.X) { astyle.ApplyStyle(ChartControl.BaseVisualStyles.ChartXAxisVisualStyle); astyle.ApplyStyle(ChartControl.DefaultVisualStyles.ChartXAxisVisualStyle); } else { astyle.ApplyStyle(ChartControl.BaseVisualStyles.ChartYAxisVisualStyle); astyle.ApplyStyle(ChartControl.DefaultVisualStyles.ChartYAxisVisualStyle); } } } #endregion #region ApplyParentStyles (CrosshairLabelVisualStyle) private void ApplyParentStyles( CrosshairValueVisualStyle pstyle, ChartContainer item) { if (item != null) { ApplyParentStyles(pstyle, item.Parent as ChartContainer); if (item is ChartPanel) pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.CrosshairLabelVisualStyle); else if (item is ChartXy) pstyle.ApplyStyle(((ChartXy)item).ChartCrosshair.CrosshairLabelVisualStyle); } else { pstyle.ApplyStyle(ChartControl.BaseVisualStyles.CrosshairLabelVisualStyle); pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.CrosshairLabelVisualStyle); } } #endregion #endregion #region InvalidateStyle /// ///Invalidate the cached Styles /// public void InvalidateStyle() { ClearEffectiveStyles(); } #endregion #region ClearEffectiveStyles protected override void ClearEffectiveStyles() { base.ClearEffectiveStyles(); _EffectiveAxisStyle.InvalidateStyle(); } #endregion #endregion #region InvalidateRender /// /// Invalidate the axis for rendering purposes. /// public override void InvalidateRender() { base.InvalidateRender(); ChartXy chartXy = Parent as ChartXy; if (chartXy != null) chartXy.InvalidateRender(); } #endregion #region RefreshRangeValues /// /// Causes an invalidation and refresh of the range values for the axis. /// public void RefreshRangeValues() { SeriesRangeChanged = true; InvalidateLayout(); } #endregion #region Copy/CopyTo /// /// Copies the axis elements to the given "copy" /// /// public override void CopyTo(ChartVisualElement copy) { ChartAxis c = copy as ChartAxis; if (c != null) { base.CopyTo(c); c.AxisAlignment = AxisAlignment; c.AxisFarMargin = AxisFarMargin; c.AxisMargins = AxisMargins; c.AxisNearMargin = AxisNearMargin; foreach (AxisStripe stripe in AxisStripes) c.AxisStripes.Add((AxisStripe)stripe.Copy()); c.ChartAxisVisualStyle = (_ChartAxisVisualStyle != null) ? ChartAxisVisualStyle.Copy() : null; c.CrosshairLabelVisualStyle = (_CrosshairLabelVisualStyle != null) ? CrosshairLabelVisualStyle.Copy() : null; c.DateTimeUnits = DateTimeUnits; c.GridInterval = GridInterval; c.GridSpacing = GridSpacing; c.MinGridInterval = MinGridInterval; c.MaxValue = MaxValue; c.MinValue = MinValue; foreach (ReferenceLine line in ReferenceLines) c.ReferenceLines.Add((ReferenceLine)line.Copy()); Title.CopyTo(c.Title); c.UseAlternateBackground = UseAlternateBackground; } } #endregion #region GetSerialData internal override SerialElementCollection GetSerialData(string serialName) { SerialElementCollection sec = new SerialElementCollection(); if (serialName != null) { if (serialName.Equals("") == true) serialName = "ChartAxis"; sec.AddStartElement(serialName); } sec.AddValue("AxisAlignment", AxisAlignment, AxisAlignment.NotSet); sec.AddValue("AxisFarMargin", AxisFarMargin, -1); sec.AddValue("AxisMargins", AxisMargins, -1); sec.AddValue("AxisNearMargin", AxisNearMargin, -1); if (_AxisStripes != null && _AxisStripes.Count > 0) { sec.AddStartElement("AxisStripes count=\"" + _AxisStripes.Count + "\""); foreach (AxisStripe stripe in _AxisStripes) sec.AddElement(stripe.GetSerialData("")); sec.AddEndElement("AxisStripes"); } if (_ChartAxisVisualStyle != null && _ChartAxisVisualStyle.IsEmpty == false) sec.AddElement(_ChartAxisVisualStyle.GetSerialData("ChartAxisVisualStyle")); if (_CrosshairLabelVisualStyle != null && _CrosshairLabelVisualStyle.IsEmpty == false) sec.AddElement(_CrosshairLabelVisualStyle.GetSerialData("CrosshairLabelVisualStyle")); sec.AddValue("DateTimeUnits", DateTimeUnits, DateTimeUnits.Days); sec.AddValue("GridInterval", GridInterval, 0.0d); sec.AddValue("GridSpacing", GridSpacing, 0.0d); sec.AddValue("MinGridInterval", MinGridInterval, 10); sec.AddDataValue("MaxValue", MaxValue, null); sec.AddDataValue("MinValue", MinValue, null); sec.AddValue("QualitativeSortDirection", QualitativeSortDirection, SortDirection.None); if (_ReferenceLines != null && _ReferenceLines.Count > 0) { sec.AddStartElement("ReferenceLines count=\"" + _ReferenceLines.Count + "\""); foreach (ReferenceLine rline in _ReferenceLines) sec.AddElement(rline.GetSerialData("")); sec.AddEndElement("ReferenceLines"); } if (_Title != null) sec.AddElement(_Title.GetSerialData("Title")); sec.AddValue("UseAlternateBackground", UseAlternateBackground, false); 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 "AxisAlignment": AxisAlignment = (AxisAlignment)se.GetValueEnum(typeof(AxisAlignment)); break; case "AxisFarMargin": AxisFarMargin = int.Parse(se.StringValue); break; case "AxisMargins": AxisMargins = int.Parse(se.StringValue); break; case "AxisNearMargin": AxisNearMargin = int.Parse(se.StringValue); break; case "DateTimeUnits": DateTimeUnits = (DateTimeUnits)se.GetValueEnum(typeof(DateTimeUnits)); break; case "GridInterval": GridInterval = double.Parse(se.StringValue); break; case "GridSpacing": GridSpacing = double.Parse(se.StringValue); break; case "MinGridInterval": MinGridInterval = int.Parse(se.StringValue); break; case "MaxValue": MaxValue = se.DataValue; break; case "MinValue": MinValue = se.DataValue; break; case "QualitativeSortDirection": QualitativeSortDirection = (SortDirection)se.GetValueEnum(typeof(SortDirection)); break; case "UseAlternateBackground": UseAlternateBackground = bool.Parse(se.StringValue); break; default: base.ProcessValue(se); break; } } #endregion #region ProcessCollection internal override void ProcessCollection(SerialElement se) { SerialElementCollection sec = se.Sec; switch (se.Name) { case "AxisStripes": case "ReferenceLines": sec.PutSerialData(this); break; case "AxisStripe": AxisStripe stripe = GetStripeByName(se.Name); if (stripe != null) sec.PutSerialData(stripe); break; case "ChartAxisVisualStyle": sec.PutSerialData(ChartAxisVisualStyle); break; case "CrosshairLabelVisualStyle": sec.PutSerialData(CrosshairLabelVisualStyle); break; case "ReferenceLine": ReferenceLine line = GetReferenceLineByName(se.Name); if (line != null) sec.PutSerialData(line); break; case "Title": sec.PutSerialData(Title); break; default: base.ProcessCollection(se); break; } } #endregion #endregion #region States [Flags] private enum States : uint { AutoCalcBarMargins = (1U << 0), DotPlotAxis = (1U << 1), EdgeAxis = (1U << 2), PrimaryAxis = (1U << 3), SeriesRangeChanged = (1U << 4), UseAlternateBackground = (1U << 5), UseAutoMinGridInterval = (1U << 6), } #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 ToString public override string ToString() { if (String.IsNullOrEmpty(Name) == false) return (Name); return (base.ToString()); } #endregion #region IDisposable public override void Dispose() { AxisStripes = null; ChartAxisVisualStyle = null; CrosshairLabelVisualStyle = null; ReferenceLines = null; Title = null; base.Dispose(); } #endregion } #endregion #region TickmarkLayout internal class TickmarkLayout { public object MinValue; public object MaxValue; public object BaseValue; public int NearMargin; public int FarMargin; public int MarginOffset; public int MajorCount; public int MinorCount; public double GridSpacing; public double MajorSpacing; public double MajorInterval; public int TickmarkStart; public int TickmarkEnd; public TickmarkTick[] Ticks; } #endregion #region TickmarkTick internal class TickmarkTick { public int Index; public object Value; public Point TickPoint; public string Label; public Size LabelSize; public Point LabelPoint; public int LabelIndex; public Color LabelColor; public SizeF TextSize; public bool LabelVisible; } #endregion #region AxisBar internal class AxisBar { public int BarCount; public int BarOffset; public int BarTotalWidth; public double BarTotal; public void Reset() { BarCount = 0; BarTotal = 0; BarTotalWidth = 0; BarOffset = int.MinValue; } } #endregion public class AxisTitle : ChartTitle { #region XyAlignment [Browsable(false)] [Bindable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [EditorBrowsable(EditorBrowsableState.Never)] public new XyAlignment XyAlignment { get { return (base.XyAlignment); } set { base.XyAlignment = value; } } #endregion } [Obsolete] public class AsixTitle : AxisTitle { } #region Enums #region AxisAlignment public enum AxisAlignment { /// /// Default is determined by the Axis (X/Y). /// NotSet = -1, /// /// Axis is displayed at the right or bottom side of the chart. /// Near, /// /// Axis is displayed at the left or top side of the chart. /// Far, } #endregion #region AxisBarType internal enum AxisBarType { Bar, HiLoBar, MaxBarCount } #endregion #region AxisOrientation /// /// AxisOrientation. /// public enum AxisOrientation { /// /// X Axis /// X, /// /// Y Axis /// Y, } #endregion #region DateTimeUnits /// /// DateTimeUnits /// public enum DateTimeUnits { Ticks, Milliseconds, Seconds, Minutes, Hours, Days, Months, Years, } #endregion #region MarginLayout public enum MarginLayout { /// /// No Margins will be displayed. /// None, /// /// Margin is displayed at the right or bottom side of the axis. /// Near, /// /// Margin is displayed at the left or top side of the axis. /// Far, /// /// Both Near and Far margins are displayed. /// Both, } #endregion #region OriginOffsetMode /// /// Mode for setting the axis origin, or offset from its /// normal position. /// public enum OriginOffsetMode { /// /// No offset /// None, /// /// Origin is shifted by an absolute amount (OriginOffset) /// Absolute, /// /// Origin is shifted relative to the major scale amounts. /// Relative, /// /// Origin is shifted a percentage of the total axis length. /// Percent, } #endregion #region OverlapLabelMode public enum OverlapLabelMode { /// /// Default is to Hide overlapping labels. /// NotSet = 0, /// /// Overlapping labels will be staggered. /// Stagger, /// /// Overlapping labels may be hidden. /// Hide, } #endregion #endregion }