1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
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. “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.
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);