Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / ThreeLineBreakChart.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               ThreeLineBreakChart.cs
9 //
10 //  Namespace:  DataVisualization.Charting.ChartTypes
11 //
12 //  Purpose:    ThreeLineBreak chart type provides methods for 
13 //              calculations and depends on the Range Column chart 
14 //              type to do all the drawing. PrepareData method is 
15 //              used to create temporary RangeColumn series and fill 
16 //              it with data. Changes are then reversed in the 
17 //              UnPrepareData method.
18 //
19 //      ThreeLineBreak Chart Overview:
20 //      ------------------------------
21 //  
22 //  The Three Line Break chart is popular in Japan for financial 
23 //  charting. These charts display a series of vertical boxes ("lines") 
24 //  that reflect changes in price values. Similar to Kagi, Renko, and 
25 //  Point & Figure charts, the Three Line Break chart ignores the 
26 //  passage of time.
27 //  
28 //  The Three Line Break charting method is so-named because of the 
29 //  number of lines typically used. Each line may indicate "Buy", 
30 //  "Sell", and "trend less" markets. An advantage of Three Line Break 
31 //  charts is that there is no arbitrary fixed reversal amount. It is 
32 //  the price action which gives the indication of a reversal. The 
33 //  disadvantage of Three Line Break charts is that the signals are 
34 //  generated after the new trend is well under way. However, many 
35 //  traders are willing to accept the late signals in exchange for 
36 //  calling major trends.
37 //  
38 //  The sensitivity of the reversal criteria can be set by changing 
39 //  the number of lines in the break. For example, short-term traders 
40 //  might use two-line breaks to get more reversals, while a 
41 //  longer-term investor might use four-line, or even 10-line breaks 
42 //  to reduce the number of reversals. This is done using the 
43 //  NumberOfLinesInBreak custom attribute.
44 //  
45 //  The following should be taken into account when working with 
46 //  Three Line Break charts:
47 //  
48 //  - The X values of data points are automatically indexed. 
49 //  
50 //  - There is a formula applied to the original data before that data 
51 //  gets plotted. This formula changes the number of points in the data, 
52 //  and also changes the data points' X/Y values. 
53 //  
54 //  - Due to data being recalculated, we do not recommend setting the 
55 //  minimum and/or maximum values for the X axis. This is because it 
56 //  cannot be determined how many data points will actually be plotted. 
57 //  However, if the axis' Maximum, or Minimum is set, then the Maximum, 
58 //  or Minimum properties should use data point index values. 
59 //  
60 //  - Data point anchoring, used for annotations, is not supported in 
61 //  this type of chart. 
62 //  
63 //      Reviewed:       AG - Microsoft 7, 2007
64 //
65 //===================================================================
66
67 #region Used namespaces
68
69 using System;
70 using System.Resources;
71 using System.Reflection;
72 using System.Collections;
73 using System.Drawing;
74 using System.Drawing.Drawing2D;
75 using System.ComponentModel.Design;
76 using System.Globalization;
77
78 #if Microsoft_CONTROL
79         using System.Windows.Forms.DataVisualization.Charting;
80         using System.Windows.Forms.DataVisualization.Charting.Data;
81         using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
82         using System.Windows.Forms.DataVisualization.Charting.Utilities;
83         using System.Windows.Forms.DataVisualization.Charting.Borders3D;
84 #else
85 using System.Web.UI.DataVisualization.Charting;
86
87 using System.Web.UI.DataVisualization.Charting.ChartTypes;
88 using System.Web.UI.DataVisualization.Charting.Data;
89 using System.Web.UI.DataVisualization.Charting.Utilities;
90 #endif
91
92 #endregion
93
94 #if Microsoft_CONTROL
95         namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
96 #else
97 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
98 #endif
99 {
100         /// <summary>
101     /// ThreeLineBreakChart class provides methods to perform all nessesary 
102     /// calculations to display ThreeLineBreak chart with the help of the 
103     /// temporary RangeColumn series. This series is created in the 
104     /// PrepareData method and then removed in the UnPrepareData method.
105         /// </summary>
106         internal class ThreeLineBreakChart : IChartType
107         {
108                 #region Methods
109
110                 /// <summary>
111                 /// Prepares ThreeLineBreak chart type for rendering.
112                 /// </summary>
113                 /// <param name="series">Series to be prepared.</param>
114                 internal static void PrepareData(Series series)
115                 {
116                         // Check series chart type
117                         if(String.Compare(series.ChartTypeName, ChartTypeNames.ThreeLineBreak, StringComparison.OrdinalIgnoreCase ) != 0 || !series.IsVisible())
118                         {
119                                 return;
120                         }
121
122                         // Get reference to the chart control
123                         Chart   chart = series.Chart;
124                         if(chart == null)
125                         {
126                 throw (new InvalidOperationException(SR.ExceptionThreeLineBreakNullReference));
127                         }
128
129             // ThreeLineBreak chart may not be combined with any other chart types
130             ChartArea area = chart.ChartAreas[series.ChartArea];
131             foreach (Series currentSeries in chart.Series)
132             {
133                 if (currentSeries.IsVisible() && currentSeries != series && area == chart.ChartAreas[currentSeries.ChartArea])
134                 {
135                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakCanNotCobine));
136                 }
137             }
138
139                         // Create a temp series which will hold original series data points
140                         Series seriesOriginalData = new Series("THREELINEBREAK_ORIGINAL_DATA_" + series.Name, series.YValuesPerPoint);
141                         seriesOriginalData.Enabled = false;
142                         seriesOriginalData.IsVisibleInLegend = false;
143                         chart.Series.Add(seriesOriginalData);
144                         foreach(DataPoint dp in series.Points)
145                         {
146                                 seriesOriginalData.Points.Add(dp);
147                         }
148                         series.Points.Clear();
149                         if(series.IsCustomPropertySet("TempDesignData"))
150                         {
151                                 seriesOriginalData["TempDesignData"] = "true";
152                         }
153
154
155                         // Change ThreeLineBreak series type to range column
156                         series["OldXValueIndexed"] = series.IsXValueIndexed.ToString(CultureInfo.InvariantCulture);
157                         series["OldYValuesPerPoint"] = series.YValuesPerPoint.ToString(CultureInfo.InvariantCulture);
158                         series.ChartType = SeriesChartType.RangeColumn;
159                         series.IsXValueIndexed = true;
160                         series.YValuesPerPoint = 2;
161
162                         // Calculate date-time interval for indexed series
163                         if(series.ChartArea.Length > 0 &&
164                                 series.IsXValueDateTime())
165                         {
166                                 // Get X axis connected to the series
167                                 Axis            xAxis = area.GetAxis(AxisName.X, series.XAxisType, series.XSubAxisName);
168
169                                 // Change interval for auto-calculated interval only
170                                 if(xAxis.Interval == 0 && xAxis.IntervalType == DateTimeIntervalType.Auto)
171                                 {
172                                         // Check if original data has X values set to date-time values and
173                                         // calculate min/max X values.
174                                         bool    nonZeroXValues = false;
175                                         double  minX = double.MaxValue;
176                                         double  maxX = double.MinValue;
177                                         foreach(DataPoint dp in seriesOriginalData.Points)
178                                         {
179                                                 if(!dp.IsEmpty)
180                                                 {
181                                                         if(dp.XValue != 0.0)
182                                                         {
183                                                                 nonZeroXValues = true;
184                                                         }
185                                                         if(dp.XValue > maxX)
186                                                         {
187                                                                 maxX = dp.XValue;
188                                                         }
189                                                         if(dp.XValue < minX)
190                                                         {
191                                                                 minX = dp.XValue;
192                                                         }
193                                                 }
194                                         }
195
196                                         if(nonZeroXValues)
197                                         {
198                                                 // Save flag that axis interval is automatic
199                                                 series["OldAutomaticXAxisInterval"] = "true";
200
201                                                 // Calculate and set axis date-time interval
202                                                 DateTimeIntervalType    intervalType = DateTimeIntervalType.Auto;
203                                                 xAxis.interval = xAxis.CalcInterval(minX, maxX, true, out intervalType, series.XValueType);
204                                                 xAxis.intervalType = intervalType;
205                                         }
206                                 }
207                         }
208
209                         // Calculate ThreeLineBreak bricks data points values
210                         FillThreeLineBreakData(series, seriesOriginalData);
211                 }
212
213                 /// <summary>
214                 /// Remove any changes done while preparing ThreeLineBreak chart type for rendering.
215                 /// </summary>
216                 /// <param name="series">Series to be un-prepared.</param>
217                 /// <returns>True if series was removed from collection.</returns>
218                 internal static bool UnPrepareData(Series series)
219                 {
220             if (series.Name.StartsWith("THREELINEBREAK_ORIGINAL_DATA_", StringComparison.Ordinal))
221             {
222                 // Get reference to the chart control
223                 Chart chart = series.Chart;
224                 if (chart == null)
225                 {
226                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakNullReference));
227                 }
228
229                 // Get original ThreeLineBreak series
230                 Series threeLineBreakSeries = chart.Series[series.Name.Substring(29)];
231                 Series.MovePositionMarkers(threeLineBreakSeries, series);
232                 // Copy data back to original ThreeLineBreak series
233                 threeLineBreakSeries.Points.Clear();
234                 if (!series.IsCustomPropertySet("TempDesignData"))
235                 {
236                     foreach (DataPoint dp in series.Points)
237                     {
238                         threeLineBreakSeries.Points.Add(dp);
239                     }
240                 }
241
242                 // Restore ThreeLineBreak series properties
243                 threeLineBreakSeries.ChartType = SeriesChartType.ThreeLineBreak;
244
245                 bool xValIndexed;
246                 bool parseSucceed = bool.TryParse(threeLineBreakSeries["OldXValueIndexed"], out xValIndexed);
247                 threeLineBreakSeries.IsXValueIndexed = parseSucceed && xValIndexed;
248
249                 int yValsPerPoint;
250                 parseSucceed = int.TryParse(threeLineBreakSeries["OldYValuesPerPoint"], NumberStyles.Any, CultureInfo.InvariantCulture, out yValsPerPoint);
251
252                 if (parseSucceed)
253                 {
254                     threeLineBreakSeries.YValuesPerPoint = yValsPerPoint;
255                 }
256
257
258                 threeLineBreakSeries.DeleteCustomProperty("OldXValueIndexed");
259                 threeLineBreakSeries.DeleteCustomProperty("OldYValuesPerPoint");
260
261                 series["OldAutomaticXAxisInterval"] = "true";
262                 if (threeLineBreakSeries.IsCustomPropertySet("OldAutomaticXAxisInterval"))
263                 {
264                     threeLineBreakSeries.DeleteCustomProperty("OldAutomaticXAxisInterval");
265
266                     // Reset automatic interval for X axis
267                     if (threeLineBreakSeries.ChartArea.Length > 0)
268                     {
269                         // Get X axis connected to the series
270                         ChartArea area = chart.ChartAreas[threeLineBreakSeries.ChartArea];
271                         Axis xAxis = area.GetAxis(AxisName.X, threeLineBreakSeries.XAxisType, threeLineBreakSeries.XSubAxisName);
272
273                         xAxis.interval = 0.0;
274                         xAxis.intervalType = DateTimeIntervalType.Auto;
275                     }
276                 }
277
278                 // Remove series from the collection
279                 chart.Series.Remove(series);
280                 return true;
281             }
282
283                         return false;
284                 }
285
286                 /// <summary>
287                 /// Fills range column series with data to draw the ThreeLineBreak chart.
288                 /// </summary>
289                 /// <param name="series">Range column chart series used to dispaly the ThreeLineBreak chart.</param>
290                 /// <param name="originalData">Series with original data.</param>
291                 private static void FillThreeLineBreakData(Series series, Series originalData)
292                 {
293                         // Get index of the Y values used
294                         int     yValueIndex = 0;
295                         if(series.IsCustomPropertySet(CustomPropertyName.UsedYValue))
296                         {
297                                 try
298                                 {
299                                         yValueIndex = int.Parse(series[CustomPropertyName.UsedYValue], CultureInfo.InvariantCulture);
300                                 }
301                                 catch
302                                 {
303                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakUsedYValueInvalid));
304                                 }
305
306                                 if(yValueIndex >= series.YValuesPerPoint)
307                                 {
308                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakUsedYValueOutOfRange));
309                                 }
310                         }
311
312                         // Get number of lines in the break
313                         int     linesInBreak = 3;
314                         if(series.IsCustomPropertySet(CustomPropertyName.NumberOfLinesInBreak))
315                         {
316                                 try
317                                 {
318                                         linesInBreak = int.Parse(series[CustomPropertyName.NumberOfLinesInBreak], CultureInfo.InvariantCulture);
319                                 }
320                                 catch
321                                 {
322                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakNumberOfLinesInBreakFormatInvalid));
323                                 }
324
325                                 if(linesInBreak <= 0)
326                                 {
327                     throw (new InvalidOperationException(SR.ExceptionThreeLineBreakNumberOfLinesInBreakValueInvalid));
328                                 }
329                         }
330
331                         // Create an array to store the history of high/low values of drawn lines
332                         ArrayList       highLowHistory = new ArrayList();
333
334                         // Fill points
335                         double  prevLow = double.NaN;
336                         double  prevHigh = double.NaN;
337                         int             sameDirectionLines = 0;
338                         int             prevDirection = 0;
339                         int             pointIndex = 0;
340                         foreach(DataPoint dataPoint in originalData.Points)
341                         {
342                                 int     direction = 0;  // 1 up; -1 down
343
344                                 // Skip empty points
345                                 if(dataPoint.IsEmpty)
346                                 {
347                                         ++pointIndex;
348                                         continue;
349                                 }
350
351                                 // Check if previus values exists
352                                 if(double.IsNaN(prevLow) || double.IsNaN(prevHigh))
353                                 {
354                                         prevHigh = dataPoint.YValues[yValueIndex];
355                                         prevLow = dataPoint.YValues[yValueIndex];
356                                         ++pointIndex;
357                                         continue;
358                                 }
359
360                                 // Get up price color
361                                 Color   priceUpColor = Color.Transparent;
362                                 string  priceUpColorString = dataPoint[CustomPropertyName.PriceUpColor];
363                                 if(priceUpColorString == null)
364                                 {
365                                         priceUpColorString = series[CustomPropertyName.PriceUpColor];
366                                 }
367                                 if(priceUpColorString != null)
368                                 {
369                                         try
370                                         {
371                                                 ColorConverter colorConverter = new ColorConverter();
372                                                 priceUpColor = (Color)colorConverter.ConvertFromString(null, CultureInfo.InvariantCulture, priceUpColorString);
373                                         }
374                                         catch
375                                         {
376                         throw (new InvalidOperationException(SR.ExceptionThreeLineBreakUpBrickColorInvalid));
377                                         }
378                                 }
379
380                                 // Check if close value exceeds last brick position by box size
381                                 if(dataPoint.YValues[yValueIndex] > prevHigh)
382                                 {
383                                         direction = 1;
384                                 }
385                                 else if(dataPoint.YValues[yValueIndex] < prevLow)
386                                 {
387                                         direction = -1;
388                                 }
389                                 else
390                                 {
391                                         direction = 0;
392                                 }
393
394                                 // Process up/down direction
395                                 if(direction != 0)
396                                 {
397                                         // Check if direction is same as previous
398                                         if(prevDirection == direction)
399                                         {
400                                                 ++sameDirectionLines;
401                                         }
402                                         else
403                                         {
404                                                 // If number of lines darwn in same direction is more or equal
405                                                 // to number of lines in the break, the price must extend the 
406                                                 // high or low price of the lines in the whole break.
407                                                 if(sameDirectionLines >= linesInBreak)
408                                                 {
409                                                         if(direction == 1)
410                                                         {
411                                                                 // Calculate high value for the last N lines
412                                                                 double lineBreakHigh = double.MinValue;
413                                                                 for(int index = 0; index < highLowHistory.Count; index += 2)
414                                                                 {
415                                                                         if(((double)highLowHistory[index]) > lineBreakHigh)
416                                                                         {
417                                                                                 lineBreakHigh = ((double)highLowHistory[index]);
418                                                                         }
419                                                                 }
420
421                                                                 // If point value is less - ignore it
422                                                                 if(dataPoint.YValues[yValueIndex] <= lineBreakHigh)
423                                                                 {
424                                                                         direction = 0;
425                                                                 }
426                                                         }
427                                                         else if(direction == -1)
428                                                         {
429                                                                 // Calculate low value for the last N lines
430                                                                 double lineBreakLow = double.MaxValue;
431                                                                 for(int index = 1; index < highLowHistory.Count; index += 2)
432                                                                 {
433                                                                         if(((double)highLowHistory[index]) < lineBreakLow)
434                                                                         {
435                                                                                 lineBreakLow = ((double)highLowHistory[index]);
436                                                                         }
437                                                                 }
438
439                                                                 // If point value is more - ignore it
440                                                                 if(dataPoint.YValues[yValueIndex] >= lineBreakLow)
441                                                                 {
442                                                                         direction = 0;
443                                                                 }
444                                                         }
445                                                 }
446
447                                                 if(direction != 0)
448                                                 {
449                                                         sameDirectionLines = 1;
450                                                 }
451                                         }
452
453                                         if(direction != 0)
454                                         {
455                                                 // Add point
456                                                 DataPoint newDataPoint = (DataPoint)dataPoint.Clone();
457                                                 newDataPoint["OriginalPointIndex"] = pointIndex.ToString(CultureInfo.InvariantCulture);
458                                                 newDataPoint.series = series;
459                                                 newDataPoint.YValues = new double[2];
460                                                 newDataPoint.XValue = dataPoint.XValue;
461                         newDataPoint.Tag = dataPoint;
462                                                 if(direction == 1)
463                                                 {
464                                                         newDataPoint.YValues[1] = prevHigh;
465                                                         newDataPoint.YValues[0] = dataPoint.YValues[yValueIndex];
466                                                         prevLow = prevHigh;
467                                                         prevHigh = dataPoint.YValues[yValueIndex];
468
469                                                         // Set ThreeLineBreak up brick appearance
470                                                         newDataPoint.Color = priceUpColor;
471                                                         if(newDataPoint.BorderWidth < 1)
472                                                         {
473                                                                 newDataPoint.BorderWidth = 1;
474                                                         }
475                                                         if(newDataPoint.BorderDashStyle == ChartDashStyle.NotSet)
476                                                         {
477                                                                 newDataPoint.BorderDashStyle = ChartDashStyle.Solid;
478                                                         }
479                                                         if( (newDataPoint.BorderColor == Color.Empty || newDataPoint.BorderColor == Color.Transparent) &&
480                                                                 (newDataPoint.Color == Color.Empty || newDataPoint.Color == Color.Transparent) )
481                                                         {
482                                                                 newDataPoint.BorderColor = series.Color;
483                                                         }
484                                                 }
485                                                 else
486                                                 {
487                                                         newDataPoint.YValues[1] = prevLow;
488                                                         newDataPoint.YValues[0] = dataPoint.YValues[yValueIndex];
489                                                         prevHigh = prevLow;
490                                                         prevLow = dataPoint.YValues[yValueIndex];
491                                                 }
492
493                                                 // Add ThreeLineBreak brick to the range column series
494                                                 series.Points.Add(newDataPoint);
495
496                                                 // Remember high/low values of drawn line
497                                                 highLowHistory.Add(prevHigh);
498                                                 highLowHistory.Add(prevLow);
499
500                                                 // Do not store all values in array only number of break lines
501                                                 if(highLowHistory.Count > linesInBreak * 2)
502                                                 {
503                                                         // Remove two items at a time (high & low)
504                                                         highLowHistory.RemoveAt(0);
505                                                         highLowHistory.RemoveAt(0);
506                                                 }
507                                         }
508                                 }
509
510                                 // Remember last direction
511                                 if(direction != 0)
512                                 {
513                                         prevDirection = direction;
514                                 }
515
516                                 ++pointIndex;
517                         }
518                 }
519
520                 #endregion // Methods
521
522                 #region Painting and Selection methods
523
524                 /// <summary>
525                 /// Paint chart.
526                 /// </summary>
527                 /// <param name="graph">The Chart Graphics object.</param>
528                 /// <param name="common">The Common elements object.</param>
529                 /// <param name="area">Chart area for this chart.</param>
530                 /// <param name="seriesToDraw">Chart series to draw.</param>
531                 virtual public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
532                 {
533             // Three Line Break series is never drawn directly. It is replaced with the range column chart. 
534             // See PrepareData method.
535                 }
536                 #endregion
537
538                 #region IChartType interface implementation
539
540                 /// <summary>
541                 /// Chart type name
542                 /// </summary>
543                 virtual public string Name                      { get{ return ChartTypeNames.ThreeLineBreak;}}
544
545                 /// <summary>
546                 /// True if chart type is stacked
547                 /// </summary>
548                 virtual public bool Stacked             { get{ return false;}}
549
550
551                 /// <summary>
552                 /// True if stacked chart type supports groups
553                 /// </summary>
554                 virtual public bool SupportStackedGroups        { get { return false; } }
555
556
557                 /// <summary>
558                 /// True if stacked chart type should draw separately positive and 
559                 /// negative data points ( Bar and column Stacked types ).
560                 /// </summary>
561                 public bool StackSign           { get{ return false;}}
562
563                 /// <summary>
564                 /// True if chart type supports axeses
565                 /// </summary>
566                 virtual public bool RequireAxes { get{ return true;} }
567
568                 /// <summary>
569                 /// Chart type with two y values used for scale ( bubble chart type )
570                 /// </summary>
571                 public bool SecondYScale{ get{ return false;} }
572
573                 /// <summary>
574                 /// True if chart type requires circular chart area.
575                 /// </summary>
576                 public bool CircularChartArea   { get{ return false;} }
577
578                 /// <summary>
579                 /// True if chart type supports logarithmic axes
580                 /// </summary>
581                 virtual public bool SupportLogarithmicAxes      { get{ return true;} }
582
583                 /// <summary>
584                 /// True if chart type requires to switch the value (Y) axes position
585                 /// </summary>
586                 virtual public bool SwitchValueAxes     { get{ return false;} }
587
588                 /// <summary>
589                 /// True if chart series can be placed side-by-side.
590                 /// </summary>
591                 public bool SideBySideSeries { get{ return false;} }
592
593                 /// <summary>
594                 /// True if each data point of a chart must be represented in the legend
595                 /// </summary>
596                 virtual public bool DataPointsInLegend  { get{ return false;} }
597
598                 /// <summary>
599                 /// If the crossing value is auto Crossing value should be 
600                 /// automatically set to zero for some chart 
601                 /// types (Bar, column, area etc.)
602                 /// </summary>
603                 virtual public bool ZeroCrossing { get{ return false;} }
604
605                 /// <summary>
606                 /// True if palette colors should be applied for each data paoint.
607                 /// Otherwise the color is applied to the series.
608                 /// </summary>
609                 virtual public bool ApplyPaletteColorsToPoints  { get { return false; } }
610
611                 /// <summary>
612                 /// Indicates that extra Y values are connected to the scale of the Y axis
613                 /// </summary>
614                 virtual public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
615                 
616                 /// <summary>
617                 /// Indicates that it's a hundredred percent chart.
618                 /// Axis scale from 0 to 100 percent should be used.
619                 /// </summary>
620                 virtual public bool HundredPercent{ get{return false;} }
621
622                 /// <summary>
623                 /// Indicates that it's a hundredred percent chart.
624                 /// Axis scale from 0 to 100 percent should be used.
625                 /// </summary>
626                 virtual public bool HundredPercentSupportNegative{ get{return false;} }
627
628                 /// <summary>
629                 /// How to draw series/points in legend:
630                 /// Filled rectangle, Line or Marker
631                 /// </summary>
632                 /// <param name="series">Legend item series.</param>
633                 /// <returns>Legend item style.</returns>
634                 virtual public LegendImageStyle GetLegendImageStyle(Series series)
635                 {
636                         return LegendImageStyle.Rectangle;
637                 }
638         
639                 /// <summary>
640                 /// Number of supported Y value(s) per point 
641                 /// </summary>
642                 virtual public int YValuesPerPoint      { get { return 1; } }
643
644                 /// <summary>
645                 /// Gets chart type image.
646                 /// </summary>
647                 /// <param name="registry">Chart types registry object.</param>
648                 /// <returns>Chart type image.</returns>
649                 virtual public System.Drawing.Image GetImage(ChartTypeRegistry registry)
650                 {
651                         return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
652                 }
653                 #endregion
654
655                 #region Y values related methods
656
657                 /// <summary>
658                 /// Helper function, which returns the Y value of the point.
659                 /// </summary>
660                 /// <param name="common">Chart common elements.</param>
661                 /// <param name="area">Chart area the series belongs to.</param>
662                 /// <param name="series">Sereis of the point.</param>
663                 /// <param name="point">Point object.</param>
664                 /// <param name="pointIndex">Index of the point.</param>
665                 /// <param name="yValueIndex">Index of the Y value to get.</param>
666                 /// <returns>Y value of the point.</returns>
667                 virtual public double GetYValue(
668                         CommonElements common, 
669                         ChartArea area, 
670                         Series series, 
671                         DataPoint point, 
672                         int pointIndex, 
673                         int yValueIndex)
674                 {
675                         return point.YValues[yValueIndex];
676                 }
677
678                 #endregion
679
680                 #region SmartLabelStyle methods
681
682                 /// <summary>
683                 /// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
684                 /// </summary>
685                 /// <param name="common">Common chart elements.</param>
686                 /// <param name="area">Chart area.</param>
687                 /// <param name="series">Series values to be used.</param>
688                 /// <param name="list">List to add to.</param>
689                 public void AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)
690                 {
691                 }
692
693                 #endregion
694
695         #region IDisposable interface implementation
696         /// <summary>
697         /// Releases unmanaged and - optionally - managed resources
698         /// </summary>
699         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
700         protected virtual void Dispose(bool disposing)
701         {
702             //Nothing to dispose at the base class. 
703         }
704
705         /// <summary>
706         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
707         /// </summary>
708         public void Dispose()
709         {
710             Dispose(true);
711             GC.SuppressFinalize(this);
712         }
713         #endregion
714         }
715 }
716