Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / StockChart.cs
blobca1b33de85bd84184b482c010e9ca946ccb4177b
1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: StockChart.cs
9 //
10 // Namespace: DataVisualization.Charting.ChartTypes
12 // Classes: StockChart, CandleStickChart
14 // Purpose: Stock chart requires 4 Y values High, Low, Open and Close.
15 //
16 // The Stock chart displays opening and closing values by using
17 // markers, which are typically lines or triangles. “OpenCloseStyle”
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.
21 //
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
35 using System;
36 using System.Collections;
37 using System.Drawing;
38 using System.Drawing.Drawing2D;
39 using System.Collections.Generic;
41 #if Microsoft_CONTROL
42 using System.Windows.Forms.DataVisualization.Charting.Utilities;
43 #else
44 using System.Web.UI.DataVisualization.Charting.Utilities;
45 #endif
47 #endregion
49 #if Microsoft_CONTROL
50 namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
51 #else
52 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
53 #endif
55 #region Open/close marks style enumeration
57 /// <summary>
58 /// Style of the Open-Close marks in the stock chart
59 /// </summary>
60 internal enum StockOpenCloseMarkStyle
62 /// <summary>
63 /// Line
64 /// </summary>
65 Line,
67 /// <summary>
68 /// Triangle
69 /// </summary>
70 Triangle,
72 /// <summary>
73 /// CandleStick. Color of the bar depends if Open value was bigger than Close value.
74 /// </summary>
75 Candlestick
78 #endregion
80 /// <summary>
81 /// CandleStick class provides chart unique name and changes the marking
82 /// style in the StockChart class to StockOpenCloseMarkStyle.CandleStick.
83 /// </summary>
84 internal class CandleStickChart : StockChart
86 #region Constructor
88 /// <summary>
89 /// CandleStick chart constructor.
90 /// </summary>
91 public CandleStickChart() : base(StockOpenCloseMarkStyle.Candlestick)
93 forceCandleStick = true;
96 #endregion
98 #region IChartType interface implementation
100 /// <summary>
101 /// Chart type name
102 /// </summary>
103 override public string Name { get{ return ChartTypeNames.Candlestick;}}
105 /// <summary>
106 /// Gets chart type image.
107 /// </summary>
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");
115 #endregion
118 /// <summary>
119 /// StockChart class provides 2D/3D drawing and hit testing
120 /// functionality for the Stock and CandleStick charts.
121 /// </summary>
122 internal class StockChart : IChartType
124 #region Fields
126 /// <summary>
127 /// Vertical axis
128 /// </summary>
129 internal Axis VAxis { get; set; }
131 /// <summary>
132 /// Horizontal axis
133 /// </summary>
134 internal Axis HAxis { get; set; }
136 /// <summary>
137 /// Default open-close style
138 /// </summary>
139 protected StockOpenCloseMarkStyle openCloseStyle = StockOpenCloseMarkStyle.Line;
141 /// <summary>
142 /// Indicates that only candle-stick type of the open-close marks should be used
143 /// </summary>
144 protected bool forceCandleStick = false;
146 #endregion
148 #region Constructor
150 /// <summary>
151 /// Stock chart constructor.
152 /// </summary>
153 public StockChart()
157 /// <summary>
158 /// Stock chart constructor.
159 /// </summary>
160 /// <param name="style">Open-close marks default style.</param>
161 public StockChart(StockOpenCloseMarkStyle style)
163 this.openCloseStyle = style;
166 #endregion
168 #region IChartType interface implementation
170 /// <summary>
171 /// Chart type name
172 /// </summary>
173 virtual public string Name { get{ return ChartTypeNames.Stock;}}
175 /// <summary>
176 /// True if chart type is stacked
177 /// </summary>
178 virtual public bool Stacked { get{ return false;}}
181 /// <summary>
182 /// True if stacked chart type supports groups
183 /// </summary>
184 virtual public bool SupportStackedGroups { get { return false; } }
187 /// <summary>
188 /// True if stacked chart type should draw separately positive and
189 /// negative data points ( Bar and column Stacked types ).
190 /// </summary>
191 public bool StackSign { get{ return false;}}
193 /// <summary>
194 /// True if chart type supports axeses
195 /// </summary>
196 virtual public bool RequireAxes { get{ return true;} }
198 /// <summary>
199 /// Chart type with two y values used for scale ( bubble chart type )
200 /// </summary>
201 public bool SecondYScale{ get{ return false;} }
203 /// <summary>
204 /// True if chart type requires circular chart area.
205 /// </summary>
206 public bool CircularChartArea { get{ return false;} }
208 /// <summary>
209 /// True if chart type supports Logarithmic axes
210 /// </summary>
211 virtual public bool SupportLogarithmicAxes { get{ return true;} }
213 /// <summary>
214 /// True if chart type requires to switch the value (Y) axes position
215 /// </summary>
216 virtual public bool SwitchValueAxes { get{ return false;} }
218 /// <summary>
219 /// True if chart series can be placed side-by-side.
220 /// </summary>
221 public bool SideBySideSeries { get{ return false;} }
223 /// <summary>
224 /// True if each data point of a chart must be represented in the legend
225 /// </summary>
226 virtual public bool DataPointsInLegend { get{ return false;} }
228 /// <summary>
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.)
232 /// </summary>
233 virtual public bool ZeroCrossing { get{ return false;} }
235 /// <summary>
236 /// True if palette colors should be applied for each data paoint.
237 /// Otherwise the color is applied to the series.
238 /// </summary>
239 virtual public bool ApplyPaletteColorsToPoints { get { return false; } }
241 /// <summary>
242 /// Indicates that extra Y values are connected to the scale of the Y axis
243 /// </summary>
244 virtual public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
246 /// <summary>
247 /// Indicates that it's a hundredred percent chart.
248 /// Axis scale from 0 to 100 percent should be used.
249 /// </summary>
250 virtual public bool HundredPercent{ get{return false;} }
252 /// <summary>
253 /// Indicates that it's a hundredred percent chart.
254 /// Axis scale from 0 to 100 percent should be used.
255 /// </summary>
256 virtual public bool HundredPercentSupportNegative{ get{return false;} }
258 /// <summary>
259 /// How to draw series/points in legend:
260 /// Filled rectangle, Line or Marker
261 /// </summary>
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;
269 /// <summary>
270 /// Number of supported Y value(s) per point
271 /// </summary>
272 virtual public int YValuesPerPoint { get { return 4; } }
274 /// <summary>
275 /// Gets chart type image.
276 /// </summary>
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");
283 #endregion
285 #region Painting and Selection methods
287 /// <summary>
288 /// Paint stock chart.
289 /// </summary>
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 );
299 /// <summary>
300 /// This method recalculates size of the bars. This method is used
301 /// from Paint or Select method.
302 /// </summary>
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(
309 bool selection,
310 ChartGraphics graph,
311 CommonElements common,
312 ChartArea area,
313 Series seriesToDraw )
316 // Prosess 3D chart type
317 if(area.Area3DStyle.Enable3D)
319 ProcessChartType3D( selection, graph, common, area, seriesToDraw );
320 return;
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())
339 continue;
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
359 if( !selection )
361 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
365 //************************************************************
366 //** Series data points loop
367 //************************************************************
368 int index = 1;
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;
376 if( indexedSeries )
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) )
392 ++index;
393 continue;
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 );
438 // Draw Hi-Low line
439 graph.DrawLineRel(
440 point.Color,
441 point.BorderWidth,
442 point.BorderDashStyle,
443 new PointF(xPosition, (float)high),
444 new PointF(xPosition, (float)low),
445 ser.ShadowColor,
446 ser.ShadowOffset );
448 // Draw Open-Close marks
449 DrawOpenCloseMarks(graph, area, ser, point, xPosition, width);
451 // End Svg Selection mode
452 graph.EndHotRegion( );
454 // Reset Clip Region
455 if(clipRegionSet)
457 graph.ResetClip();
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(
471 areaRect,
472 point,
473 ser.Name,
474 index - 1 );
477 ++index;
480 //************************************************************
481 //** Second series data points loop, when markers and labels
482 //** are drawn.
483 //************************************************************
485 int markerIndex = 0;
486 index = 1;
487 foreach( DataPoint point in ser.Points )
489 // Get point X position
490 double xValue = point.XValue;
491 if( indexedSeries )
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) )
507 ++index;
508 continue;
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);
535 // Draw marker
536 if(point.MarkerStyle != MarkerStyle.None || point.MarkerImage.Length > 0)
538 // Get marker size
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;
557 // Draw marker
558 if(markerIndex == 0)
560 // Draw the marker
561 graph.DrawMarkerRel(markerPosition,
562 point.MarkerStyle,
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,
567 point.MarkerImage,
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(
591 insertIndex,
592 graph,
593 circCoord[0],
594 circCoord[1],
595 circCoord[2],
596 point,
597 ser.Name,
598 index - 1 );
600 // All other markers represented as rectangles
601 else
603 common.HotRegionsList.AddHotRegion(
604 new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
605 point,
606 ser.Name,
607 index - 1 );
613 // Increase the markers counter
614 ++markerIndex;
615 if(ser.MarkerStep == markerIndex)
617 markerIndex = 0;
621 // Draw label
622 DrawLabel(common, area, graph, ser, point, new PointF(xPosition, (float)Math.Min(high, low)), index);
624 // Increase point counter
625 ++index;
628 // Call Paint event
629 if( !selection )
631 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
636 /// <summary>
637 /// Draws stock chart open-close marks depending on selected style.
638 /// </summary>
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(
646 ChartGraphics graph,
647 ChartArea area,
648 Series ser,
649 DataPoint point,
650 float xPosition,
651 float width)
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) )
662 //return;
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;
671 // Detect style
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)
716 showOpen = true;
717 showClose = true;
719 else if (String.Compare(showOpenClose, "Open", StringComparison.OrdinalIgnoreCase) == 0)
721 showOpen = true;
722 showClose = false;
724 else if (String.Compare(showOpenClose, "Close", StringComparison.OrdinalIgnoreCase) == 0)
726 showOpen = false;
727 showClose = true;
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)
753 bool failed = false;
756 priceUpColor = (Color)colorConverter.ConvertFromString(attrValue);
758 catch (ArgumentException)
760 failed = true;
762 catch (NotSupportedException)
764 failed = true;
767 if (failed)
769 priceUpColor = (Color)colorConverter.ConvertFromInvariantString(attrValue);
773 attrValue = point[CustomPropertyName.PriceDownColor];
774 if(attrValue != null && attrValue.Length > 0)
776 bool failed = false;
779 priceDownColor = (Color)colorConverter.ConvertFromString(attrValue);
781 catch (ArgumentException)
783 failed = true;
785 catch (NotSupportedException)
787 failed = true;
790 if (failed)
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;
801 rect.Width = width;
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(
815 rect,
816 barColor,
817 point.BackHatchStyle,
818 point.BackImage,
819 point.BackImageWrapMode,
820 point.BackImageTransparentColor,
821 point.BackImageAlignment,
822 point.BackGradientStyle,
823 point.BackSecondaryColor,
824 barBorderColor,
825 point.BorderWidth,
826 point.BorderDashStyle,
827 ser.ShadowColor,
828 ser.ShadowOffset,
829 PenAlignment.Inset );
831 else
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
852 if (showOpen)
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
864 if (showClose)
866 if (closeY <= VAxis.ViewMaximum && closeY >= VAxis.ViewMinimum)
868 path.Reset();
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
884 else
886 // Draw Open mark line
887 if(showOpen)
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
899 if(showClose)
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 );
911 // Reset Clip Region
912 if(clipRegionSet)
914 graph.ResetClip();
918 /// <summary>
919 /// Draws stock chart data point label.
920 /// </summary>
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,
930 ChartArea area,
931 ChartGraphics graph,
932 Series ser,
933 DataPoint point,
934 PointF position,
935 int pointIndex)
937 if(ser.IsValueShownAsLabel || point.IsValueShownAsLabel || point.Label.Length > 0)
939 // Label text format
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;
950 // Get label text
951 string text;
952 if (point.Label.Length == 0)
954 // Check what value to show (High, Low, Open, Close)
955 int valueIndex = 3;
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)
968 valueIndex = 0;
970 else if (String.Compare(valueType, "Low", StringComparison.OrdinalIgnoreCase) == 0)
972 valueIndex = 1;
974 else if (String.Compare(valueType, "Open", StringComparison.OrdinalIgnoreCase) == 0)
976 valueIndex = 2;
979 text = ValueConverter.FormatValue(
980 ser.Chart,
981 point,
982 point.Tag,
983 point.YValues[valueIndex],
984 point.LabelFormat,
985 ser.YValueType,
986 ChartElementType.DataPoint);
988 else
990 text = point.ReplaceKeywords(point.Label);
993 // Get text angle
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)
1005 // Get marker size
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(
1025 common,
1026 graph,
1027 area,
1028 ser.SmartLabelStyle,
1029 position,
1030 sizeFont,
1031 format,
1032 position,
1033 markerSize,
1034 LabelAlignmentStyles.Top);
1036 // Smart labels always use 0 degrees text angle
1037 textAngle = 0;
1043 // Draw label
1044 if (!position.IsEmpty)
1046 RectangleF labelBackPosition = RectangleF.Empty;
1048 if (!point.LabelBackColor.IsEmpty ||
1049 point.LabelBorderWidth > 0 ||
1050 !point.LabelBorderColor.IsEmpty)
1052 // Get text size
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(
1066 graph,
1067 position,
1068 sizeLabel,
1069 format,
1070 true);
1074 // Draw label text
1075 using (Brush brush = new SolidBrush(point.LabelForeColor))
1077 graph.DrawPointLabelStringRel(
1078 common,
1079 text,
1080 point.Font,
1081 brush,
1082 position,
1083 format,
1084 textAngle,
1085 labelBackPosition,
1087 point.LabelBackColor,
1088 point.LabelBorderColor,
1089 point.LabelBorderWidth,
1090 point.LabelBorderDashStyle,
1091 ser,
1092 point,
1093 pointIndex - 1);
1101 #endregion
1103 #region 3D Drawing and Selection methods
1105 /// <summary>
1106 /// This method recalculates size of the bars. This method is used
1107 /// from Paint or Select method.
1108 /// </summary>
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(
1115 bool selection,
1116 ChartGraphics graph,
1117 CommonElements common,
1118 ChartArea area,
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())
1137 continue;
1140 // Check if drawn series is specified
1141 if(seriesToDraw != null && seriesToDraw.Name != ser.Name)
1143 continue;
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
1163 if( !selection )
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 //************************************************************
1177 int index = 1;
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;
1185 if( indexedSeries )
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) )
1201 ++index;
1202 continue;
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 );
1250 // Draw Hi-Low line
1251 graph.DrawLineRel(
1252 point.Color,
1253 point.BorderWidth,
1254 point.BorderDashStyle,
1255 points[0].PointF,
1256 points[1].PointF,
1257 ser.ShadowColor,
1258 ser.ShadowOffset );
1260 // Draw Open-Close marks
1261 DrawOpenCloseMarks3D(graph, area, ser, point, xPosition, width, seriesZPosition, seriesDepth);
1262 xPosition = points[0].X;
1263 high = points[0].Y;
1264 low = points[1].Y;
1266 // End Svg Selection mode
1267 graph.EndHotRegion( );
1269 // Reset Clip Region
1270 if(clipRegionSet)
1272 graph.ResetClip();
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(
1285 areaRect,
1286 point,
1287 ser.Name,
1288 index - 1 );
1292 ++index;
1295 //************************************************************
1296 //** Second series data points loop, when markers and labels
1297 //** are drawn.
1298 //************************************************************
1299 int markerIndex = 0;
1300 index = 1;
1301 foreach( DataPoint point in ser.Points )
1303 // Get point X position
1304 double xValue = point.XValue;
1305 if( indexedSeries )
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) )
1321 ++index;
1322 continue;
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;
1356 high = points[0].Y;
1357 low = points[1].Y;
1359 // Draw label
1360 DrawLabel(common, area, graph, ser, point, new PointF(xPosition, (float)Math.Min(high, low)), index);
1362 // Draw marker
1363 if(point.MarkerStyle != MarkerStyle.None || point.MarkerImage.Length > 0)
1365 // Get marker size
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;
1384 // Draw marker
1385 if(markerIndex == 0)
1387 // Draw the marker
1388 graph.DrawMarkerRel(markerPosition,
1389 point.MarkerStyle,
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,
1394 point.MarkerImage,
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(
1418 insertIndex,
1419 graph,
1420 circCoord[0],
1421 circCoord[1],
1422 circCoord[2],
1423 point,
1424 ser.Name,
1425 index - 1 );
1427 // All other markers represented as rectangles
1428 else
1430 common.HotRegionsList.AddHotRegion(
1431 new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
1432 point,
1433 ser.Name,
1434 index - 1 );
1439 // Increase the markers counter
1440 ++markerIndex;
1441 if(ser.MarkerStep == markerIndex)
1443 markerIndex = 0;
1446 ++index;
1449 // Call Paint event
1450 if( !selection )
1452 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
1457 /// <summary>
1458 /// Draws stock chart open-close marks depending on selected style.
1459 /// </summary>
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,
1470 ChartArea area,
1471 Series ser,
1472 DataPoint point,
1473 float xPosition,
1474 float width,
1475 float zPosition,
1476 float depth)
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) )
1487 //return;
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;
1496 // Detect style
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)
1541 showOpen = true;
1542 showClose = true;
1544 else if (String.Compare(showOpenClose, "Open", StringComparison.OrdinalIgnoreCase) == 0)
1546 showOpen = true;
1547 showClose = false;
1549 else if (String.Compare(showOpenClose, "Close", StringComparison.OrdinalIgnoreCase) == 0)
1551 showOpen = false;
1552 showClose = true;
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)
1585 failed = true;
1587 catch (ArgumentException)
1589 failed = true;
1592 if (failed)
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)
1608 failed = true;
1610 catch (NotSupportedException)
1612 failed = true;
1615 if (failed)
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;
1626 rect.Width = width;
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
1642 if(rect.Height > 1)
1644 graph.FillRectangleRel(
1645 rect,
1646 barColor,
1647 point.BackHatchStyle,
1648 point.BackImage,
1649 point.BackImageWrapMode,
1650 point.BackImageTransparentColor,
1651 point.BackImageAlignment,
1652 point.BackGradientStyle,
1653 point.BackSecondaryColor,
1654 barBorderColor,
1655 point.BorderWidth,
1656 point.BorderDashStyle,
1657 ser.ShadowColor,
1658 ser.ShadowOffset,
1659 PenAlignment.Inset);
1661 else
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
1689 if (showOpen)
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
1701 if (showClose)
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);
1713 path.Reset();
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
1725 else
1727 // Draw Open mark line
1728 if(showOpen)
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,
1739 points[0].PointF,
1740 points[1].PointF,
1741 ser.ShadowColor, ser.ShadowOffset );
1745 // Draw Close mark line
1746 if(showClose)
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,
1757 points[0].PointF,
1758 points[1].PointF,
1759 ser.ShadowColor, ser.ShadowOffset );
1764 // Reset Clip Region
1765 if(clipRegionSet)
1767 graph.ResetClip();
1771 #endregion
1773 #region Y values related methods
1775 /// <summary>
1776 /// Helper function, which returns the Y value of the point.
1777 /// </summary>
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,
1787 ChartArea area,
1788 Series series,
1789 DataPoint point,
1790 int pointIndex,
1791 int yValueIndex)
1793 return point.YValues[yValueIndex];
1796 #endregion
1798 #region SmartLabelStyle methods
1800 /// <summary>
1801 /// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
1802 /// </summary>
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)
1837 index++;
1838 continue;
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)
1846 index++;
1847 continue;
1850 //************************************************************
1851 //** Get marker position and size
1852 //************************************************************
1854 // Get marker position
1855 PointF markerPosition = PointF.Empty;
1856 markerPosition.Y = (float)vAxis.GetLinearPosition(yValue);
1857 if( indexedSeries )
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 );
1863 else
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;
1872 // Get marker size
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(
1896 markerPosition.X,
1897 markerPosition.Y,
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,
1918 markerSize.Width,
1919 markerSize.Height);
1920 list.Add(markerRect);
1923 // Increase the markers counter
1924 ++markerIndex;
1925 if(series.MarkerStep == markerIndex)
1927 markerIndex = 0;
1931 ++index;
1935 #endregion
1937 #region IDisposable interface implementation
1938 /// <summary>
1939 /// Releases unmanaged and - optionally - managed resources
1940 /// </summary>
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.
1947 /// <summary>
1948 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
1949 /// </summary>
1950 public void Dispose()
1952 Dispose(true);
1953 GC.SuppressFinalize(this);
1955 #endregion