1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: ColumnChart.cs
10 // Namespace: System.Web.UI.DataVisualization.Charting.ChartTypes
12 // Classes: ColumnChart, RangeColumnChart
14 // Purpose: Provides 2D/3D drawing and hit testing functionality
15 // for the Column and RangeColumn charts.
17 // Reviewed: AG - Aug 8, 2002
18 // AG - Microsoft 6, 2007
20 //===================================================================
22 #region Used namespaces
25 using System
.Collections
;
26 using System
.Collections
.Generic
;
28 using System
.Drawing
.Drawing2D
;
29 using System
.Globalization
;
32 using System
.Windows
.Forms
.DataVisualization
.Charting
.Utilities
;
34 using System
.Web
.UI
.DataVisualization
.Charting
.Utilities
;
40 namespace System
.Windows
.Forms
.DataVisualization
.Charting
.ChartTypes
42 namespace System
.Web
.UI
.DataVisualization
.Charting
.ChartTypes
46 /// ColumnChart class provides 2D/3D drawing and hit testing
47 /// functionality for the Column and RangeColumn charts. The
48 /// only difference between the RangeColumn and Column chart
49 /// is that 2 Y values are used to position top and bottom
50 /// side of each RangeColumn column.
52 internal class ColumnChart
: PointChart
57 /// Labels and markers have to be shifted if there
58 /// is more than one series for column chart.
60 private double _shiftedX
= 0;
63 /// Labels and markers have to be shifted if there
64 /// is more than one series for column chart. This property
65 /// will give a name of the series, which is used, for
66 /// labels and markers. Point chart
68 private string _shiftedSerName
= "";
71 /// Indicates that two Y values are used to calculate column position
73 protected bool useTwoValues
= false;
76 /// Indicates that columns from different series are drawn side by side
78 protected bool drawSeriesSideBySide
= true;
81 /// Coordinates of COP used when sorting 3D points order
83 protected COPCoordinates coordinates
= COPCoordinates
.X
;
87 #region IChartType interface implementation
92 override public string Name { get{ return ChartTypeNames.Column;}}
95 /// Gets chart type image.
97 /// <param name="registry">Chart types registry object.</param>
98 /// <returns>Chart type image.</returns>
99 override public System
.Drawing
.Image
GetImage(ChartTypeRegistry registry
)
101 return (System
.Drawing
.Image
)registry
.ResourceManager
.GetObject(this.Name
+ "ChartType");
105 /// True if chart type is stacked
107 override public bool Stacked { get{ return false;}}
110 /// True if chart type supports axeses
112 override public bool RequireAxes { get{ return true;}
}
115 /// True if chart type supports logarithmic axes
117 override public bool SupportLogarithmicAxes { get{ return true;}
}
120 /// True if chart type requires to switch the value (Y) axes position
122 override public bool SwitchValueAxes { get{ return false;}
}
125 /// True if chart series can be placed side-by-side.
127 override public bool SideBySideSeries { get{ return true;}
}
130 /// True if each data point of a chart must be represented in the legend
132 override public bool DataPointsInLegend { get{ return false;}
}
135 /// Indicates that extra Y values are connected to the scale of the Y axis
137 override public bool ExtraYValuesConnectedToYAxis{ get { return false; }
}
140 /// True if palette colors should be applied for each data paoint.
141 /// Otherwise the color is applied to the series.
143 override public bool ApplyPaletteColorsToPoints { get { return false; }
}
146 /// How to draw series/points in legend:
147 /// Filled rectangle, Line or Marker
149 /// <param name="series">Legend item series.</param>
150 /// <returns>Legend item style.</returns>
151 override public LegendImageStyle
GetLegendImageStyle(Series series
)
153 return LegendImageStyle
.Rectangle
;
157 /// Number of supported Y value(s) per point
159 override public int YValuesPerPoint{ get { return 1; }
}
162 /// If the crossing value is auto Crossing value should be
163 /// automatically set to zero for some chart
164 /// types (Bar, column, area etc.)
166 override public bool ZeroCrossing { get{ return true;}
}
173 /// Default constructor
175 public ColumnChart() : base(false)
184 /// Labels and markers have to be shifted if there
185 /// is more than one series for column chart.
187 override public double ShiftedX
200 /// Labels and markers have to be shifted if there
201 /// is more than one series for column chart. This property
202 /// will give a name of the series, which is used, for
203 /// labels and markers.
205 override public string ShiftedSerName
209 return _shiftedSerName
;
213 _shiftedSerName
= value;
219 #region Painting and selection methods
222 /// Paint Column Chart.
224 /// <param name="graph">The Chart Graphics object.</param>
225 /// <param name="common">The Common elements object.</param>
226 /// <param name="area">Chart area for this chart.</param>
227 /// <param name="seriesToDraw">Chart series to draw.</param>
228 override public void Paint(
230 CommonElements common
,
235 this.Common
= common
;
237 ProcessChartType( false, false, graph
, common
, area
, seriesToDraw
);
239 // Draw labels and markers
240 ProcessChartType( true, false, graph
, common
, area
, seriesToDraw
);
244 /// This method recalculates size of the columns and paint them or do the hit test.
245 /// This method is used from Paint or Select method.
247 /// <param name="labels">Mode which draws only labels and markers.</param>
248 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
249 /// <param name="graph">The Chart Graphics object.</param>
250 /// <param name="common">The Common elements object.</param>
251 /// <param name="area">Chart area for this chart.</param>
252 /// <param name="seriesToDraw">Chart series to draw.</param>
253 private void ProcessChartType(
257 CommonElements common
,
259 Series seriesToDraw
)
261 // Prosess 3D chart type
262 if(area
.Area3DStyle
.Enable3D
)
264 ProcessChartType3D( labels
, selection
, graph
, common
, area
, seriesToDraw
);
269 SizeF pixelRelSize
= graph
.GetRelativeSize(new SizeF(1.1f
, 1.1f
));
271 // All data series from chart area which have Column chart type
272 List
<string> typeSeries
= area
.GetSeriesFromChartType(Name
);
274 // Check if series should be drawn side by side
275 bool currentDrawSeriesSideBySide
= this.drawSeriesSideBySide
;
276 foreach(string seriesName
in typeSeries
)
278 if(common
.DataManager
.Series
[seriesName
].IsCustomPropertySet(CustomPropertyName
.DrawSideBySide
))
280 string attribValue
= common
.DataManager
.Series
[seriesName
][CustomPropertyName
.DrawSideBySide
];
281 if(String
.Compare( attribValue
, "False", StringComparison
.OrdinalIgnoreCase
) == 0 )
283 currentDrawSeriesSideBySide
= false;
285 else if(String
.Compare( attribValue
, "True", StringComparison
.OrdinalIgnoreCase
) == 0)
287 currentDrawSeriesSideBySide
= true;
289 else if(String
.Compare( attribValue
, "Auto", StringComparison
.OrdinalIgnoreCase
) == 0)
295 throw (new InvalidOperationException(SR
.ExceptionAttributeDrawSideBySideInvalid
));
300 // Find the number of "Column chart" data series
301 double numOfSeries
= typeSeries
.Count
;
302 if(!currentDrawSeriesSideBySide
)
307 // Check if column chart series are indexed
308 bool indexedSeries
= ChartHelper
.IndexedSeries(this.Common
, area
.GetSeriesFromChartType(Name
).ToArray());
310 //************************************************************
311 //** Loop through all series
312 //************************************************************
314 foreach( Series ser
in common
.DataManager
.Series
)
316 // Process non empty series of the area with Column chart type
317 if( String
.Compare( ser
.ChartTypeName
, Name
, true, System
.Globalization
.CultureInfo
.CurrentCulture
) != 0
318 || ser
.ChartArea
!= area
.Name
|| ser
.Points
.Count
== 0 || !ser
.IsVisible())
323 // Set shifted series name property
324 ShiftedSerName
= ser
.Name
;
326 // Set active vertical/horizontal axis
327 Axis vAxis
= area
.GetAxis(AxisName
.Y
, ser
.YAxisType
, ser
.YSubAxisName
);
328 Axis hAxis
= area
.GetAxis(AxisName
.X
, ser
.XAxisType
, ser
.XSubAxisName
);
329 double horizontalViewMax
= hAxis
.ViewMaximum
;
330 double horizontalViewMin
= hAxis
.ViewMinimum
;
331 double verticalViewMax
= vAxis
.ViewMaximum
;
332 double verticalViewMin
= vAxis
.ViewMinimum
;
333 double verticalAxisCrossing
= vAxis
.GetPosition(vAxis
.Crossing
);
335 // Get points interval:
336 // - set interval to 1 for indexed series
337 // - if points are not equaly spaced, the minimum interval between points is selected.
338 // - if points have same interval bars do not overlap each other.
339 bool sameInterval
= false;
343 if (ser
.Points
.Count
== 1 &&
344 (ser
.XValueType
== ChartValueType
.Date
||
345 ser
.XValueType
== ChartValueType
.DateTime
||
346 ser
.XValueType
== ChartValueType
.Time
||
347 ser
.XValueType
== ChartValueType
.DateTimeOffset
))
349 // Check if interval is the same
350 area
.GetPointsInterval(typeSeries
, hAxis
.IsLogarithmic
, hAxis
.logarithmBase
, true, out sameInterval
);
352 // Special case when there is only one data point and date scale is used.
353 if (!double.IsNaN(hAxis
.majorGrid
.GetInterval()) && hAxis
.majorGrid
.GetIntervalType() != DateTimeIntervalType
.NotSet
)
355 interval
= ChartHelper
.GetIntervalSize(hAxis
.minimum
, hAxis
.majorGrid
.GetInterval(), hAxis
.majorGrid
.GetIntervalType());
359 interval
= ChartHelper
.GetIntervalSize(hAxis
.minimum
, hAxis
.Interval
, hAxis
.IntervalType
);
364 interval
= area
.GetPointsInterval( typeSeries
, hAxis
.IsLogarithmic
, hAxis
.logarithmBase
, true, out sameInterval
);
369 double width
= ser
.GetPointWidth(graph
, hAxis
, interval
, 0.8) / numOfSeries
;
371 // Call Back Paint event
374 common
.Chart
.CallOnPrePaint(new ChartPaintEventArgs(ser
, graph
, common
, area
.PlotAreaPosition
));
377 //************************************************************
378 //** Loop through all points in series
379 //************************************************************
381 foreach( DataPoint point
in ser
.Points
)
383 // Change Y value if Column is out of plot area
384 double yValue
= vAxis
.GetLogValue( GetYValue(common
, area
, ser
, point
, index
, (useTwoValues
) ? 1 : 0) );
386 if( yValue
> verticalViewMax
)
388 yValue
= verticalViewMax
;
390 if( yValue
< verticalViewMin
)
392 yValue
= verticalViewMin
;
395 // Recalculates Height position and zero position of Columns
396 double height
= vAxis
.GetLinearPosition( yValue
);
398 // Set start position for a column
399 double columnStartPosition
= 0;
402 // Point Y value (first) is used to determine the column starting position
403 double yValueStart
= vAxis
.GetLogValue( GetYValue(common
, area
, ser
, point
, index
, 0 ) );
404 if( yValueStart
> verticalViewMax
)
406 yValueStart
= verticalViewMax
;
408 else if( yValueStart
< verticalViewMin
)
410 yValueStart
= verticalViewMin
;
413 columnStartPosition
= vAxis
.GetLinearPosition(yValueStart
);
417 // Column starts on the horizontal axis crossing
418 columnStartPosition
= verticalAxisCrossing
;
421 // Increase point index
429 // The formula for position is based on a distance
430 //from the grid line or nPoints position.
431 xPosition
= hAxis
.GetPosition( (double)index
) - width
* ((double) numOfSeries
) / 2.0 + width
/2 + seriesIndx
* width
;
432 xCenterVal
= hAxis
.GetPosition( (double)index
);
434 else if( sameInterval
)
436 xPosition
= hAxis
.GetPosition( point
.XValue
) - width
* ((double) numOfSeries
) / 2.0 + width
/2 + seriesIndx
* width
;
437 xCenterVal
= hAxis
.GetPosition( point
.XValue
);
441 xPosition
= hAxis
.GetPosition( point
.XValue
);
442 xCenterVal
= hAxis
.GetPosition( point
.XValue
);
445 // Labels and markers have to be shifted if there
446 // is more than one series for column chart.
447 ShiftedX
= xPosition
- xCenterVal
;
450 // Make sure that points with small values are still visible
451 if( height
< columnStartPosition
&&
452 (columnStartPosition
- height
) < pixelRelSize
.Height
)
454 height
= columnStartPosition
- pixelRelSize
.Height
;
456 if( height
> columnStartPosition
&&
457 (height
- columnStartPosition
) < pixelRelSize
.Height
)
459 height
= columnStartPosition
+ pixelRelSize
.Height
;
462 // Get column rectangle
463 RectangleF rectSize
= RectangleF
.Empty
;
466 // Set the Column rectangle
467 rectSize
.X
= (float)(xPosition
- width
/2);
468 rectSize
.Width
= (float)(width
);
471 // The top side of rectangle has always
472 // smaller value than a bottom value
473 if( columnStartPosition
< height
)
475 rectSize
.Y
= (float)columnStartPosition
;
476 rectSize
.Height
= (float)height
- rectSize
.Y
;
480 rectSize
.Y
= (float)height
;
481 rectSize
.Height
= (float)columnStartPosition
- rectSize
.Y
;
485 catch(OverflowException
)
490 // if data point is not empty
496 //************************************************************
498 //************************************************************
499 if( common
.ProcessModePaint
)
503 // Check if column is completly out of the data scaleView
504 double xValue
= (indexedSeries
) ? index
: point
.XValue
;
505 xValue
= hAxis
.GetLogValue(xValue
);
506 if(xValue
< horizontalViewMin
|| xValue
> horizontalViewMax
)
511 // Check if column is partialy in the data scaleView
512 bool clipRegionSet
= false;
513 if(rectSize
.X
< area
.PlotAreaPosition
.X
|| rectSize
.Right
> area
.PlotAreaPosition
.Right
)
515 // Set clipping region for line drawing
516 graph
.SetClip( area
.PlotAreaPosition
.ToRectangleF() );
517 clipRegionSet
= true;
520 // Start Svg Selection mode
521 graph
.StartHotRegion( point
);
523 // Draw the Column rectangle
524 DrawColumn2D(graph
, vAxis
, rectSize
, point
, ser
);
526 // End Svg Selection mode
527 graph
.EndHotRegion( );
535 else if(this.useTwoValues
)
537 // Draw labels and markers
549 //************************************************************
550 // Hot Regions mode used for image maps, tool tips and
552 //************************************************************
553 if( common
.ProcessModeRegions
&& !labels
)
555 common
.HotRegionsList
.AddHotRegion( rectSize
, point
, ser
.Name
, index
- 1 );
562 common
.Chart
.CallOnPostPaint(new ChartPaintEventArgs(ser
, graph
, common
, area
.PlotAreaPosition
));
566 if(currentDrawSeriesSideBySide
)
571 // Draw labels and markers using the base class algorithm
572 if( labels
&& !this.useTwoValues
)
574 base.ProcessChartType( false, graph
, common
, area
, seriesToDraw
);
582 /// <param name="graph">Chart graphics.</param>
583 /// <param name="vAxis">Vertical axis.</param>
584 /// <param name="rectSize">Column position and size.</param>
585 /// <param name="point">Column data point.</param>
586 /// <param name="ser">Column series.</param>
587 protected virtual void DrawColumn2D(
594 graph
.FillRectangleRel(
597 point
.BackHatchStyle
,
599 point
.BackImageWrapMode
,
600 point
.BackImageTransparentColor
,
601 point
.BackImageAlignment
,
602 point
.BackGradientStyle
,
603 point
.BackSecondaryColor
,
606 point
.BorderDashStyle
,
610 ChartGraphics
.GetBarDrawingStyle(point
),
615 /// Gets label position for the column depending on the Y value.
617 /// <returns>Return automaticly detected label position.</returns>
618 /// <param name="series">Data series.</param>
619 /// <param name="pointIndex">Point index.</param>
620 /// <returns>Label aligning.</returns>
621 override protected LabelAlignmentStyles
GetAutoLabelPosition(Series series
, int pointIndex
)
623 if( series
.Points
[pointIndex
].YValues
[0] >= 0 )
624 return LabelAlignmentStyles
.Top
;
626 return LabelAlignmentStyles
.Bottom
;
630 /// Indicates that markers are drawnd on the X edge of the data scaleView.
632 /// <returns>False. Column chart never draws markers on the edge.</returns>
633 override protected bool ShouldDrawMarkerOnViewEdgeX()
640 #region 3D painting and selection methods
643 /// This method recalculates size of the columns and paint them or do the hit test in 3d space.
644 /// This method is used from Paint or Select method.
646 /// <param name="labels">Mode which draws only labels and markers.</param>
647 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
648 /// <param name="graph">The Chart Graphics object.</param>
649 /// <param name="common">The Common elements object.</param>
650 /// <param name="area">Chart area for this chart.</param>
651 /// <param name="seriesToDraw">Chart series to draw.</param>
652 private void ProcessChartType3D( bool labels
, bool selection
, ChartGraphics graph
, CommonElements common
, ChartArea area
, Series seriesToDraw
)
654 // Labels & markers are drawn with the data points in the first iteration
655 if(labels
&& !selection
)
661 SizeF pixelRelSize
= graph
.GetRelativeSize(new SizeF(1.1f
, 1.1f
));
663 // Get list of series to draw
664 List
<string> typeSeries
= null;
665 bool currentDrawSeriesSideBySide
= this.drawSeriesSideBySide
;
666 if( (area
.Area3DStyle
.IsClustered
&& this.SideBySideSeries
) ||
669 // Draw all series of the same chart type
670 typeSeries
= area
.GetSeriesFromChartType(Name
);
672 // Check if series should be drawn side by side
673 foreach(string seriesName
in typeSeries
)
675 if(common
.DataManager
.Series
[seriesName
].IsCustomPropertySet(CustomPropertyName
.DrawSideBySide
))
677 string attribValue
= common
.DataManager
.Series
[seriesName
][CustomPropertyName
.DrawSideBySide
];
678 if(String
.Compare( attribValue
, "False", StringComparison
.OrdinalIgnoreCase
)==0)
680 currentDrawSeriesSideBySide
= false;
682 else if(String
.Compare( attribValue
, "True", StringComparison
.OrdinalIgnoreCase
)==0)
684 currentDrawSeriesSideBySide
= true;
686 else if(String
.Compare( attribValue
, "Auto", StringComparison
.OrdinalIgnoreCase
)==0)
692 throw (new InvalidOperationException(SR
.ExceptionAttributeDrawSideBySideInvalid
));
699 // Draw just one chart series
700 typeSeries
= new List
<string>();
701 typeSeries
.Add(seriesToDraw
.Name
);
704 //************************************************************
705 //** Get order of data points drawing
706 //************************************************************
707 ArrayList dataPointDrawingOrder
= area
.GetDataPointDrawingOrder(typeSeries
, this, selection
, coordinates
, null, this.YValueIndex
, currentDrawSeriesSideBySide
);
709 //************************************************************
710 //** Loop through all data poins
711 //************************************************************
712 foreach(object obj
in dataPointDrawingOrder
)
714 // Get point & series
715 DataPoint3D pointEx
= (DataPoint3D
) obj
;
716 DataPoint point
= pointEx
.dataPoint
;
717 Series ser
= point
.series
;
719 // Get point bar drawing style
720 BarDrawingStyle barDrawingStyle
= ChartGraphics
.GetBarDrawingStyle(point
);
722 // Set active vertical/horizontal axis
723 Axis vAxis
= area
.GetAxis(AxisName
.Y
, ser
.YAxisType
, ser
.YSubAxisName
);
724 Axis hAxis
= area
.GetAxis(AxisName
.X
, ser
.XAxisType
, ser
.XSubAxisName
);
726 // Change Y value if Column is out of plot area
727 float topDarkening
= 0f
;
728 float bottomDarkening
= 0f
;
729 double yValue
= GetYValue(common
, area
, ser
, pointEx
.dataPoint
, pointEx
.index
- 1, (useTwoValues
) ? 1 : 0);
730 yValue
= vAxis
.GetLogValue(yValue
);
731 if( yValue
> vAxis
.ViewMaximum
)
734 yValue
= vAxis
.ViewMaximum
;
736 if( yValue
< vAxis
.ViewMinimum
)
739 yValue
= vAxis
.ViewMinimum
;
742 // Recalculates Height position and zero position of Columns
743 double height
= vAxis
.GetLinearPosition( yValue
);
745 // Set start position for a column
746 double columnStartPosition
= 0;
749 // Point Y value (first) is used to determine the column starting position
750 double yValueStart
= vAxis
.GetLogValue( GetYValue(common
, area
, ser
, point
, pointEx
.index
- 1, 0 ) );
751 if( yValueStart
> vAxis
.ViewMaximum
)
753 bottomDarkening
= 0.5f
;
754 yValueStart
= vAxis
.ViewMaximum
;
756 else if( yValueStart
< vAxis
.ViewMinimum
)
758 bottomDarkening
= 0.5f
;
759 yValueStart
= vAxis
.ViewMinimum
;
762 columnStartPosition
= vAxis
.GetLinearPosition(yValueStart
);
766 // Column starts on the horizontal axis crossing
767 columnStartPosition
= vAxis
.GetPosition(vAxis
.Crossing
);
770 // Labels and markers have to be shifted if there
771 // is more than one series for column chart.
772 if(!currentDrawSeriesSideBySide
)
774 pointEx
.xPosition
= pointEx
.xCenterVal
;
776 ShiftedX
= pointEx
.xPosition
- pointEx
.xCenterVal
;
778 // Make sure that points with small values are still visible
779 if( height
< columnStartPosition
&&
780 (columnStartPosition
- height
) < pixelRelSize
.Height
)
782 height
= columnStartPosition
- pixelRelSize
.Height
;
784 if( height
> columnStartPosition
&&
785 (height
- columnStartPosition
) < pixelRelSize
.Height
)
787 height
= columnStartPosition
+ pixelRelSize
.Height
;
790 // Get column rectangle
791 RectangleF rectSize
= RectangleF
.Empty
;
794 // Set the Column rectangle
795 rectSize
.X
= (float)(pointEx
.xPosition
- pointEx
.width
/2);
796 rectSize
.Width
= (float)(pointEx
.width
);
798 // The top side of rectangle has always
799 // smaller value than a bottom value
800 if( columnStartPosition
< height
)
802 float temp
= bottomDarkening
;
803 bottomDarkening
= topDarkening
;
806 rectSize
.Y
= (float)columnStartPosition
;
807 rectSize
.Height
= (float)height
- rectSize
.Y
;
811 rectSize
.Y
= (float)height
;
812 rectSize
.Height
= (float)columnStartPosition
- rectSize
.Y
;
815 catch(OverflowException
)
820 //************************************************************
822 //************************************************************
823 // Path projection of 3D rect.
824 GraphicsPath rectPath
= null;
826 // Check if column is completly out of the data scaleView
827 double xValue
= (pointEx
.indexedSeries
) ? pointEx
.index
: point
.XValue
;
828 xValue
= hAxis
.GetLogValue(xValue
);
829 if(xValue
< hAxis
.ViewMinimum
|| xValue
> hAxis
.ViewMaximum
)
834 // Check if column is partialy in the data scaleView
835 bool clipRegionSet
= false;
836 if(rectSize
.Right
<= area
.PlotAreaPosition
.X
|| rectSize
.X
>= area
.PlotAreaPosition
.Right
)
841 if(rectSize
.X
< area
.PlotAreaPosition
.X
)
843 rectSize
.Width
-= area
.PlotAreaPosition
.X
- rectSize
.X
;
844 rectSize
.X
= area
.PlotAreaPosition
.X
;
846 if(rectSize
.Right
> area
.PlotAreaPosition
.Right
)
848 rectSize
.Width
-= rectSize
.Right
- area
.PlotAreaPosition
.Right
;
850 if(rectSize
.Width
< 0)
855 // Detect if we need to get graphical path of drawn object
856 DrawingOperationTypes drawingOperationType
= DrawingOperationTypes
.DrawElement
;
858 if( common
.ProcessModeRegions
)
860 drawingOperationType
|= DrawingOperationTypes
.CalcElementPath
;
864 rectSize
.Height
> 0f
&&
867 // Start Svg Selection mode
868 graph
.StartHotRegion( point
);
870 rectPath
= graph
.Fill3DRectangle(
875 area
.Area3DStyle
.LightStyle
,
881 point
.BorderDashStyle
,
884 drawingOperationType
);
886 // End Svg Selection mode
887 graph
.EndHotRegion( );
889 //************************************************************
890 // Hot Regions mode used for image maps, tool tips and
892 //************************************************************
893 if( common
.ProcessModeRegions
&& !labels
)
895 common
.HotRegionsList
.AddHotRegion(
904 if (rectPath
!= null)
916 // Draw Labels & markers for each data point
917 this.ProcessSinglePoint3D(
928 // Finish processing 3D labels
929 this.DrawAccumulated3DLabels(graph
, common
, area
);
934 #region 2D and 3D Labels Drawing
937 /// This method draws label.
939 /// <param name="graph">The Chart Graphics object</param>
940 /// <param name="common">The Common elements object</param>
941 /// <param name="area">Chart area for this chart</param>
942 /// <param name="columnPosition">Column position</param>
943 /// <param name="point">Data point</param>
944 /// <param name="ser">Data series</param>
945 /// <param name="pointIndex">Data point index.</param>
946 protected virtual void DrawLabel(
949 CommonElements common
,
950 RectangleF columnPosition
,
955 // Labels drawing functionality is inhereted from the PointChart class.
959 /// Draws\Hit tests single 3D point.
961 /// <param name="pointEx">3D point information.</param>
962 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
963 /// <param name="graph">The Chart Graphics object.</param>
964 /// <param name="common">The Common elements object.</param>
965 /// <param name="area">Chart area for this chart.</param>
966 /// <param name="columnPosition">Column position</param>
967 /// <param name="pointIndex">Point index.</param>
968 protected virtual void ProcessSinglePoint3D(
972 CommonElements common
,
974 RectangleF columnPosition
,
978 // Draw Labels & markers for each data point
979 base.ProcessSinglePoint3D(
991 /// ColumnChart class contains all the code necessary to draw
992 /// both Column and RangeColumn charts. The RangeColumnChart class
993 /// is used to override few default settings, so that 2 Y values
994 /// will be used to define top and bottom position of each column.
996 internal class RangeColumnChart
: ColumnChart
1001 /// Public constructor
1003 public RangeColumnChart()
1005 // Set the flag to use two Y values, while drawing the columns
1006 this.useTwoValues
= true;
1008 // Coordinates of COP used when sorting 3D points order
1009 this.coordinates
= COPCoordinates
.X
| COPCoordinates
.Y
;
1011 // Index of the main Y value
1012 this.YValueIndex
= 1;
1017 #region IChartType interface implementation
1022 override public string Name { get{ return ChartTypeNames.RangeColumn;}}
1025 /// If the crossing value is auto Crossing value should be
1026 /// automatically set to zero for some chart
1027 /// types (Bar, column, area etc.)
1029 override public bool ZeroCrossing { get{ return true;}
}
1032 /// Number of supported Y value(s) per point
1034 override public int YValuesPerPoint{ get { return 2; }
}
1037 /// Indicates that extra Y values are connected to the scale of the Y axis
1039 override public bool ExtraYValuesConnectedToYAxis{ get { return true; }
}
1043 #region Y values related methods
1046 /// Helper function, which returns the Y value of the point.
1048 /// <param name="common">Chart common elements.</param>
1049 /// <param name="area">Chart area the series belongs to.</param>
1050 /// <param name="series">Sereis of the point.</param>
1051 /// <param name="point">Point object.</param>
1052 /// <param name="pointIndex">Index of the point.</param>
1053 /// <param name="yValueIndex">Index of the Y value to get.</param>
1054 /// <returns>Y value of the point.</returns>
1055 override public double GetYValue(
1056 CommonElements common
,
1063 // Calculate column height
1064 if(yValueIndex
== -1)
1066 return -(base.GetYValue(common
, area
, series
, point
, pointIndex
, 1) -
1067 base.GetYValue(common
, area
, series
, point
, pointIndex
, 0));
1070 return base.GetYValue(common
, area
, series
, point
, pointIndex
, yValueIndex
);
1075 #region 2D and 3D Labels Drawing
1078 /// This method draws label.
1080 /// <param name="graph">The Chart Graphics object</param>
1081 /// <param name="common">The Common elements object</param>
1082 /// <param name="area">Chart area for this chart</param>
1083 /// <param name="columnPosition">Column position</param>
1084 /// <param name="point">Data point</param>
1085 /// <param name="series">Data series</param>
1086 /// <param name="pointIndex">Data point index.</param>
1087 protected override void DrawLabel(
1089 ChartGraphics graph
,
1090 CommonElements common
,
1091 RectangleF columnPosition
,
1096 //************************************************************
1097 //** Get marker position and size
1098 //************************************************************
1100 // Get intersection between column rectangle and plotting area rectangle
1101 RectangleF intersection
= RectangleF
.Intersect(
1102 columnPosition
, area
.PlotAreaPosition
.ToRectangleF() );
1104 // If intersection is empty no drawing required
1105 if(intersection
.Height
<= 0f
|| intersection
.Width
<= 0f
)
1110 // Get marker position
1111 PointF markerPosition
= PointF
.Empty
;
1112 markerPosition
.X
= intersection
.X
+ intersection
.Width
/ 2f
;
1113 markerPosition
.Y
= intersection
.Y
;
1115 // Remeber pre-calculated point position
1116 point
.positionRel
= new PointF(markerPosition
.X
, markerPosition
.Y
);
1118 // Get point some point properties and save them in variables
1119 int pointMarkerSize
= point
.MarkerSize
;
1120 string pointMarkerImage
= point
.MarkerImage
;
1121 MarkerStyle pointMarkerStyle
= point
.MarkerStyle
;
1124 SizeF markerSize
= base.GetMarkerSize(
1132 //************************************************************
1133 //** Draw point chart
1134 //************************************************************
1135 if(pointMarkerStyle
!= MarkerStyle
.None
||
1136 pointMarkerImage
.Length
> 0)
1138 // Start Svg Selection mode
1139 graph
.StartHotRegion( point
);
1142 graph
.DrawMarkerRel(markerPosition
,
1143 (pointMarkerStyle
== MarkerStyle
.None
) ? MarkerStyle
.Circle
: pointMarkerStyle
,
1144 (int)markerSize
.Height
,
1145 (point
.MarkerColor
== Color
.Empty
) ? point
.Color
: point
.MarkerColor
,
1146 (point
.MarkerBorderColor
== Color
.Empty
) ? point
.BorderColor
: point
.MarkerBorderColor
,
1147 GetMarkerBorderSize(point
),
1149 point
.MarkerImageTransparentColor
,
1150 (point
.series
!= null) ? point
.series
.ShadowOffset
: 0,
1151 (point
.series
!= null) ? point
.series
.ShadowColor
: Color
.Empty
,
1152 new RectangleF(markerPosition
.X
, markerPosition
.Y
, markerSize
.Width
, markerSize
.Height
));
1154 // End Svg Selection mode
1155 graph
.EndHotRegion( );
1157 //************************************************************
1158 // Hot Regions mode used for image maps, tool tips and
1159 // hit test function
1160 //************************************************************
1161 if( common
.ProcessModeRegions
)
1163 // Get relative marker size
1164 SizeF relativeMarkerSize
= graph
.GetRelativeSize(markerSize
);
1166 // Insert area just after the last custom area
1167 int insertIndex
= common
.HotRegionsList
.FindInsertIndex();
1169 // Insert circle area
1170 if(pointMarkerStyle
== MarkerStyle
.Circle
)
1172 float[] circCoord
= new float[3];
1173 circCoord
[0] = markerPosition
.X
;
1174 circCoord
[1] = markerPosition
.Y
;
1175 circCoord
[2] = relativeMarkerSize
.Width
/2f
;
1177 common
.HotRegionsList
.AddHotRegion(
1187 // All other markers represented as rectangles
1190 common
.HotRegionsList
.AddHotRegion(
1191 new RectangleF(markerPosition
.X
- relativeMarkerSize
.Width
/2f
, markerPosition
.Y
- relativeMarkerSize
.Height
/2f
, relativeMarkerSize
.Width
, relativeMarkerSize
.Height
),
1200 //************************************************************
1201 //** Draw LabelStyle
1202 //************************************************************
1204 // Label text format
1205 using (StringFormat format
= new StringFormat())
1207 format
.Alignment
= StringAlignment
.Center
;
1208 format
.LineAlignment
= StringAlignment
.Center
;
1210 // Disable the clip region
1211 Region oldClipRegion
= graph
.Clip
;
1212 graph
.Clip
= new Region();
1214 if (point
.IsValueShownAsLabel
|| point
.Label
.Length
> 0)
1218 if (point
.Label
.Length
== 0)
1220 // Round Y values for 100% stacked area
1221 double pointLabelValue
= GetYValue(common
, area
, series
, point
, pointIndex
, 0);
1223 text
= ValueConverter
.FormatValue(
1230 ChartElementType
.DataPoint
);
1234 text
= point
.ReplaceKeywords(point
.Label
);
1237 // Calculate label position
1238 PointF labelPosition
= PointF
.Empty
;
1239 labelPosition
.X
= intersection
.X
+ intersection
.Width
/ 2f
;
1240 labelPosition
.Y
= intersection
.Y
+ intersection
.Height
/ 2f
;
1242 // Start Svg Selection mode
1243 graph
.StartHotRegion(point
, true);
1246 SizeF sizeFont
= graph
.GetRelativeSize(graph
.MeasureString(text
, point
.Font
, new SizeF(1000f
, 1000f
), StringFormat
.GenericTypographic
));
1248 // Get label background position
1249 RectangleF labelBackPosition
= RectangleF
.Empty
;
1250 SizeF sizeLabel
= new SizeF(sizeFont
.Width
, sizeFont
.Height
);
1251 sizeLabel
.Width
+= sizeLabel
.Width
/ text
.Length
;
1252 sizeLabel
.Height
+= sizeFont
.Height
/ 8;
1253 labelBackPosition
= GetLabelPosition(
1261 using (Brush brush
= new SolidBrush(point
.LabelForeColor
))
1263 graph
.DrawPointLabelStringRel(
1272 point
.LabelBackColor
,
1273 point
.LabelBorderColor
,
1274 point
.LabelBorderWidth
,
1275 point
.LabelBorderDashStyle
,
1281 // End Svg Selection mode
1282 graph
.EndHotRegion();
1285 // Restore old clip region
1286 graph
.Clip
= oldClipRegion
;
1291 /// Draws\Hit tests single 3D point.
1293 /// <param name="pointEx">3D point information.</param>
1294 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
1295 /// <param name="graph">The Chart Graphics object.</param>
1296 /// <param name="common">The Common elements object.</param>
1297 /// <param name="area">Chart area for this chart.</param>
1298 /// <param name="columnPosition">Column position</param>
1299 /// <param name="pointIndex">Point index.</param>
1300 protected override void ProcessSinglePoint3D(
1301 DataPoint3D pointEx
,
1303 ChartGraphics graph
,
1304 CommonElements common
,
1306 RectangleF columnPosition
,
1310 DataPoint point
= pointEx
.dataPoint
;
1312 // Check required Y values number
1313 if(point
.YValues
.Length
< this.YValuesPerPoint
)
1315 throw(new InvalidOperationException(SR
.ExceptionChartTypeRequiresYValues(this.Name
,this.YValuesPerPoint
.ToString(CultureInfo
.InvariantCulture
))));
1318 // Label text format
1319 using (StringFormat format
= new StringFormat())
1321 format
.Alignment
= StringAlignment
.Center
;
1322 format
.LineAlignment
= StringAlignment
.Center
;
1324 // Disable the clip region
1325 Region oldClipRegion
= graph
.Clip
;
1326 graph
.Clip
= new Region();
1328 if (point
.IsValueShownAsLabel
|| point
.Label
.Length
> 0)
1332 if (point
.Label
.Length
== 0)
1335 double pointLabelValue
= GetYValue(common
, area
, pointEx
.dataPoint
.series
, point
, pointEx
.index
- 1, 0);
1336 text
= ValueConverter
.FormatValue(
1337 pointEx
.dataPoint
.series
.Chart
,
1342 pointEx
.dataPoint
.series
.YValueType
,
1343 ChartElementType
.DataPoint
);
1347 text
= point
.ReplaceKeywords(point
.Label
);
1351 // Calculate label position
1352 PointF labelPosition
= PointF
.Empty
;
1353 labelPosition
.X
= columnPosition
.X
+ columnPosition
.Width
/ 2f
;
1354 labelPosition
.Y
= columnPosition
.Y
+ columnPosition
.Height
/ 2f
;
1356 // Transform coordinates
1357 Point3D
[] marker3DPosition
= new Point3D
[1];
1358 marker3DPosition
[0] = new Point3D(labelPosition
.X
, labelPosition
.Y
, (float)(pointEx
.zPosition
+ pointEx
.depth
));
1359 area
.matrix3D
.TransformPoints(marker3DPosition
);
1361 labelPosition
.X
= marker3DPosition
[0].X
;
1362 labelPosition
.Y
= marker3DPosition
[0].Y
;
1364 // Start Svg Selection mode
1365 graph
.StartHotRegion(point
, true);
1368 SizeF sizeFont
= graph
.GetRelativeSize(graph
.MeasureString(text
, point
.Font
, new SizeF(1000f
, 1000f
), StringFormat
.GenericTypographic
));
1370 // Get label background position
1371 RectangleF labelBackPosition
= RectangleF
.Empty
;
1372 SizeF sizeLabel
= new SizeF(sizeFont
.Width
, sizeFont
.Height
);
1373 sizeLabel
.Width
+= sizeLabel
.Width
/ text
.Length
;
1374 sizeLabel
.Height
+= sizeFont
.Height
/ 8;
1375 labelBackPosition
= GetLabelPosition(
1383 using (Brush brush
= new SolidBrush(point
.LabelForeColor
))
1385 graph
.DrawPointLabelStringRel(
1394 point
.LabelBackColor
,
1395 point
.LabelBorderColor
,
1396 point
.LabelBorderWidth
,
1397 point
.LabelBorderDashStyle
,
1403 // End Svg Selection mode
1404 graph
.EndHotRegion();
1407 // Restore old clip region
1408 graph
.Clip
= oldClipRegion
;