1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
10 // Namespace: DataVisualization.Charting.ChartTypes
12 // Classes: StockChart, CandleStickChart
14 // Purpose: Stock chart requires 4 Y values High, Low, Open and Close.
16 // The Stock chart displays opening and closing values by using
17 // markers, which are typically lines or triangles.
\93OpenCloseStyle
\94
18 // custom attribute may be used to control the style of the markers.
19 // The opening values are shown by the markers on the left, and the
20 // closing values are shown by the markers on the right.
22 // A stock chart is typically used to illustrate significant stock
23 // price points including a stock's open, close, high, and low price
24 // points. However, this type of chart can also be used to analyze
25 // scientific data, because each series of data displays a high, low,
26 // open, and close value.
28 // Reviewed: AG - Aug 6, 2002
29 // AG - Microsoft 7, 2007
31 //===================================================================
33 #region Used namespaces
36 using System.Collections;
38 using System.Drawing.Drawing2D;
39 using System.Collections.Generic;
42 using System.Windows.Forms.DataVisualization.Charting.Utilities;
44 using System.Web.UI.DataVisualization.Charting.Utilities;
50 namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
52 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
55 #region Open/close marks style enumeration
58 /// Style of the Open-Close marks in the stock chart
60 internal enum StockOpenCloseMarkStyle
73 /// CandleStick. Color of the bar depends if Open value was bigger than Close value.
81 /// CandleStick class provides chart unique name and changes the marking
82 /// style in the StockChart class to StockOpenCloseMarkStyle.CandleStick.
84 internal class CandleStickChart : StockChart
89 /// CandleStick chart constructor.
91 public CandleStickChart() : base(StockOpenCloseMarkStyle.Candlestick)
93 forceCandleStick = true;
98 #region IChartType interface implementation
103 override public string Name { get{ return ChartTypeNames.Candlestick;}}
106 /// Gets chart type image.
108 /// <param name="registry">Chart types registry object.</param>
109 /// <returns>Chart type image.</returns>
110 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
112 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
119 /// StockChart class provides 2D/3D drawing and hit testing
120 /// functionality for the Stock and CandleStick charts.
122 internal class StockChart : IChartType
129 internal Axis VAxis { get; set; }
134 internal Axis HAxis { get; set; }
137 /// Default open-close style
139 protected StockOpenCloseMarkStyle openCloseStyle = StockOpenCloseMarkStyle.Line;
142 /// Indicates that only candle-stick type of the open-close marks should be used
144 protected bool forceCandleStick = false;
151 /// Stock chart constructor.
158 /// Stock chart constructor.
160 /// <param name="style">Open-close marks default style.</param>
161 public StockChart(StockOpenCloseMarkStyle style)
163 this.openCloseStyle = style;
168 #region IChartType interface implementation
173 virtual public string Name { get{ return ChartTypeNames.Stock;}}
176 /// True if chart type is stacked
178 virtual public bool Stacked { get{ return false;}}
182 /// True if stacked chart type supports groups
184 virtual public bool SupportStackedGroups { get { return false; } }
188 /// True if stacked chart type should draw separately positive and
189 /// negative data points ( Bar and column Stacked types ).
191 public bool StackSign { get{ return false;}}
194 /// True if chart type supports axeses
196 virtual public bool RequireAxes { get{ return true;} }
199 /// Chart type with two y values used for scale ( bubble chart type )
201 public bool SecondYScale{ get{ return false;} }
204 /// True if chart type requires circular chart area.
206 public bool CircularChartArea { get{ return false;} }
209 /// True if chart type supports Logarithmic axes
211 virtual public bool SupportLogarithmicAxes { get{ return true;} }
214 /// True if chart type requires to switch the value (Y) axes position
216 virtual public bool SwitchValueAxes { get{ return false;} }
219 /// True if chart series can be placed side-by-side.
221 public bool SideBySideSeries { get{ return false;} }
224 /// True if each data point of a chart must be represented in the legend
226 virtual public bool DataPointsInLegend { get{ return false;} }
229 /// If the crossing value is auto Crossing value should be
230 /// automatically set to zero for some chart
231 /// types (Bar, column, area etc.)
233 virtual public bool ZeroCrossing { get{ return false;} }
236 /// True if palette colors should be applied for each data paoint.
237 /// Otherwise the color is applied to the series.
239 virtual public bool ApplyPaletteColorsToPoints { get { return false; } }
242 /// Indicates that extra Y values are connected to the scale of the Y axis
244 virtual public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
247 /// Indicates that it's a hundredred percent chart.
248 /// Axis scale from 0 to 100 percent should be used.
250 virtual public bool HundredPercent{ get{return false;} }
253 /// Indicates that it's a hundredred percent chart.
254 /// Axis scale from 0 to 100 percent should be used.
256 virtual public bool HundredPercentSupportNegative{ get{return false;} }
259 /// How to draw series/points in legend:
260 /// Filled rectangle, Line or Marker
262 /// <param name="series">Legend item series.</param>
263 /// <returns>Legend item style.</returns>
264 virtual public LegendImageStyle GetLegendImageStyle(Series series)
266 return LegendImageStyle.Line;
270 /// Number of supported Y value(s) per point
272 virtual public int YValuesPerPoint { get { return 4; } }
275 /// Gets chart type image.
277 /// <param name="registry">Chart types registry object.</param>
278 /// <returns>Chart type image.</returns>
279 virtual public System.Drawing.Image GetImage(ChartTypeRegistry registry)
281 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
285 #region Painting and Selection methods
288 /// Paint stock chart.
290 /// <param name="graph">The Chart Graphics object.</param>
291 /// <param name="common">The Common elements object.</param>
292 /// <param name="area">Chart area for this chart.</param>
293 /// <param name="seriesToDraw">Chart series to draw.</param>
294 virtual public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
296 ProcessChartType( false, graph, common, area, seriesToDraw );
300 /// This method recalculates size of the bars. This method is used
301 /// from Paint or Select method.
303 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
304 /// <param name="graph">The Chart Graphics object.</param>
305 /// <param name="common">The Common elements object.</param>
306 /// <param name="area">Chart area for this chart.</param>
307 /// <param name="seriesToDraw">Chart series to draw.</param>
308 virtual protected void ProcessChartType(
311 CommonElements common,
313 Series seriesToDraw )
316 // Prosess 3D chart type
317 if(area.Area3DStyle.Enable3D)
319 ProcessChartType3D( selection, graph, common, area, seriesToDraw );
324 // All data series from chart area which have Stock chart type
325 List<string> typeSeries = area.GetSeriesFromChartType(this.Name);
327 // Zero X values mode.
328 bool indexedSeries = ChartHelper.IndexedSeries(common, typeSeries.ToArray() );
330 //************************************************************
331 //** Loop through all series
332 //************************************************************
333 foreach( Series ser in common.DataManager.Series )
335 // Process non empty series of the area with stock chart type
336 if( String.Compare( ser.ChartTypeName, this.Name, StringComparison.OrdinalIgnoreCase ) != 0
337 || ser.ChartArea != area.Name || !ser.IsVisible())
342 // Check that we have at least 4 Y values
343 if(ser.YValuesPerPoint < 4)
345 throw(new ArgumentException(SR.ExceptionChartTypeRequiresYValues("StockChart", "4")));
348 // Set active horizontal/vertical axis
349 HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
350 VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
352 // Get interval between points
353 double interval = (indexedSeries) ? 1 : area.GetPointsInterval( HAxis.IsLogarithmic, HAxis.logarithmBase );
355 // Calculates the width of the candles.
356 float width = (float)(ser.GetPointWidth(graph, HAxis, interval, 0.8));
358 // Call Back Paint event
361 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
365 //************************************************************
366 //** Series data points loop
367 //************************************************************
369 foreach( DataPoint point in ser.Points )
371 // Reset pre-calculated point position
372 point.positionRel = new PointF(float.NaN, float.NaN);
374 // Get point X position
375 double xValue = point.XValue;
378 xValue = (double)index;
380 float xPosition = (float)HAxis.GetPosition( xValue );
382 double yValue0 = VAxis.GetLogValue( point.YValues[0] );
383 double yValue1 = VAxis.GetLogValue( point.YValues[1] );
384 xValue = HAxis.GetLogValue(xValue);
386 // Check if chart is completly out of the data scaleView
387 if(xValue < HAxis.ViewMinimum ||
388 xValue > HAxis.ViewMaximum ||
389 (yValue0 < VAxis.ViewMinimum && yValue1 < VAxis.ViewMinimum) ||
390 (yValue0 > VAxis.ViewMaximum && yValue1 > VAxis.ViewMaximum) )
396 // Make sure High/Low values are in data scaleView range
397 double high = VAxis.GetLogValue( point.YValues[0] );
398 double low = VAxis.GetLogValue( point.YValues[1] );
400 if( high > VAxis.ViewMaximum )
402 high = VAxis.ViewMaximum;
404 if( high < VAxis.ViewMinimum )
406 high = VAxis.ViewMinimum;
408 high = (float)VAxis.GetLinearPosition(high);
410 if( low > VAxis.ViewMaximum )
412 low = VAxis.ViewMaximum;
414 if( low < VAxis.ViewMinimum )
416 low = VAxis.ViewMinimum;
418 low = VAxis.GetLinearPosition(low);
420 // Remeber pre-calculated point position
421 point.positionRel = new PointF((float)xPosition, (float)high);
423 if( common.ProcessModePaint )
426 // Check if chart is partialy in the data scaleView
427 bool clipRegionSet = false;
428 if(xValue == HAxis.ViewMinimum || xValue == HAxis.ViewMaximum )
430 // Set clipping region for line drawing
431 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
432 clipRegionSet = true;
435 // Start Svg Selection mode
436 graph.StartHotRegion( point );
442 point.BorderDashStyle,
443 new PointF(xPosition, (float)high),
444 new PointF(xPosition, (float)low),
448 // Draw Open-Close marks
449 DrawOpenCloseMarks(graph, area, ser, point, xPosition, width);
451 // End Svg Selection mode
452 graph.EndHotRegion( );
461 if( common.ProcessModeRegions )
463 // Calculate rect around the hi-lo line and open-close marks
464 RectangleF areaRect = RectangleF.Empty;
465 areaRect.X = xPosition - width / 2f;
466 areaRect.Y = (float)Math.Min(high, low);
467 areaRect.Width = width;
468 areaRect.Height = (float)Math.Max(high, low) - areaRect.Y;
470 common.HotRegionsList.AddHotRegion(
480 //************************************************************
481 //** Second series data points loop, when markers and labels
483 //************************************************************
487 foreach( DataPoint point in ser.Points )
489 // Get point X position
490 double xValue = point.XValue;
493 xValue = (double)index;
495 float xPosition = (float)HAxis.GetPosition( xValue );
497 double yValue0 = VAxis.GetLogValue( point.YValues[0] );
498 double yValue1 = VAxis.GetLogValue( point.YValues[1] );
499 xValue = HAxis.GetLogValue(xValue);
501 // Check if chart is completly out of the data scaleView
502 if(xValue < HAxis.ViewMinimum ||
503 xValue > HAxis.ViewMaximum ||
504 (yValue0 < VAxis.ViewMinimum && yValue1 < VAxis.ViewMinimum) ||
505 (yValue0 > VAxis.ViewMaximum && yValue1 > VAxis.ViewMaximum) )
511 // Make sure High/Low values are in data scaleView range
512 double high = VAxis.GetLogValue( point.YValues[0] );
513 double low = VAxis.GetLogValue( point.YValues[1] );
515 if( high > VAxis.ViewMaximum )
517 high = VAxis.ViewMaximum;
519 if( high < VAxis.ViewMinimum )
521 high = VAxis.ViewMinimum;
523 high = (float)VAxis.GetLinearPosition(high);
525 if( low > VAxis.ViewMaximum )
527 low = VAxis.ViewMaximum;
529 if( low < VAxis.ViewMinimum )
531 low = VAxis.ViewMinimum;
533 low = VAxis.GetLinearPosition(low);
536 if(point.MarkerStyle != MarkerStyle.None || point.MarkerImage.Length > 0)
539 SizeF markerSize = SizeF.Empty;
540 markerSize.Width = point.MarkerSize;
541 markerSize.Height = point.MarkerSize;
542 if (graph != null && graph.Graphics != null)
544 // Marker size is in pixels and we do the mapping for higher DPIs
545 markerSize.Width = point.MarkerSize * graph.Graphics.DpiX / 96;
546 markerSize.Height = point.MarkerSize * graph.Graphics.DpiY / 96;
549 if (point.MarkerImage.Length > 0)
550 common.ImageLoader.GetAdjustedImageSize(point.MarkerImage, graph.Graphics, ref markerSize);
552 // Get marker position
553 PointF markerPosition = PointF.Empty;
554 markerPosition.X = xPosition;
555 markerPosition.Y = (float)high - graph.GetRelativeSize(markerSize).Height/2f;
561 graph.DrawMarkerRel(markerPosition,
563 (int)markerSize.Height,
564 (point.MarkerColor == Color.Empty) ? point.Color : point.MarkerColor,
565 (point.MarkerBorderColor == Color.Empty) ? point.BorderColor : point.MarkerBorderColor,
566 point.MarkerBorderWidth,
568 point.MarkerImageTransparentColor,
569 (point.series != null) ? point.series.ShadowOffset : 0,
570 (point.series != null) ? point.series.ShadowColor : Color.Empty,
571 new RectangleF(markerPosition.X, markerPosition.Y, markerSize.Width, markerSize.Height));
573 if( common.ProcessModeRegions )
575 // Get relative marker size
576 SizeF relativeMarkerSize = graph.GetRelativeSize(markerSize);
578 // Insert area just after the last custom area
579 int insertIndex = common.HotRegionsList.FindInsertIndex();
580 common.HotRegionsList.FindInsertIndex();
582 // Insert circle area
583 if(point.MarkerStyle == MarkerStyle.Circle)
585 float[] circCoord = new float[3];
586 circCoord[0] = markerPosition.X;
587 circCoord[1] = markerPosition.Y;
588 circCoord[2] = relativeMarkerSize.Width/2f;
590 common.HotRegionsList.AddHotRegion(
600 // All other markers represented as rectangles
603 common.HotRegionsList.AddHotRegion(
604 new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
613 // Increase the markers counter
615 if(ser.MarkerStep == markerIndex)
622 DrawLabel(common, area, graph, ser, point, new PointF(xPosition, (float)Math.Min(high, low)), index);
624 // Increase point counter
631 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
637 /// Draws stock chart open-close marks depending on selected style.
639 /// <param name="graph">Chart graphics object.</param>
640 /// <param name="area">Chart area.</param>
641 /// <param name="ser">Data point series.</param>
642 /// <param name="point">Data point to draw.</param>
643 /// <param name="xPosition">X position.</param>
644 /// <param name="width">Point width.</param>
645 virtual protected void DrawOpenCloseMarks(
653 double openY = VAxis.GetLogValue( point.YValues[2] );
654 double closeY = VAxis.GetLogValue( point.YValues[3] );
656 // Check if mark is inside data scaleView
657 if( (openY > VAxis.ViewMaximum ||
658 openY < VAxis.ViewMinimum) &&
659 (closeY > VAxis.ViewMaximum ||
660 closeY < VAxis.ViewMinimum) )
665 // Calculate open-close position
666 float open = (float)VAxis.GetLinearPosition(openY);
667 float close = (float)VAxis.GetLinearPosition(closeY);
668 SizeF absSize = graph.GetAbsoluteSize(new SizeF(width, width));
669 float height = graph.GetRelativeSize(absSize).Height;
672 StockOpenCloseMarkStyle style = openCloseStyle;
673 string styleType = "";
674 if(point.IsCustomPropertySet(CustomPropertyName.OpenCloseStyle))
676 styleType = point[CustomPropertyName.OpenCloseStyle];
678 else if(ser.IsCustomPropertySet(CustomPropertyName.OpenCloseStyle))
680 styleType = ser[CustomPropertyName.OpenCloseStyle];
683 if(styleType != null && styleType.Length > 0)
685 if(String.Compare(styleType, "Candlestick", StringComparison.OrdinalIgnoreCase) == 0)
687 style = StockOpenCloseMarkStyle.Candlestick;
689 else if (String.Compare(styleType, "Triangle", StringComparison.OrdinalIgnoreCase) == 0)
691 style = StockOpenCloseMarkStyle.Triangle;
693 else if (String.Compare(styleType, "Line", StringComparison.OrdinalIgnoreCase) == 0)
695 style = StockOpenCloseMarkStyle.Line;
699 // Get attribute which controls if open/close marks are shown
700 bool showOpen = true;
701 bool showClose = true;
702 string showOpenClose = "";
703 if(point.IsCustomPropertySet(CustomPropertyName.ShowOpenClose))
705 showOpenClose = point[CustomPropertyName.ShowOpenClose];
707 else if(ser.IsCustomPropertySet(CustomPropertyName.ShowOpenClose))
709 showOpenClose = ser[CustomPropertyName.ShowOpenClose];
712 if(showOpenClose != null && showOpenClose.Length > 0)
714 if(String.Compare(showOpenClose, "Both", StringComparison.OrdinalIgnoreCase) == 0)
719 else if (String.Compare(showOpenClose, "Open", StringComparison.OrdinalIgnoreCase) == 0)
724 else if (String.Compare(showOpenClose, "Close", StringComparison.OrdinalIgnoreCase) == 0)
731 // Check if chart is partialy in the data scaleView
732 bool clipRegionSet = false;
733 if( style == StockOpenCloseMarkStyle.Candlestick || (xPosition - width / 2f) < area.PlotAreaPosition.X || (xPosition + width / 2f) > area.PlotAreaPosition.Right)
735 // Set clipping region for line drawing
736 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
737 clipRegionSet = true;
741 // Draw open-close marks as bar
742 if(forceCandleStick || style == StockOpenCloseMarkStyle.Candlestick)
744 // Colors used to draw bar of the open-close style
745 ColorConverter colorConverter = new ColorConverter();
746 Color priceUpColor = point.Color;
747 Color priceDownColor = point.BackSecondaryColor;
749 // Check if special color properties are set
750 string attrValue = point[CustomPropertyName.PriceUpColor];
751 if(attrValue != null && attrValue.Length > 0)
756 priceUpColor = (Color)colorConverter.ConvertFromString(attrValue);
758 catch (ArgumentException)
762 catch (NotSupportedException)
769 priceUpColor = (Color)colorConverter.ConvertFromInvariantString(attrValue);
773 attrValue = point[CustomPropertyName.PriceDownColor];
774 if(attrValue != null && attrValue.Length > 0)
779 priceDownColor = (Color)colorConverter.ConvertFromString(attrValue);
781 catch (ArgumentException)
785 catch (NotSupportedException)
792 priceDownColor = (Color)colorConverter.ConvertFromInvariantString(attrValue);
796 // Calculate bar rectangle
797 RectangleF rect = RectangleF.Empty;
798 rect.Y = (float)Math.Min(open, close);
799 rect.X = xPosition - width / 2f;
800 rect.Height = (float)Math.Max(open, close) - rect.Y;
803 // Bar and border color
804 Color barColor = (open > close) ? priceUpColor : priceDownColor;
805 Color barBorderColor = (point.BorderColor == Color.Empty) ? (barColor == Color.Empty) ? point.Color : barColor : point.BorderColor;
807 // Get absolute height
808 SizeF sizeOfHeight = new SizeF( rect.Height, rect.Height );
809 sizeOfHeight = graph.GetAbsoluteSize( sizeOfHeight );
811 // Draw open-close bar
812 if( sizeOfHeight.Height > 1 )
814 graph.FillRectangleRel(
817 point.BackHatchStyle,
819 point.BackImageWrapMode,
820 point.BackImageTransparentColor,
821 point.BackImageAlignment,
822 point.BackGradientStyle,
823 point.BackSecondaryColor,
826 point.BorderDashStyle,
829 PenAlignment.Inset );
833 graph.DrawLineRel(barBorderColor, point.BorderWidth, point.BorderDashStyle,
834 new PointF(rect.X, rect.Y),
835 new PointF(rect.Right, rect.Y),
836 ser.ShadowColor, ser.ShadowOffset );
840 // Draw open-close marks as triangals
841 else if(style == StockOpenCloseMarkStyle.Triangle)
843 using (GraphicsPath path = new GraphicsPath())
845 PointF point1 = graph.GetAbsolutePoint(new PointF(xPosition, open));
846 PointF point2 = graph.GetAbsolutePoint(new PointF(xPosition - width / 2f, open + height / 2f));
847 PointF point3 = graph.GetAbsolutePoint(new PointF(xPosition - width / 2f, open - height / 2f));
849 using (Brush brush = new SolidBrush(point.Color))
851 // Draw Open mark line
854 if (openY <= VAxis.ViewMaximum && openY >= VAxis.ViewMinimum)
856 path.AddLine(point2, point1);
857 path.AddLine(point1, point3);
858 path.AddLine(point3, point3);
859 graph.FillPath(brush, path);
863 // Draw close mark line
866 if (closeY <= VAxis.ViewMaximum && closeY >= VAxis.ViewMinimum)
869 point1 = graph.GetAbsolutePoint(new PointF(xPosition, close));
870 point2 = graph.GetAbsolutePoint(new PointF(xPosition + width / 2f, close + height / 2f));
871 point3 = graph.GetAbsolutePoint(new PointF(xPosition + width / 2f, close - height / 2f));
872 path.AddLine(point2, point1);
873 path.AddLine(point1, point3);
874 path.AddLine(point3, point3);
875 graph.FillPath(brush, path);
883 // Draw ope-close marks as lines
886 // Draw Open mark line
889 if(openY <= VAxis.ViewMaximum && openY >= VAxis.ViewMinimum)
891 graph.DrawLineRel(point.Color, point.BorderWidth, point.BorderDashStyle,
892 new PointF(xPosition - width/2f, open),
893 new PointF(xPosition, open),
894 ser.ShadowColor, ser.ShadowOffset );
898 // Draw Close mark line
901 if(closeY <= VAxis.ViewMaximum && closeY >= VAxis.ViewMinimum)
903 graph.DrawLineRel(point.Color, point.BorderWidth, point.BorderDashStyle,
904 new PointF(xPosition, close),
905 new PointF(xPosition + width/2f, close),
906 ser.ShadowColor, ser.ShadowOffset );
919 /// Draws stock chart data point label.
921 /// <param name="common">The Common elements object</param>
922 /// <param name="area">Chart area for this chart</param>
923 /// <param name="graph">Chart graphics object.</param>
924 /// <param name="ser">Data point series.</param>
925 /// <param name="point">Data point to draw.</param>
926 /// <param name="position">Label position.</param>
927 /// <param name="pointIndex">Data point index in the series.</param>
928 virtual protected void DrawLabel(
929 CommonElements common,
937 if(ser.IsValueShownAsLabel || point.IsValueShownAsLabel || point.Label.Length > 0)
940 using (StringFormat format = new StringFormat())
942 format.Alignment = StringAlignment.Near;
943 format.LineAlignment = StringAlignment.Center;
944 if (point.LabelAngle == 0)
946 format.Alignment = StringAlignment.Center;
947 format.LineAlignment = StringAlignment.Far;
952 if (point.Label.Length == 0)
954 // Check what value to show (High, Low, Open, Close)
956 string valueType = "";
957 if (point.IsCustomPropertySet(CustomPropertyName.LabelValueType))
959 valueType = point[CustomPropertyName.LabelValueType];
961 else if (ser.IsCustomPropertySet(CustomPropertyName.LabelValueType))
963 valueType = ser[CustomPropertyName.LabelValueType];
966 if (String.Compare(valueType, "High", StringComparison.OrdinalIgnoreCase) == 0)
970 else if (String.Compare(valueType, "Low", StringComparison.OrdinalIgnoreCase) == 0)
974 else if (String.Compare(valueType, "Open", StringComparison.OrdinalIgnoreCase) == 0)
979 text = ValueConverter.FormatValue(
983 point.YValues[valueIndex],
986 ChartElementType.DataPoint);
990 text = point.ReplaceKeywords(point.Label);
994 int textAngle = point.LabelAngle;
996 // Check if text contains white space only
997 if (text.Trim().Length != 0)
999 SizeF sizeFont = SizeF.Empty;
1002 // Check if Smart Labels are enabled
1003 if (ser.SmartLabelStyle.Enabled)
1006 SizeF markerSize = SizeF.Empty;
1007 markerSize.Width = point.MarkerSize;
1008 markerSize.Height = point.MarkerSize;
1009 if (graph != null && graph.Graphics != null)
1011 // Marker size is in pixels and we do the mapping for higher DPIs
1012 markerSize.Width = point.MarkerSize * graph.Graphics.DpiX / 96;
1013 markerSize.Height = point.MarkerSize * graph.Graphics.DpiY / 96;
1016 if (point.MarkerImage.Length > 0)
1017 common.ImageLoader.GetAdjustedImageSize(point.MarkerImage, graph.Graphics, ref markerSize);
1019 // Get point label style attribute
1020 markerSize = graph.GetRelativeSize(markerSize);
1021 sizeFont = graph.GetRelativeSize(graph.MeasureString(text, point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
1023 // Adjust label position using SmartLabelStyle algorithm
1024 position = area.smartLabels.AdjustSmartLabelPosition(
1028 ser.SmartLabelStyle,
1034 LabelAlignmentStyles.Top);
1036 // Smart labels always use 0 degrees text angle
1044 if (!position.IsEmpty)
1046 RectangleF labelBackPosition = RectangleF.Empty;
1048 if (!point.LabelBackColor.IsEmpty ||
1049 point.LabelBorderWidth > 0 ||
1050 !point.LabelBorderColor.IsEmpty)
1053 if (sizeFont.IsEmpty)
1055 sizeFont = graph.GetRelativeSize(graph.MeasureString(text, point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
1058 // Adjust label y coordinate
1059 position.Y -= sizeFont.Height / 8;
1061 // Get label background position
1062 SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1063 sizeLabel.Height += sizeFont.Height / 8;
1064 sizeLabel.Width += sizeLabel.Width / text.Length;
1065 labelBackPosition = PointChart.GetLabelPosition(
1075 using (Brush brush = new SolidBrush(point.LabelForeColor))
1077 graph.DrawPointLabelStringRel(
1087 point.LabelBackColor,
1088 point.LabelBorderColor,
1089 point.LabelBorderWidth,
1090 point.LabelBorderDashStyle,
1103 #region 3D Drawing and Selection methods
1106 /// This method recalculates size of the bars. This method is used
1107 /// from Paint or Select method.
1109 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
1110 /// <param name="graph">The Chart Graphics object.</param>
1111 /// <param name="common">The Common elements object.</param>
1112 /// <param name="area">Chart area for this chart.</param>
1113 /// <param name="seriesToDraw">Chart series to draw.</param>
1114 virtual protected void ProcessChartType3D(
1116 ChartGraphics graph,
1117 CommonElements common,
1119 Series seriesToDraw )
1122 // All data series from chart area which have Stock chart type
1123 List<string> typeSeries = area.GetSeriesFromChartType(this.Name);
1125 // Zero X values mode.
1126 bool indexedSeries = ChartHelper.IndexedSeries(common, typeSeries.ToArray() );
1128 //************************************************************
1129 //** Loop through all series
1130 //************************************************************
1131 foreach( Series ser in common.DataManager.Series )
1133 // Process non empty series of the area with stock chart type
1134 if( String.Compare( ser.ChartTypeName, this.Name, StringComparison.OrdinalIgnoreCase ) != 0
1135 || ser.ChartArea != area.Name || !ser.IsVisible())
1140 // Check if drawn series is specified
1141 if(seriesToDraw != null && seriesToDraw.Name != ser.Name)
1146 // Check that we have at least 4 Y values
1147 if(ser.YValuesPerPoint < 4)
1149 throw(new ArgumentException(SR.ExceptionChartTypeRequiresYValues("StockChart", "4" )));
1152 // Set active horizontal/vertical axis
1153 HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
1154 VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
1156 // Get interval between points
1157 double interval = (indexedSeries) ? 1 : area.GetPointsInterval( HAxis.IsLogarithmic, HAxis.logarithmBase );
1159 // Calculates the width of the candles.
1160 float width = (float)(ser.GetPointWidth(graph, HAxis, interval, 0.8));
1162 // Call Back Paint event
1165 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
1168 //************************************************************
1169 //** Get series depth and Z position
1170 //************************************************************
1171 float seriesDepth, seriesZPosition;
1172 area.GetSeriesZPositionAndDepth(ser, out seriesDepth, out seriesZPosition);
1174 //************************************************************
1175 //** Series data points loop
1176 //************************************************************
1178 foreach( DataPoint point in ser.Points )
1180 // Reset pre-calculated point position
1181 point.positionRel = new PointF(float.NaN, float.NaN);
1183 // Get point X position
1184 double xValue = point.XValue;
1187 xValue = (double)index;
1189 float xPosition = (float)HAxis.GetPosition( xValue );
1191 double yValue0 = VAxis.GetLogValue( point.YValues[0] );
1192 double yValue1 = VAxis.GetLogValue( point.YValues[1] );
1193 xValue = HAxis.GetLogValue(xValue);
1195 // Check if chart is completly out of the data scaleView
1196 if(xValue < HAxis.ViewMinimum ||
1197 xValue > HAxis.ViewMaximum ||
1198 (yValue0 < VAxis.ViewMinimum && yValue1 < VAxis.ViewMinimum) ||
1199 (yValue0 > VAxis.ViewMaximum && yValue1 > VAxis.ViewMaximum) )
1205 // Check if chart is partialy in the data scaleView
1206 bool clipRegionSet = false;
1207 if(xValue == HAxis.ViewMinimum || xValue == HAxis.ViewMaximum )
1209 // Set clipping region for line drawing
1210 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
1211 clipRegionSet = true;
1214 // Make sure High/Low values are in data scaleView range
1215 double high = VAxis.GetLogValue( point.YValues[0] );
1216 double low = VAxis.GetLogValue( point.YValues[1] );
1218 if( high > VAxis.ViewMaximum )
1220 high = VAxis.ViewMaximum;
1222 if( high < VAxis.ViewMinimum )
1224 high = VAxis.ViewMinimum;
1226 high = (float)VAxis.GetLinearPosition(high);
1228 if( low > VAxis.ViewMaximum )
1230 low = VAxis.ViewMaximum;
1232 if( low < VAxis.ViewMinimum )
1234 low = VAxis.ViewMinimum;
1236 low = VAxis.GetLinearPosition(low);
1238 // Remeber pre-calculated point position
1239 point.positionRel = new PointF((float)xPosition, (float)high);
1241 // 3D Transform coordinates
1242 Point3D[] points = new Point3D[2];
1243 points[0] = new Point3D(xPosition, (float)high, seriesZPosition+seriesDepth/2f);
1244 points[1] = new Point3D(xPosition, (float)low, seriesZPosition+seriesDepth/2f);
1245 area.matrix3D.TransformPoints(points);
1247 // Start Svg Selection mode
1248 graph.StartHotRegion( point );
1254 point.BorderDashStyle,
1260 // Draw Open-Close marks
1261 DrawOpenCloseMarks3D(graph, area, ser, point, xPosition, width, seriesZPosition, seriesDepth);
1262 xPosition = points[0].X;
1266 // End Svg Selection mode
1267 graph.EndHotRegion( );
1269 // Reset Clip Region
1275 if( common.ProcessModeRegions )
1277 // Calculate rect around the hi-lo line and open-close marks
1278 RectangleF areaRect = RectangleF.Empty;
1279 areaRect.X = xPosition - width / 2f;
1280 areaRect.Y = (float)Math.Min(high, low);
1281 areaRect.Width = width;
1282 areaRect.Height = (float)Math.Max(high, low) - areaRect.Y;
1284 common.HotRegionsList.AddHotRegion(
1295 //************************************************************
1296 //** Second series data points loop, when markers and labels
1298 //************************************************************
1299 int markerIndex = 0;
1301 foreach( DataPoint point in ser.Points )
1303 // Get point X position
1304 double xValue = point.XValue;
1307 xValue = (double)index;
1309 float xPosition = (float)HAxis.GetPosition( xValue );
1311 double yValue0 = VAxis.GetLogValue( point.YValues[0] );
1312 double yValue1 = VAxis.GetLogValue( point.YValues[1] );
1313 xValue = HAxis.GetLogValue(xValue);
1315 // Check if chart is completly out of the data scaleView
1316 if(xValue < HAxis.ViewMinimum ||
1317 xValue > HAxis.ViewMaximum ||
1318 (yValue0 < VAxis.ViewMinimum && yValue1 < VAxis.ViewMinimum) ||
1319 (yValue0 > VAxis.ViewMaximum && yValue1 > VAxis.ViewMaximum) )
1325 // Make sure High/Low values are in data scaleView range
1326 double high = VAxis.GetLogValue( point.YValues[0] );
1327 double low = VAxis.GetLogValue( point.YValues[1] );
1329 if( high > VAxis.ViewMaximum )
1331 high = VAxis.ViewMaximum;
1333 if( high < VAxis.ViewMinimum )
1335 high = VAxis.ViewMinimum;
1337 high = (float)VAxis.GetLinearPosition(high);
1339 if( low > VAxis.ViewMaximum )
1341 low = VAxis.ViewMaximum;
1343 if( low < VAxis.ViewMinimum )
1345 low = VAxis.ViewMinimum;
1347 low = VAxis.GetLinearPosition(low);
1350 // 3D Transform coordinates
1351 Point3D[] points = new Point3D[2];
1352 points[0] = new Point3D(xPosition, (float)high, seriesZPosition+seriesDepth/2f);
1353 points[1] = new Point3D(xPosition, (float)low, seriesZPosition+seriesDepth/2f);
1354 area.matrix3D.TransformPoints(points);
1355 xPosition = points[0].X;
1360 DrawLabel(common, area, graph, ser, point, new PointF(xPosition, (float)Math.Min(high, low)), index);
1363 if(point.MarkerStyle != MarkerStyle.None || point.MarkerImage.Length > 0)
1366 SizeF markerSize = SizeF.Empty;
1367 markerSize.Width = point.MarkerSize;
1368 markerSize.Height = point.MarkerSize;
1369 if (graph != null && graph.Graphics != null)
1371 // Marker size is in pixels and we do the mapping for higher DPIs
1372 markerSize.Width = point.MarkerSize * graph.Graphics.DpiX / 96;
1373 markerSize.Height = point.MarkerSize * graph.Graphics.DpiY / 96;
1376 if (point.MarkerImage.Length > 0)
1377 common.ImageLoader.GetAdjustedImageSize(point.MarkerImage, graph.Graphics, ref markerSize);
1379 // Get marker position
1380 PointF markerPosition = PointF.Empty;
1381 markerPosition.X = xPosition;
1382 markerPosition.Y = (float)high - graph.GetRelativeSize(markerSize).Height/2f;
1385 if(markerIndex == 0)
1388 graph.DrawMarkerRel(markerPosition,
1390 (int)markerSize.Height,
1391 (point.MarkerColor == Color.Empty) ? point.Color : point.MarkerColor,
1392 (point.MarkerBorderColor == Color.Empty) ? point.BorderColor : point.MarkerBorderColor,
1393 point.MarkerBorderWidth,
1395 point.MarkerImageTransparentColor,
1396 (point.series != null) ? point.series.ShadowOffset : 0,
1397 (point.series != null) ? point.series.ShadowColor : Color.Empty,
1398 new RectangleF(markerPosition.X, markerPosition.Y, markerSize.Width, markerSize.Height));
1400 if( common.ProcessModeRegions )
1402 // Get relative marker size
1403 SizeF relativeMarkerSize = graph.GetRelativeSize(markerSize);
1405 // Insert area just after the last custom area
1406 int insertIndex = common.HotRegionsList.FindInsertIndex();
1407 common.HotRegionsList.FindInsertIndex();
1409 // Insert circle area
1410 if(point.MarkerStyle == MarkerStyle.Circle)
1412 float[] circCoord = new float[3];
1413 circCoord[0] = markerPosition.X;
1414 circCoord[1] = markerPosition.Y;
1415 circCoord[2] = relativeMarkerSize.Width/2f;
1417 common.HotRegionsList.AddHotRegion(
1427 // All other markers represented as rectangles
1430 common.HotRegionsList.AddHotRegion(
1431 new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
1439 // Increase the markers counter
1441 if(ser.MarkerStep == markerIndex)
1452 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
1458 /// Draws stock chart open-close marks depending on selected style.
1460 /// <param name="graph">Chart graphics object.</param>
1461 /// <param name="area">Chart area.</param>
1462 /// <param name="ser">Data point series.</param>
1463 /// <param name="point">Data point to draw.</param>
1464 /// <param name="xPosition">X position.</param>
1465 /// <param name="width">Point width.</param>
1466 /// <param name="zPosition">Series Z position.</param>
1467 /// <param name="depth">Series depth.</param>
1468 virtual protected void DrawOpenCloseMarks3D(
1469 ChartGraphics graph,
1478 double openY = VAxis.GetLogValue( point.YValues[2] );
1479 double closeY = VAxis.GetLogValue( point.YValues[3] );
1481 // Check if mark is inside data scaleView
1482 if( (openY > VAxis.ViewMaximum ||
1483 openY < VAxis.ViewMinimum) &&
1484 (closeY > VAxis.ViewMaximum ||
1485 closeY < VAxis.ViewMinimum) )
1490 // Calculate open-close position
1491 float open = (float)VAxis.GetLinearPosition(openY);
1492 float close = (float)VAxis.GetLinearPosition(closeY);
1493 SizeF absSize = graph.GetAbsoluteSize(new SizeF(width, width));
1494 float height = graph.GetRelativeSize(absSize).Height;
1497 StockOpenCloseMarkStyle style = openCloseStyle;
1498 string styleType = "";
1499 if(point.IsCustomPropertySet(CustomPropertyName.OpenCloseStyle))
1501 styleType = point[CustomPropertyName.OpenCloseStyle];
1503 else if(ser.IsCustomPropertySet(CustomPropertyName.OpenCloseStyle))
1505 styleType = ser[CustomPropertyName.OpenCloseStyle];
1508 if(styleType != null && styleType.Length > 0)
1510 if(String.Compare(styleType, "Candlestick", StringComparison.OrdinalIgnoreCase) == 0)
1512 style = StockOpenCloseMarkStyle.Candlestick;
1514 else if (String.Compare(styleType, "Triangle", StringComparison.OrdinalIgnoreCase) == 0)
1516 style = StockOpenCloseMarkStyle.Triangle;
1518 else if (String.Compare(styleType, "Line", StringComparison.OrdinalIgnoreCase) == 0)
1520 style = StockOpenCloseMarkStyle.Line;
1524 // Get attribute which controls if open/close marks are shown
1525 bool showOpen = true;
1526 bool showClose = true;
1527 string showOpenClose = "";
1528 if(point.IsCustomPropertySet(CustomPropertyName.ShowOpenClose))
1530 showOpenClose = point[CustomPropertyName.ShowOpenClose];
1532 else if(ser.IsCustomPropertySet(CustomPropertyName.ShowOpenClose))
1534 showOpenClose = ser[CustomPropertyName.ShowOpenClose];
1537 if(showOpenClose != null && showOpenClose.Length > 0)
1539 if(String.Compare(showOpenClose, "Both", StringComparison.OrdinalIgnoreCase) == 0)
1544 else if (String.Compare(showOpenClose, "Open", StringComparison.OrdinalIgnoreCase) == 0)
1549 else if (String.Compare(showOpenClose, "Close", StringComparison.OrdinalIgnoreCase) == 0)
1556 // Check if chart is partialy in the data scaleView
1557 bool clipRegionSet = false;
1558 if((xPosition - width / 2f) < area.PlotAreaPosition.X || (xPosition + width / 2f) > area.PlotAreaPosition.Right)
1560 // Set clipping region for line drawing
1561 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
1562 clipRegionSet = true;
1566 // Draw open-close marks as bar
1567 if(forceCandleStick || style == StockOpenCloseMarkStyle.Candlestick)
1569 // Colors used to draw bar of the open-close style
1570 ColorConverter colorConverter = new ColorConverter();
1571 Color priceUpColor = point.Color;
1572 Color priceDownColor = point.BackSecondaryColor;
1574 // Check if special color properties are set
1575 string attrValue = point[CustomPropertyName.PriceUpColor];
1576 if(attrValue != null && attrValue.Length > 0)
1578 bool failed = false;
1581 priceUpColor = (Color)colorConverter.ConvertFromString(attrValue);
1583 catch (NotSupportedException)
1587 catch (ArgumentException)
1594 priceUpColor = (Color)colorConverter.ConvertFromInvariantString(attrValue);
1598 attrValue = point[CustomPropertyName.PriceDownColor];
1599 if(attrValue != null && attrValue.Length > 0)
1601 bool failed = false;
1604 priceDownColor = (Color)colorConverter.ConvertFromString(attrValue);
1606 catch (ArgumentException)
1610 catch (NotSupportedException)
1617 priceDownColor = (Color)colorConverter.ConvertFromInvariantString(attrValue);
1621 // Calculate bar rectangle
1622 RectangleF rect = RectangleF.Empty;
1623 rect.Y = (float)Math.Min(open, close);
1624 rect.X = xPosition - width / 2f;
1625 rect.Height = (float)Math.Max(open, close) - rect.Y;
1628 // Bar and border color
1629 Color barColor = (open > close) ? priceUpColor : priceDownColor;
1630 Color barBorderColor = (point.BorderColor == Color.Empty) ? (barColor == Color.Empty) ? point.Color : barColor : point.BorderColor;
1632 // Translate coordinates
1633 Point3D[] points = new Point3D[2];
1634 points[0] = new Point3D(rect.X, rect.Y, zPosition + depth/2f);
1635 points[1] = new Point3D(rect.Right, rect.Bottom, zPosition + depth/2f);
1636 area.matrix3D.TransformPoints(points);
1637 rect.Location = points[0].PointF;
1638 rect.Width = (float)Math.Abs(points[1].X - points[0].X);
1639 rect.Height = (float)Math.Abs(points[1].Y - points[0].Y);
1641 // Draw open-close bar
1644 graph.FillRectangleRel(
1647 point.BackHatchStyle,
1649 point.BackImageWrapMode,
1650 point.BackImageTransparentColor,
1651 point.BackImageAlignment,
1652 point.BackGradientStyle,
1653 point.BackSecondaryColor,
1656 point.BorderDashStyle,
1659 PenAlignment.Inset);
1663 graph.DrawLineRel(barBorderColor, point.BorderWidth, point.BorderDashStyle,
1664 new PointF(rect.X, rect.Y),
1665 new PointF(rect.Right, rect.Y),
1666 ser.ShadowColor, ser.ShadowOffset );
1670 // Draw open-close marks as triangals
1671 else if(style == StockOpenCloseMarkStyle.Triangle)
1673 using (GraphicsPath path = new GraphicsPath())
1676 // Translate coordinates
1677 Point3D[] points = new Point3D[3];
1678 points[0] = new Point3D(xPosition, open, zPosition + depth / 2f);
1679 points[1] = new Point3D(xPosition - width / 2f, open + height / 2f, zPosition + depth / 2f);
1680 points[2] = new Point3D(xPosition - width / 2f, open - height / 2f, zPosition + depth / 2f);
1681 area.matrix3D.TransformPoints(points);
1682 points[0].PointF = graph.GetAbsolutePoint(points[0].PointF);
1683 points[1].PointF = graph.GetAbsolutePoint(points[1].PointF);
1684 points[2].PointF = graph.GetAbsolutePoint(points[2].PointF);
1686 using (Brush brush = new SolidBrush(point.Color))
1688 // Draw Open mark line
1691 if (openY <= VAxis.ViewMaximum && openY >= VAxis.ViewMinimum)
1693 path.AddLine(points[1].PointF, points[0].PointF);
1694 path.AddLine(points[0].PointF, points[2].PointF);
1695 path.AddLine(points[2].PointF, points[2].PointF);
1696 graph.FillPath(brush, path);
1700 // Draw close mark line
1703 if (closeY <= VAxis.ViewMaximum && closeY >= VAxis.ViewMinimum)
1705 points[0] = new Point3D(xPosition, close, zPosition + depth / 2f);
1706 points[1] = new Point3D(xPosition + width / 2f, close + height / 2f, zPosition + depth / 2f);
1707 points[2] = new Point3D(xPosition + width / 2f, close - height / 2f, zPosition + depth / 2f);
1708 area.matrix3D.TransformPoints(points);
1709 points[0].PointF = graph.GetAbsolutePoint(points[0].PointF);
1710 points[1].PointF = graph.GetAbsolutePoint(points[1].PointF);
1711 points[2].PointF = graph.GetAbsolutePoint(points[2].PointF);
1714 path.AddLine(points[1].PointF, points[0].PointF);
1715 path.AddLine(points[0].PointF, points[2].PointF);
1716 path.AddLine(points[2].PointF, points[2].PointF);
1717 graph.FillPath(brush, path);
1724 // Draw ope-close marks as lines
1727 // Draw Open mark line
1730 if(openY <= VAxis.ViewMaximum && openY >= VAxis.ViewMinimum)
1732 // Translate coordinates
1733 Point3D[] points = new Point3D[2];
1734 points[0] = new Point3D(xPosition - width/2f, open, zPosition + depth/2f);
1735 points[1] = new Point3D(xPosition, open, zPosition + depth/2f);
1736 area.matrix3D.TransformPoints(points);
1738 graph.DrawLineRel(point.Color, point.BorderWidth, point.BorderDashStyle,
1741 ser.ShadowColor, ser.ShadowOffset );
1745 // Draw Close mark line
1748 if(closeY <= VAxis.ViewMaximum && closeY >= VAxis.ViewMinimum)
1750 // Translate coordinates
1751 Point3D[] points = new Point3D[2];
1752 points[0] = new Point3D(xPosition, close, zPosition + depth/2f);
1753 points[1] = new Point3D(xPosition + width/2f, close, zPosition + depth/2f);
1754 area.matrix3D.TransformPoints(points);
1756 graph.DrawLineRel(point.Color, point.BorderWidth, point.BorderDashStyle,
1759 ser.ShadowColor, ser.ShadowOffset );
1764 // Reset Clip Region
1773 #region Y values related methods
1776 /// Helper function, which returns the Y value of the point.
1778 /// <param name="common">Chart common elements.</param>
1779 /// <param name="area">Chart area the series belongs to.</param>
1780 /// <param name="series">Sereis of the point.</param>
1781 /// <param name="point">Point object.</param>
1782 /// <param name="pointIndex">Index of the point.</param>
1783 /// <param name="yValueIndex">Index of the Y value to get.</param>
1784 /// <returns>Y value of the point.</returns>
1785 virtual public double GetYValue(
1786 CommonElements common,
1793 return point.YValues[yValueIndex];
1798 #region SmartLabelStyle methods
1801 /// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
1803 /// <param name="common">Common chart elements.</param>
1804 /// <param name="area">Chart area.</param>
1805 /// <param name="series">Series values to be used.</param>
1806 /// <param name="list">List to add to.</param>
1807 public void AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)
1809 // Check if series is indexed
1810 bool indexedSeries = ChartHelper.IndexedSeries(common, area.GetSeriesFromChartType(this.Name).ToArray() );
1812 //************************************************************
1813 //** Set active horizontal/vertical axis
1814 //************************************************************
1815 Axis hAxis = area.GetAxis(AxisName.X, series.XAxisType, series.XSubAxisName);
1816 Axis vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
1818 //************************************************************
1819 //** Loop through all data points in the series
1820 //************************************************************
1821 int markerIndex = 0; // Marker index
1822 int index = 1; // Data points loop
1823 foreach( DataPoint point in series.Points )
1825 //************************************************************
1826 //** Check if point values are in the chart area
1827 //************************************************************
1829 // Check for min/max Y values
1830 double yValue = GetYValue(common, area, series, point, index - 1, 0);
1832 // Axis is Logarithmic
1833 yValue = vAxis.GetLogValue( yValue );
1835 if( yValue > vAxis.ViewMaximum || yValue < vAxis.ViewMinimum)
1841 // Check for min/max X values
1842 double xValue = (indexedSeries) ? (double)index : point.XValue;
1843 xValue = hAxis.GetLogValue(xValue);
1844 if(xValue > hAxis.ViewMaximum || xValue < hAxis.ViewMinimum)
1850 //************************************************************
1851 //** Get marker position and size
1852 //************************************************************
1854 // Get marker position
1855 PointF markerPosition = PointF.Empty;
1856 markerPosition.Y = (float)vAxis.GetLinearPosition(yValue);
1859 // The formula for position is based on a distance
1860 // from the grid line or nPoints position.
1861 markerPosition.X = (float)hAxis.GetPosition( (double)index );
1865 markerPosition.X = (float)hAxis.GetPosition( point.XValue );
1868 // Get point some point properties and save them in variables
1869 string pointMarkerImage = point.MarkerImage;
1870 MarkerStyle pointMarkerStyle = point.MarkerStyle;
1873 SizeF markerSize = SizeF.Empty;
1874 markerSize.Width = point.MarkerSize;
1875 markerSize.Height = point.MarkerSize;
1876 if (common != null && common.graph != null && common.graph.Graphics != null)
1878 // Marker size is in pixels and we do the mapping for higher DPIs
1879 markerSize.Width = point.MarkerSize * common.graph.Graphics.DpiX / 96;
1880 markerSize.Height = point.MarkerSize * common.graph.Graphics.DpiY / 96;
1883 if (point.MarkerImage.Length > 0)
1884 if(common.graph != null)
1885 common.ImageLoader.GetAdjustedImageSize(point.MarkerImage, common.graph.Graphics, ref markerSize);
1887 // Transform marker position in 3D space
1888 if(area.Area3DStyle.Enable3D)
1890 // Get series depth and Z position
1891 float seriesDepth, seriesZPosition;
1892 area.GetSeriesZPositionAndDepth(series, out seriesDepth, out seriesZPosition);
1894 Point3D[] marker3DPosition = new Point3D[1];
1895 marker3DPosition[0] = new Point3D(
1898 (float)(seriesZPosition + seriesDepth/2f));
1900 // Transform coordinates
1901 area.matrix3D.TransformPoints(marker3DPosition);
1902 markerPosition = marker3DPosition[0].PointF;
1905 // Check if marker visible
1906 if(pointMarkerStyle != MarkerStyle.None ||
1907 pointMarkerImage.Length > 0)
1909 // Check marker index
1910 if(markerIndex == 0)
1912 markerSize = common.graph.GetRelativeSize(markerSize);
1914 // Add marker position into the list
1915 RectangleF markerRect = new RectangleF(
1916 markerPosition.X - markerSize.Width / 2f,
1917 markerPosition.Y - markerSize.Height,
1920 list.Add(markerRect);
1923 // Increase the markers counter
1925 if(series.MarkerStep == markerIndex)
1937 #region IDisposable interface implementation
1939 /// Releases unmanaged and - optionally - managed resources
1941 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
1942 protected virtual void Dispose(bool disposing)
1944 //Nothing to dispose at the base class.
1948 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
1950 public void Dispose()
1953 GC.SuppressFinalize(this);