1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
14 // Purpose: The ChartArea class represents one chart area within
15 // a chart image, and is used to plot one or more chart
16 // series. The number of chart series that can be plotted
17 // in a chart area is unlimited.
19 // Reviewed: GS - August 6, 2002
20 // AG - August 7, 2002
21 // AG - Microsoft 16, 2007
23 //===================================================================
25 #region Used namespaces
27 using System
.Collections
;
28 using System
.Collections
.Specialized
;
29 using System
.ComponentModel
;
30 using System
.ComponentModel
.Design
;
33 using System
.Drawing
.Design
;
34 using System
.Drawing
.Drawing2D
;
35 using System
.Globalization
;
36 using System
.Diagnostics
.CodeAnalysis
;
40 using System
.Windows
.Forms
.DataVisualization
.Charting
.Data
;
41 using System
.Windows
.Forms
.DataVisualization
.Charting
.ChartTypes
;
42 using System
.Windows
.Forms
.DataVisualization
.Charting
.Utilities
;
43 using System
.Windows
.Forms
.DataVisualization
.Charting
.Borders3D
;
44 using System
.Windows
.Forms
.DataVisualization
.Charting
;
45 using System
.ComponentModel
.Design
.Serialization
;
46 using System
.Reflection
;
47 using System
.Windows
.Forms
.Design
;
51 using System
.Web
.UI
.DataVisualization
.Charting
;
52 using System
.Web
.UI
.DataVisualization
.Charting
.Data
;
53 using System
.Web
.UI
.DataVisualization
.Charting
.ChartTypes
;
54 using System
.Web
.UI
.DataVisualization
.Charting
.Utilities
;
55 using System
.Web
.UI
.DataVisualization
.Charting
.Borders3D
;
62 namespace System
.Windows
.Forms
.DataVisualization
.Charting
64 namespace System
.Web
.UI
.DataVisualization
.Charting
67 #region Chart area aligment enumerations
70 /// An enumeration of the alignment orientations of a ChartArea
73 public enum AreaAlignmentOrientations
76 /// Chart areas are not automatically aligned.
81 /// Chart areas are aligned vertically.
86 /// Chart areas are aligned horizontally.
91 /// Chart areas are aligned using all values (horizontally and vertically).
93 All
= Vertical
| Horizontal
97 /// An enumeration of the alignment styles of a ChartArea
100 public enum AreaAlignmentStyles
103 /// Chart areas are not automatically aligned.
108 /// Chart areas are aligned by positions.
113 /// Chart areas are aligned by inner plot positions.
118 /// Chart areas are aligned by axes views.
122 #if Microsoft_CONTROL
125 /// Cursor and Selection alignment.
130 /// Complete alignment.
132 All
= Position
| PlotPosition
| Cursor
| AxesView
133 #else // Microsoft_CONTROL
136 /// Complete alignment.
138 All
= Position
| PlotPosition
| AxesView
140 #endif // Microsoft_CONTROL
146 /// The ChartArea class is used to create and display a chart
147 /// area within a chart image. The chart area is a rectangular
148 /// area on a chart image. It has 4 axes, horizontal and vertical grids.
149 /// A chart area can contain more than one different chart type.
150 /// The number of chart series that can be plotted in a chart area
153 /// ChartArea class exposes all the properties and methods
154 /// of its base ChartArea3D class.
157 DefaultProperty("Axes"),
158 SRDescription("DescriptionAttributeChartArea_ChartArea"),
161 [AspNetHostingPermission(System
.Security
.Permissions
.SecurityAction
.InheritanceDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
162 [AspNetHostingPermission(System
.Security
.Permissions
.SecurityAction
.LinkDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
164 public partial class ChartArea
: ChartNamedElement
166 #region Chart Area Fields
169 /// Plot area position
171 internal ElementPosition PlotAreaPosition
;
173 // Private data members, which store properties values
174 private Axis
[] _axisArray
= new Axis
[4];
175 private Color _backColor
= Color
.Empty
;
176 private ChartHatchStyle _backHatchStyle
= ChartHatchStyle
.None
;
177 private string _backImage
= "";
178 private ChartImageWrapMode _backImageWrapMode
= ChartImageWrapMode
.Tile
;
179 private Color _backImageTransparentColor
= Color
.Empty
;
180 private ChartImageAlignmentStyle _backImageAlignment
= ChartImageAlignmentStyle
.TopLeft
;
181 private GradientStyle _backGradientStyle
= GradientStyle
.None
;
182 private Color _backSecondaryColor
= Color
.Empty
;
183 private Color _borderColor
= Color
.Black
;
184 private int _borderWidth
= 1;
185 private ChartDashStyle _borderDashStyle
= ChartDashStyle
.NotSet
;
186 private int _shadowOffset
= 0;
187 private Color _shadowColor
= Color
.FromArgb(128, 0, 0, 0);
188 private ElementPosition _areaPosition
= null;
189 private ElementPosition _innerPlotPosition
= null;
190 internal int IterationCounter
= 0;
192 private bool _isSameFontSizeForAllAxes
= false;
193 internal float axesAutoFontSize
= 8f
;
195 private string _alignWithChartArea
= Constants
.NotSetValue
;
196 private AreaAlignmentOrientations _alignmentOrientation
= AreaAlignmentOrientations
.Vertical
;
197 private AreaAlignmentStyles _alignmentStyle
= AreaAlignmentStyles
.All
;
198 private int _circularSectorNumber
= int.MinValue
;
199 private int _circularUsePolygons
= int.MinValue
;
201 // Flag indicates that chart area is acurrently aligned
202 internal bool alignmentInProcess
= false;
204 // Chart area position before adjustments
205 internal RectangleF originalAreaPosition
= RectangleF
.Empty
;
207 // Chart area inner plot position before adjustments
208 internal RectangleF originalInnerPlotPosition
= RectangleF
.Empty
;
210 // Chart area position before adjustments
211 internal RectangleF lastAreaPosition
= RectangleF
.Empty
;
214 // Center of the circulat chart area
215 internal PointF circularCenter
= PointF
.Empty
;
217 private ArrayList _circularAxisList
= null;
219 #if Microsoft_CONTROL
220 // Buffered plotting area image
221 internal Bitmap areaBufferBitmap
= null;
223 private Cursor _cursorX
= new Cursor();
224 private Cursor _cursorY
= new Cursor();
227 // Area SmartLabel class
228 internal SmartLabel smartLabels
= new SmartLabel();
230 // Gets or sets a flag that specifies whether the chart area is visible.
231 private bool _visible
= true;
235 #region Chart Area Cursor properties
237 #if Microsoft_CONTROL
240 /// Gets or sets a Cursor object that is used for cursors and selected ranges along the X-axis.
243 SRCategory("CategoryAttributeCursor"),
246 SRDescription("DescriptionAttributeChartArea_CursorX"),
247 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
248 TypeConverter(typeof(NoNameExpandableObjectConverter
)),
250 public Cursor CursorX
260 // Initialize chart object
261 _cursorX
.Initialize(this, AxisName
.X
);
266 /// Gets or sets a Cursor object that is used for cursors and selected ranges along the Y-axis.
269 SRCategory("CategoryAttributeCursor"),
272 SRDescription("DescriptionAttributeChartArea_CursorY"),
273 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
274 TypeConverter(typeof(NoNameExpandableObjectConverter
)),
276 public Cursor CursorY
286 // Initialize chart object
287 _cursorY
.Initialize(this, AxisName
.Y
);
291 #endif // Microsoft_CONTROL
295 #region Chart Area properties
298 /// Gets or sets a flag that specifies whether the chart area is visible.
301 /// When this flag is set to false, all series, legends, titles and annotation objects
302 /// associated with the chart area will also be hidden.
305 /// <b>True</b> if the chart area is visible; <b>false</b> otherwise.
308 SRCategory("CategoryAttributeAppearance"),
310 SRDescription("DescriptionAttributeChartArea_Visible"),
311 ParenthesizePropertyNameAttribute(true),
313 virtual public bool Visible
327 /// Gets or sets the name of the ChartArea object to which this chart area should be aligned.
330 SRCategory("CategoryAttributeAlignment"),
332 DefaultValue(Constants
.NotSetValue
),
333 SRDescription("DescriptionAttributeChartArea_AlignWithChartArea"),
334 TypeConverter(typeof(LegendAreaNameConverter
)),
335 #if !Microsoft_CONTROL
336 PersistenceMode(PersistenceMode
.Attribute
)
339 public string AlignWithChartArea
343 return _alignWithChartArea
;
347 if (value != _alignWithChartArea
)
349 if (String
.IsNullOrEmpty(value))
351 _alignWithChartArea
= Constants
.NotSetValue
;
355 if (Chart
!= null && Chart
.ChartAreas
!= null)
357 Chart
.ChartAreas
.VerifyNameReference(value);
359 _alignWithChartArea
= value;
367 /// Gets or sets the alignment orientation of a chart area.
370 SRCategory("CategoryAttributeAlignment"),
372 DefaultValue(AreaAlignmentOrientations
.Vertical
),
373 SRDescription("DescriptionAttributeChartArea_AlignOrientation"),
374 Editor(Editors
.FlagsEnumUITypeEditor
.Editor
, Editors
.FlagsEnumUITypeEditor
.Base
),
375 #if !Microsoft_CONTROL
376 PersistenceMode(PersistenceMode
.Attribute
)
379 public AreaAlignmentOrientations AlignmentOrientation
383 return _alignmentOrientation
;
387 _alignmentOrientation
= value;
394 /// Gets or sets the alignment style of the ChartArea.
397 SRCategory("CategoryAttributeAlignment"),
399 DefaultValue(AreaAlignmentStyles
.All
),
400 SRDescription("DescriptionAttributeChartArea_AlignType"),
401 Editor(Editors
.FlagsEnumUITypeEditor
.Editor
, Editors
.FlagsEnumUITypeEditor
.Base
),
402 #if !Microsoft_CONTROL
403 PersistenceMode(PersistenceMode
.Attribute
)
406 public AreaAlignmentStyles AlignmentStyle
410 return _alignmentStyle
;
414 _alignmentStyle
= value;
420 /// Gets or sets an array that represents all axes for a chart area.
423 SRCategory("CategoryAttributeAxes"),
425 SRDescription("DescriptionAttributeChartArea_Axes"),
426 TypeConverter(typeof(AxesArrayConverter
)),
427 Editor(Editors
.AxesArrayEditor
.Editor
, Editors
.AxesArrayEditor
.Base
),
428 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility
.Hidden
),
429 SerializationVisibilityAttribute(SerializationVisibility
.Hidden
)
431 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
449 /// Avoid serialization of the axes array
451 [EditorBrowsableAttribute(EditorBrowsableState
.Never
)]
452 internal bool ShouldSerializeAxes()
458 /// Gets or sets an Axis object that represents the primary Y-axis.
461 SRCategory("CategoryAttributeAxis"),
464 SRDescription("DescriptionAttributeChartArea_AxisY"),
465 #if Microsoft_CONTROL
466 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
468 PersistenceMode(PersistenceMode
.InnerProperty
),
470 TypeConverter(typeof(NoNameExpandableObjectConverter
))
481 axisY
.Initialize(this, AxisName
.Y
);
482 _axisArray
[1] = axisY
;
488 /// Gets or sets an Axis object that represents the primary X-axis.
491 SRCategory("CategoryAttributeAxis"),
494 SRDescription("DescriptionAttributeChartArea_AxisX"),
495 #if Microsoft_CONTROL
496 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
498 PersistenceMode(PersistenceMode
.InnerProperty
),
500 TypeConverter(typeof(NoNameExpandableObjectConverter
))
511 axisX
.Initialize(this, AxisName
.X
);
512 _axisArray
[0] = axisX
;
518 /// Gets or sets an Axis object that represents the secondary X-axis.
521 SRCategory("CategoryAttributeAxis"),
524 SRDescription("DescriptionAttributeChartArea_AxisX2"),
525 #if Microsoft_CONTROL
526 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
528 PersistenceMode(PersistenceMode
.InnerProperty
),
530 TypeConverter(typeof(NoNameExpandableObjectConverter
))
541 axisX2
.Initialize(this, AxisName
.X2
);
542 _axisArray
[2] = axisX2
;
548 /// Gets or sets an Axis object that represents the secondary Y-axis.
551 SRCategory("CategoryAttributeAxis"),
554 SRDescription("DescriptionAttributeChartArea_AxisY2"),
555 #if Microsoft_CONTROL
556 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
558 PersistenceMode(PersistenceMode
.InnerProperty
),
560 TypeConverter(typeof(NoNameExpandableObjectConverter
))
571 axisY2
.Initialize(this, AxisName
.Y2
);
572 _axisArray
[3] = axisY2
;
578 /// Gets or sets an ElementPosition object, which defines the position of a chart area object within the chart image.
581 SRCategory("CategoryAttributeAppearance"),
583 SRDescription("DescriptionAttributeChartArea_Position"),
584 #if Microsoft_CONTROL
585 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
587 PersistenceMode(PersistenceMode
.InnerProperty
),
589 NotifyParentPropertyAttribute(true),
590 TypeConverter(typeof(ElementPositionConverter
)),
591 SerializationVisibilityAttribute(SerializationVisibility
.Element
)
593 public ElementPosition Position
597 // Serialize only position values if Auto set to false
598 if(this.Chart
!= null && this.Chart
.serializationStatus
== SerializationStatus
.Saving
)
600 if(_areaPosition
.Auto
)
602 return new ElementPosition();
606 ElementPosition newPosition
= new ElementPosition();
607 #if Microsoft_CONTROL
608 newPosition
.Auto
= false;
610 newPosition
.Auto
= true;
612 newPosition
.SetPositionNoAuto(_areaPosition
.X
, _areaPosition
.Y
, _areaPosition
.Width
, _areaPosition
.Height
);
616 return _areaPosition
;
620 _areaPosition
= value;
621 _areaPosition
.Parent
= this;
622 _areaPosition
.resetAreaAutoPosition
= true;
628 /// Determoines if this position should be serialized.
630 /// <returns></returns>
631 internal bool ShouldSerializePosition()
633 return !this.Position
.Auto
;
637 /// Gets or sets an ElementPosition object, which defines the inner plot position of a chart area object.
640 SRCategory("CategoryAttributeAppearance"),
642 SRDescription("DescriptionAttributeChartArea_InnerPlotPosition"),
643 #if Microsoft_CONTROL
644 DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
),
646 PersistenceMode(PersistenceMode
.InnerProperty
),
648 NotifyParentPropertyAttribute(true),
649 TypeConverter(typeof(ElementPositionConverter
)),
650 SerializationVisibilityAttribute(SerializationVisibility
.Element
)
652 public ElementPosition InnerPlotPosition
656 // Serialize only position values if Auto set to false
657 if (this.Common
!= null && this.Common
.Chart
!= null && this.Common
.Chart
.serializationStatus
== SerializationStatus
.Saving
)
659 if(_innerPlotPosition
.Auto
)
661 return new ElementPosition();
665 ElementPosition newPosition
= new ElementPosition();
666 #if Microsoft_CONTROL
667 newPosition
.Auto
= false;
669 newPosition
.Auto
= true;
671 newPosition
.SetPositionNoAuto(_innerPlotPosition
.X
, _innerPlotPosition
.Y
, _innerPlotPosition
.Width
, _innerPlotPosition
.Height
);
675 return _innerPlotPosition
;
679 _innerPlotPosition
= value;
680 _innerPlotPosition
.Parent
= this;
686 /// Determoines if this position should be serialized.
688 /// <returns></returns>
689 internal bool ShouldSerializeInnerPlotPosition()
691 return !this.InnerPlotPosition
.Auto
;
695 /// Gets or sets the background color of a ChartArea object.
699 SRCategory("CategoryAttributeAppearance"),
701 DefaultValue(typeof(Color
), ""),
702 SRDescription("DescriptionAttributeBackColor"),
703 NotifyParentPropertyAttribute(true),
704 TypeConverter(typeof(ColorConverter
)),
705 Editor(Editors
.ChartColorEditor
.Editor
, Editors
.ChartColorEditor
.Base
),
706 #if !Microsoft_CONTROL
707 PersistenceMode(PersistenceMode
.Attribute
)
710 public Color BackColor
724 /// Gets or sets the hatching style of a ChartArea object.
729 SRCategory("CategoryAttributeAppearance"),
731 DefaultValue(ChartHatchStyle
.None
),
732 NotifyParentPropertyAttribute(true),
733 SRDescription("DescriptionAttributeBackHatchStyle"),
734 #if !Microsoft_CONTROL
735 PersistenceMode(PersistenceMode
.Attribute
),
737 Editor(Editors
.HatchStyleEditor
.Editor
, Editors
.HatchStyleEditor
.Base
)
740 public ChartHatchStyle BackHatchStyle
744 return _backHatchStyle
;
748 _backHatchStyle
= value;
754 /// Gets or sets the background image of a ChartArea object.
757 SRCategory("CategoryAttributeAppearance"),
760 SRDescription("DescriptionAttributeBackImage"),
761 Editor(Editors
.ImageValueEditor
.Editor
, Editors
.ImageValueEditor
.Base
),
762 #if !Microsoft_CONTROL
763 PersistenceMode(PersistenceMode
.Attribute
),
765 NotifyParentPropertyAttribute(true)
767 public string BackImage
781 /// Gets or sets the drawing mode of the background image of a ChartArea object.
784 SRCategory("CategoryAttributeAppearance"),
786 DefaultValue(ChartImageWrapMode
.Tile
),
787 NotifyParentPropertyAttribute(true),
788 SRDescription("DescriptionAttributeImageWrapMode"),
789 #if !Microsoft_CONTROL
790 PersistenceMode(PersistenceMode
.Attribute
)
793 public ChartImageWrapMode BackImageWrapMode
797 return _backImageWrapMode
;
801 _backImageWrapMode
= value;
807 /// Gets or sets the color of a ChartArea object's background image that will be drawn as transparent.
810 SRCategory("CategoryAttributeAppearance"),
812 DefaultValue(typeof(Color
), ""),
813 NotifyParentPropertyAttribute(true),
814 SRDescription("DescriptionAttributeImageTransparentColor"),
815 TypeConverter(typeof(ColorConverter
)),
816 Editor(Editors
.ChartColorEditor
.Editor
, Editors
.ChartColorEditor
.Base
),
817 #if !Microsoft_CONTROL
818 PersistenceMode(PersistenceMode
.Attribute
)
821 public Color BackImageTransparentColor
825 return _backImageTransparentColor
;
829 _backImageTransparentColor
= value;
835 /// Gets or sets the alignment of a ChartArea object.
838 SRCategory("CategoryAttributeAppearance"),
840 DefaultValue(ChartImageAlignmentStyle
.TopLeft
),
841 NotifyParentPropertyAttribute(true),
842 SRDescription("DescriptionAttributeBackImageAlign"),
843 #if !Microsoft_CONTROL
844 PersistenceMode(PersistenceMode
.Attribute
)
847 public ChartImageAlignmentStyle BackImageAlignment
851 return _backImageAlignment
;
855 _backImageAlignment
= value;
861 /// Gets or sets the orientation of a chart element's gradient,
862 /// and also determines whether or not a gradient is used.
866 SRCategory("CategoryAttributeAppearance"),
868 DefaultValue(GradientStyle
.None
),
869 NotifyParentPropertyAttribute(true),
870 SRDescription("DescriptionAttributeBackGradientStyle"),
871 #if !Microsoft_CONTROL
872 PersistenceMode(PersistenceMode
.Attribute
),
874 Editor(Editors
.GradientEditor
.Editor
, Editors
.GradientEditor
.Base
)
876 public GradientStyle BackGradientStyle
880 return _backGradientStyle
;
884 _backGradientStyle
= value;
890 /// Gets or sets the secondary color of a ChartArea object.
894 SRCategory("CategoryAttributeAppearance"),
896 DefaultValue(typeof(Color
), ""),
897 NotifyParentPropertyAttribute(true),
898 SRDescription("DescriptionAttributeBackSecondaryColor"),
899 TypeConverter(typeof(ColorConverter
)),
900 Editor(Editors
.ChartColorEditor
.Editor
, Editors
.ChartColorEditor
.Base
),
901 #if !Microsoft_CONTROL
902 PersistenceMode(PersistenceMode
.Attribute
)
905 public Color BackSecondaryColor
909 return _backSecondaryColor
;
913 _backSecondaryColor
= value;
919 /// Gets or sets the shadow color of a ChartArea object.
922 SRCategory("CategoryAttributeAppearance"),
924 DefaultValue(typeof(Color
), "128,0,0,0"),
925 SRDescription("DescriptionAttributeShadowColor"),
926 NotifyParentPropertyAttribute(true),
927 TypeConverter(typeof(ColorConverter
)),
928 Editor(Editors
.ChartColorEditor
.Editor
, Editors
.ChartColorEditor
.Base
),
929 #if !Microsoft_CONTROL
930 PersistenceMode(PersistenceMode
.Attribute
)
933 public Color ShadowColor
941 _shadowColor
= value;
947 /// Gets or sets the shadow offset (in pixels) of a ChartArea object.
950 SRCategory("CategoryAttributeAppearance"),
953 SRDescription("DescriptionAttributeShadowOffset"),
954 NotifyParentPropertyAttribute(true),
955 #if !Microsoft_CONTROL
956 PersistenceMode(PersistenceMode
.Attribute
)
959 public int ShadowOffset
963 return _shadowOffset
;
967 _shadowOffset
= value;
973 /// Gets or sets the border color of a ChartArea object.
977 SRCategory("CategoryAttributeAppearance"),
979 DefaultValue(typeof(Color
), "Black"),
980 SRDescription("DescriptionAttributeBorderColor"),
981 NotifyParentPropertyAttribute(true),
982 TypeConverter(typeof(ColorConverter
)),
983 Editor(Editors
.ChartColorEditor
.Editor
, Editors
.ChartColorEditor
.Base
),
984 #if !Microsoft_CONTROL
985 PersistenceMode(PersistenceMode
.Attribute
)
988 public Color BorderColor
996 _borderColor
= value;
1002 /// Gets or sets the border width of a ChartArea object.
1006 SRCategory("CategoryAttributeAppearance"),
1009 SRDescription("DescriptionAttributeBorderWidth"),
1010 NotifyParentPropertyAttribute(true),
1011 #if !Microsoft_CONTROL
1012 PersistenceMode(PersistenceMode
.Attribute
)
1015 public int BorderWidth
1019 return _borderWidth
;
1025 throw (new ArgumentOutOfRangeException("value", SR
.ExceptionBorderWidthIsNegative
));
1027 _borderWidth
= value;
1033 /// Gets or sets the style of the border line of a ChartArea object.
1037 SRCategory("CategoryAttributeAppearance"),
1039 DefaultValue(ChartDashStyle
.NotSet
),
1040 SRDescription("DescriptionAttributeBorderDashStyle"),
1041 NotifyParentPropertyAttribute(true),
1042 #if !Microsoft_CONTROL
1043 PersistenceMode(PersistenceMode
.Attribute
)
1046 public ChartDashStyle BorderDashStyle
1050 return _borderDashStyle
;
1054 _borderDashStyle
= value;
1060 /// Gets or sets the unique name of a ChartArea object.
1064 SRCategory("CategoryAttributeMisc"),
1066 SRDescription("DescriptionAttributeChartArea_Name"),
1067 NotifyParentPropertyAttribute(true),
1068 #if !Microsoft_CONTROL
1069 PersistenceMode(PersistenceMode
.Attribute
)
1072 public override string Name
1085 /// Gets or sets a Boolean that determines if the labels of the axes for all chart area
1086 /// , which have LabelsAutoFit property set to true, are of equal size.
1089 SRCategory("CategoryAttributeAppearance"),
1091 DefaultValue(false),
1092 SRDescription("DescriptionAttributeChartArea_EquallySizedAxesFont"),
1093 NotifyParentPropertyAttribute(true),
1094 #if !Microsoft_CONTROL
1095 PersistenceMode(PersistenceMode
.Attribute
)
1098 public bool IsSameFontSizeForAllAxes
1102 return _isSameFontSizeForAllAxes
;
1106 _isSameFontSizeForAllAxes
= value;
1114 #region Constructors
1116 /// ChartArea constructor.
1124 /// ChartArea constructor.
1126 /// <param name="name">The name.</param>
1127 public ChartArea(string name
) : base(name
)
1133 #region Chart Area Methods
1135 /// Restores series order and X axis reversed mode for the 3D charts.
1137 internal void Restore3DAnglesAndReverseMode()
1139 if(this.Area3DStyle
.Enable3D
&& !this.chartAreaIsCurcular
)
1141 // Restore axis "IsReversed" property and old Y angle
1142 this.AxisX
.IsReversed
= oldReverseX
;
1143 this.AxisX2
.IsReversed
= oldReverseX
;
1144 this.AxisY
.IsReversed
= oldReverseY
;
1145 this.AxisY2
.IsReversed
= oldReverseY
;
1146 this.Area3DStyle
.Rotation
= oldYAngle
;
1151 /// Sets series order and X axis reversed mode for the 3D charts.
1153 internal void Set3DAnglesAndReverseMode()
1155 // Clear series reversed flag
1156 _reverseSeriesOrder
= false;
1158 // If 3D charting is enabled
1159 if(this.Area3DStyle
.Enable3D
)
1161 // Make sure primary & secondary axis has the same IsReversed settings
1162 // This is a limitation for the 3D chart required for labels drawing.
1163 this.AxisX2
.IsReversed
= this.AxisX
.IsReversed
;
1164 this.AxisY2
.IsReversed
= this.AxisY
.IsReversed
;
1166 // Remember reversed order of X & Y axis and Angles
1167 oldReverseX
= this.AxisX
.IsReversed
;
1168 oldReverseY
= this.AxisY
.IsReversed
;
1169 oldYAngle
= this.Area3DStyle
.Rotation
;
1172 if(this.Area3DStyle
.Rotation
> 90 || this.Area3DStyle
.Rotation
< -90)
1174 // This method depends on the 'switchValueAxes' field which is calculated based on the chart types
1175 // of the series associated with the chart area. We need to call SetData method to make sure this field
1176 // is correctly initialized. Because we only need to collect information about the series, we pass 'false'
1177 // as parameters to limit the amount of work this function does.
1178 this.SetData(false, false);
1180 // Reversed series order
1181 _reverseSeriesOrder
= true;
1183 // Reversed primary and secondary X axis
1184 if(!this.switchValueAxes
)
1186 this.AxisX
.IsReversed
= !this.AxisX
.IsReversed
;
1187 this.AxisX2
.IsReversed
= !this.AxisX2
.IsReversed
;
1190 // Reversed primary and secondary Y axis for chart types like Bar
1193 this.AxisY
.IsReversed
= !this.AxisY
.IsReversed
;
1194 this.AxisY2
.IsReversed
= !this.AxisY2
.IsReversed
;
1198 if(this.Area3DStyle
.Rotation
> 90)
1200 this.Area3DStyle
.Rotation
= (this.Area3DStyle
.Rotation
- 90) - 90;
1202 else if(this.Area3DStyle
.Rotation
< -90)
1204 this.Area3DStyle
.Rotation
= (this.Area3DStyle
.Rotation
+ 90) + 90;
1211 /// Save all automatic values like Minimum and Maximum.
1213 internal void SetTempValues()
1215 // Save non automatic area position
1216 if(!this.Position
.Auto
)
1218 this.originalAreaPosition
= this.Position
.ToRectangleF();
1221 // Save non automatic area inner plot position
1222 if(!this.InnerPlotPosition
.Auto
)
1224 this.originalInnerPlotPosition
= this.InnerPlotPosition
.ToRectangleF();
1227 this._circularSectorNumber
= int.MinValue
;
1228 this._circularUsePolygons
= int.MinValue
;
1229 this._circularAxisList
= null;
1231 // Save Minimum and maximum values for all axes
1232 axisX
.StoreAxisValues();
1233 axisX2
.StoreAxisValues();
1234 axisY
.StoreAxisValues();
1235 axisY2
.StoreAxisValues();
1239 /// Load all automatic values like Minimum and Maximum with original values.
1241 internal void GetTempValues()
1243 // Take Minimum and maximum values for all axes
1244 axisX
.ResetAxisValues();
1245 axisX2
.ResetAxisValues();
1246 axisY
.ResetAxisValues();
1247 axisY2
.ResetAxisValues();
1249 // Restore non automatic area position
1250 if(!this.originalAreaPosition
.IsEmpty
)
1252 this.lastAreaPosition
= this.Position
.ToRectangleF();
1253 this.Position
.SetPositionNoAuto(this.originalAreaPosition
.X
, this.originalAreaPosition
.Y
, this.originalAreaPosition
.Width
, this.originalAreaPosition
.Height
);
1254 this.originalAreaPosition
= RectangleF
.Empty
;
1257 // Save non automatic area inner plot position
1258 if(!this.originalInnerPlotPosition
.IsEmpty
)
1260 this.InnerPlotPosition
.SetPositionNoAuto(this.originalInnerPlotPosition
.X
, this.originalInnerPlotPosition
.Y
, this.originalInnerPlotPosition
.Width
, this.originalInnerPlotPosition
.Height
);
1261 this.originalInnerPlotPosition
= RectangleF
.Empty
;
1266 /// Initialize Chart area and axes
1268 internal void Initialize()
1270 // Initialize 3D style class
1271 _area3DStyle
= new ChartArea3DStyle(this);
1273 // Create axes for this chart area.
1274 axisY
= new Axis( );
1275 axisX
= new Axis( );
1276 axisX2
= new Axis( );
1277 axisY2
= new Axis( );
1280 axisX
.Initialize(this, AxisName
.X
);
1281 axisY
.Initialize(this, AxisName
.Y
);
1282 axisX2
.Initialize(this, AxisName
.X2
);
1283 axisY2
.Initialize(this, AxisName
.Y2
);
1285 // Initialize axes array
1286 _axisArray
[0] = axisX
;
1287 _axisArray
[1] = axisY
;
1288 _axisArray
[2] = axisX2
;
1289 _axisArray
[3] = axisY2
;
1291 // Set flag to reset auto values for all areas
1292 _areaPosition
= new ElementPosition(this);
1293 _areaPosition
.resetAreaAutoPosition
= true;
1295 _innerPlotPosition
= new ElementPosition(this);
1297 // Set the position of the new chart area
1298 if( PlotAreaPosition
== null )
1300 PlotAreaPosition
= new ElementPosition(this);
1303 #if Microsoft_CONTROL
1305 // Initialize cursor class
1306 this._cursorX
.Initialize(this, AxisName
.X
);
1307 this._cursorY
.Initialize(this, AxisName
.Y
);
1309 #endif // Microsoft_CONTROL
1313 /// Minimum and maximum do not have to be calculated
1314 /// from data series every time. It is very time
1315 /// consuming. Minimum and maximum are buffered
1316 /// and only when this flags are set Minimum and
1317 /// Maximum are refreshed from data.
1319 internal void ResetMinMaxFromData()
1321 _axisArray
[0].refreshMinMaxFromData
= true;
1322 _axisArray
[1].refreshMinMaxFromData
= true;
1323 _axisArray
[2].refreshMinMaxFromData
= true;
1324 _axisArray
[3].refreshMinMaxFromData
= true;
1328 /// Recalculates the axes scale of a chart area.
1330 public void RecalculateAxesScale()
1332 // Read axis Max/Min from data
1333 ResetMinMaxFromData();
1335 #if Microsoft_CONTROL
1336 Set3DAnglesAndReverseMode();
1340 // Initialize area position
1341 _axisArray
[0].ReCalc( PlotAreaPosition
);
1342 _axisArray
[1].ReCalc( PlotAreaPosition
);
1343 _axisArray
[2].ReCalc( PlotAreaPosition
);
1344 _axisArray
[3].ReCalc( PlotAreaPosition
);
1346 // Find all Data and chart types which belong
1347 // to this chart area an set default values
1350 #if Microsoft_CONTROL
1351 Restore3DAnglesAndReverseMode();
1357 /// RecalculateAxesScale the chart area
1359 internal void ReCalcInternal()
1361 // Initialize area position
1362 _axisArray
[0].ReCalc( PlotAreaPosition
);
1363 _axisArray
[1].ReCalc( PlotAreaPosition
);
1364 _axisArray
[2].ReCalc( PlotAreaPosition
);
1365 _axisArray
[3].ReCalc( PlotAreaPosition
);
1367 // Find all Data and chart types which belong
1368 // to this chart area an set default values
1374 /// Reset auto calculated chart area values.
1376 internal void ResetAutoValues()
1378 _axisArray
[0].ResetAutoValues();
1379 _axisArray
[1].ResetAutoValues();
1380 _axisArray
[2].ResetAutoValues();
1381 _axisArray
[3].ResetAutoValues();
1385 /// Calculates Position for the background.
1387 /// <param name="withScrollBars">Calculate with scroll bars</param>
1388 /// <returns>Background rectangle</returns>
1389 internal RectangleF
GetBackgroundPosition( bool withScrollBars
)
1391 // For pie and doughnut, which do not have axes, the position
1392 // for the background is Chart area position not plotting
1394 RectangleF backgroundPosition
= PlotAreaPosition
.ToRectangleF();
1397 backgroundPosition
= Position
.ToRectangleF();
1400 // Without scroll bars
1401 if( !withScrollBars
)
1403 return backgroundPosition
;
1406 // Add scroll bar rectangles to the area background
1407 RectangleF backgroundPositionWithScrollBars
= new RectangleF(backgroundPosition
.Location
, backgroundPosition
.Size
);
1409 #if Microsoft_CONTROL
1413 // Loop through all axis
1414 foreach(Axis axis
in this.Axes
)
1416 // Find axis with visible scroll bars
1417 if(axis
.ScrollBar
.IsVisible
&& axis
.ScrollBar
.IsPositionedInside
)
1419 // Change size of the background rectangle depending on the axis position
1420 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1422 backgroundPositionWithScrollBars
.Height
+= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1424 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1426 backgroundPositionWithScrollBars
.Y
-= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1427 backgroundPositionWithScrollBars
.Height
+= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1429 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1431 backgroundPositionWithScrollBars
.X
-= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1432 backgroundPositionWithScrollBars
.Width
+= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1434 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1436 backgroundPositionWithScrollBars
.Width
+= (float)axis
.ScrollBar
.GetScrollBarRelativeSize();
1442 #endif // Microsoft_CONTROL
1443 return backgroundPositionWithScrollBars
;
1447 /// Call when the chart area is resized.
1449 /// <param name="chartGraph">Chart graphics object.</param>
1450 internal void Resize(ChartGraphics chartGraph
)
1452 // Initialize plotting area position
1453 RectangleF plottingRect
= Position
.ToRectangleF();
1454 if(!InnerPlotPosition
.Auto
)
1456 plottingRect
.X
+= (Position
.Width
/ 100F
) * InnerPlotPosition
.X
;
1457 plottingRect
.Y
+= (Position
.Height
/ 100F
) * InnerPlotPosition
.Y
;
1458 plottingRect
.Width
= (Position
.Width
/ 100F
) * InnerPlotPosition
.Width
;
1459 plottingRect
.Height
= (Position
.Height
/ 100F
) * InnerPlotPosition
.Height
;
1462 //******************************************************
1463 //** Calculate number of vertical and horizontal axis
1464 //******************************************************
1465 int verticalAxes
= 0;
1466 int horizontalAxes
= 0;
1467 foreach(Axis axis
in this.Axes
)
1471 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1475 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1479 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1483 else if(axis
.AxisPosition
== AxisPosition
.Right
)
1489 if(horizontalAxes
<= 0 )
1493 if(verticalAxes
<= 0 )
1499 //******************************************************
1500 //** Find same auto-fit font size
1501 //******************************************************
1502 Axis
[] axisArray
= (this.switchValueAxes
) ?
1503 new Axis
[] {this.AxisX, this.AxisX2, this.AxisY, this.AxisY2}
:
1504 new Axis
[] {this.AxisY, this.AxisY2, this.AxisX, this.AxisX2}
;
1505 if(this.IsSameFontSizeForAllAxes
)
1507 axesAutoFontSize
= 20;
1508 foreach(Axis axis
in axisArray
)
1510 // Process only enabled axis
1514 if(axis
.AxisPosition
== AxisPosition
.Bottom
|| axis
.AxisPosition
== AxisPosition
.Top
)
1516 axis
.Resize(chartGraph
, this.PlotAreaPosition
, plottingRect
, horizontalAxes
, InnerPlotPosition
.Auto
);
1520 axis
.Resize(chartGraph
, this.PlotAreaPosition
, plottingRect
, verticalAxes
, InnerPlotPosition
.Auto
);
1523 // Calculate smallest font size
1524 if(axis
.IsLabelAutoFit
&& axis
.autoLabelFont
!= null)
1526 axesAutoFontSize
= Math
.Min(axesAutoFontSize
, axis
.autoLabelFont
.Size
);
1532 //******************************************************
1533 //** Adjust plotting area position according to the axes
1534 //** elements (title, labels, tick marks) size.
1535 //******************************************************
1536 RectangleF rectLabelSideSpacing
= RectangleF
.Empty
;
1537 foreach(Axis axis
in axisArray
)
1539 // Process only enabled axis
1540 if( ! axis
.enabled
)
1542 //******************************************************
1543 //** Adjust for the 3D Wall Width for disabled axis
1544 //******************************************************
1545 if(InnerPlotPosition
.Auto
&& this.Area3DStyle
.Enable3D
&& !this.chartAreaIsCurcular
)
1547 SizeF areaWallSize
= chartGraph
.GetRelativeSize(new SizeF(this.Area3DStyle
.WallWidth
, this.Area3DStyle
.WallWidth
));
1548 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1550 plottingRect
.Height
-= areaWallSize
.Height
;
1552 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1554 plottingRect
.Y
+= areaWallSize
.Height
;
1555 plottingRect
.Height
-= areaWallSize
.Height
;
1557 else if(axis
.AxisPosition
== AxisPosition
.Right
)
1559 plottingRect
.Width
-= areaWallSize
.Width
;
1561 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1563 plottingRect
.X
+= areaWallSize
.Width
;
1564 plottingRect
.Width
-= areaWallSize
.Width
;
1571 //******************************************************
1572 //** Calculate axes elements position
1573 //******************************************************
1574 if(axis
.AxisPosition
== AxisPosition
.Bottom
|| axis
.AxisPosition
== AxisPosition
.Top
)
1576 axis
.Resize(chartGraph
, this.PlotAreaPosition
, plottingRect
, horizontalAxes
, InnerPlotPosition
.Auto
);
1580 axis
.Resize(chartGraph
, this.PlotAreaPosition
, plottingRect
, verticalAxes
, InnerPlotPosition
.Auto
);
1583 // Shift top/bottom labels so they will not overlap with left/right labels
1584 PreventTopBottomAxesLabelsOverlapping(axis
);
1586 //******************************************************
1587 //** Check axis position
1588 //******************************************************
1589 float axisPosition
= (float)axis
.GetAxisPosition();
1590 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1592 if(!axis
.GetIsMarksNextToAxis())
1594 axisPosition
= plottingRect
.Bottom
;
1596 axisPosition
= plottingRect
.Bottom
- axisPosition
;
1598 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1600 if(!axis
.GetIsMarksNextToAxis())
1602 axisPosition
= plottingRect
.Y
;
1604 axisPosition
= axisPosition
- plottingRect
.Top
;
1606 else if(axis
.AxisPosition
== AxisPosition
.Right
)
1608 if(!axis
.GetIsMarksNextToAxis())
1610 axisPosition
= plottingRect
.Right
;
1612 axisPosition
= plottingRect
.Right
- axisPosition
;
1614 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1616 if(!axis
.GetIsMarksNextToAxis())
1618 axisPosition
= plottingRect
.X
;
1620 axisPosition
= axisPosition
- plottingRect
.Left
;
1623 //******************************************************
1624 //** Adjust axis elements size with axis position
1625 //******************************************************
1626 // Calculate total size of axis elements
1627 float axisSize
= axis
.markSize
+ axis
.labelSize
;
1630 // Add sub-axis size
1631 if(!this.chartAreaIsCurcular
&& !this.Area3DStyle
.Enable3D
)
1633 foreach(SubAxis subAxis
in axis
.SubAxes
)
1635 axisSize
+= subAxis
.markSize
+ subAxis
.labelSize
+ subAxis
.titleSize
;
1640 // Adjust depending on the axis position
1641 axisSize
-= axisPosition
;
1648 // Add axis title and scroll bar size (always outside of plotting area)
1649 axisSize
+= axis
.titleSize
+ axis
.scrollBarSize
;
1652 // Calculate horizontal axes size for circualar area
1653 if(this.chartAreaIsCurcular
&&
1654 (axis
.AxisPosition
== AxisPosition
.Top
|| axis
.AxisPosition
== AxisPosition
.Bottom
) )
1656 axisSize
= axis
.titleSize
+ axis
.markSize
+ axis
.scrollBarSize
;
1659 //******************************************************
1660 //** Adjust plotting area
1661 //******************************************************
1662 if(InnerPlotPosition
.Auto
)
1664 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1666 plottingRect
.Height
-= axisSize
;
1668 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1670 plottingRect
.Y
+= axisSize
;
1671 plottingRect
.Height
-= axisSize
;
1673 else if(axis
.AxisPosition
== AxisPosition
.Left
)
1675 plottingRect
.X
+= axisSize
;
1676 plottingRect
.Width
-= axisSize
;
1678 else if(axis
.AxisPosition
== AxisPosition
.Right
)
1680 plottingRect
.Width
-= axisSize
;
1683 // Check if labels side offset should be processed
1684 bool addLabelsSideOffsets
= true;
1686 // Update the plotting area depending on the size required for labels on the sides
1687 if (addLabelsSideOffsets
)
1689 if (axis
.AxisPosition
== AxisPosition
.Bottom
|| axis
.AxisPosition
== AxisPosition
.Top
)
1691 if (axis
.labelNearOffset
!= 0 && axis
.labelNearOffset
< Position
.X
)
1693 float offset
= Position
.X
- axis
.labelNearOffset
;
1694 if (Math
.Abs(offset
) > plottingRect
.Width
* 0.3f
)
1696 offset
= plottingRect
.Width
* 0.3f
;
1699 // NOTE: Code was removed to solve an issue with extra space when labels angle = 45
1700 //rectLabelSideSpacing.Width = (float)Math.Max(offset, rectLabelSideSpacing.Width);
1701 rectLabelSideSpacing
.X
= (float)Math
.Max(offset
, rectLabelSideSpacing
.X
);
1704 if (axis
.labelFarOffset
> Position
.Right
)
1706 if ((axis
.labelFarOffset
- Position
.Right
) < plottingRect
.Width
* 0.3f
)
1708 rectLabelSideSpacing
.Width
= (float)Math
.Max(axis
.labelFarOffset
- Position
.Right
, rectLabelSideSpacing
.Width
);
1712 rectLabelSideSpacing
.Width
= (float)Math
.Max(plottingRect
.Width
* 0.3f
, rectLabelSideSpacing
.Width
);
1719 if (axis
.labelNearOffset
!= 0 && axis
.labelNearOffset
< Position
.Y
)
1721 float offset
= Position
.Y
- axis
.labelNearOffset
;
1722 if (Math
.Abs(offset
) > plottingRect
.Height
* 0.3f
)
1724 offset
= plottingRect
.Height
* 0.3f
;
1727 // NOTE: Code was removed to solve an issue with extra space when labels angle = 45
1728 //rectLabelSideSpacing.Height = (float)Math.Max(offset, rectLabelSideSpacing.Height);
1729 rectLabelSideSpacing
.Y
= (float)Math
.Max(offset
, rectLabelSideSpacing
.Y
);
1732 if (axis
.labelFarOffset
> Position
.Bottom
)
1734 if ((axis
.labelFarOffset
- Position
.Bottom
) < plottingRect
.Height
* 0.3f
)
1736 rectLabelSideSpacing
.Height
= (float)Math
.Max(axis
.labelFarOffset
- Position
.Bottom
, rectLabelSideSpacing
.Height
);
1740 rectLabelSideSpacing
.Height
= (float)Math
.Max(plottingRect
.Height
* 0.3f
, rectLabelSideSpacing
.Height
);
1748 //******************************************************
1749 //** Make sure there is enough space
1750 //** for labels on the chart sides
1751 //******************************************************
1752 if (!this.chartAreaIsCurcular
)
1754 if (rectLabelSideSpacing
.Y
> 0 && rectLabelSideSpacing
.Y
> plottingRect
.Y
- Position
.Y
)
1756 float delta
= (plottingRect
.Y
- Position
.Y
) - rectLabelSideSpacing
.Y
;
1757 plottingRect
.Y
-= delta
;
1758 plottingRect
.Height
+= delta
;
1760 if (rectLabelSideSpacing
.X
> 0 && rectLabelSideSpacing
.X
> plottingRect
.X
- Position
.X
)
1762 float delta
= (plottingRect
.X
- Position
.X
) - rectLabelSideSpacing
.X
;
1763 plottingRect
.X
-= delta
;
1764 plottingRect
.Width
+= delta
;
1766 if (rectLabelSideSpacing
.Height
> 0 && rectLabelSideSpacing
.Height
> Position
.Bottom
- plottingRect
.Bottom
)
1768 plottingRect
.Height
+= (Position
.Bottom
- plottingRect
.Bottom
) - rectLabelSideSpacing
.Height
;
1770 if (rectLabelSideSpacing
.Width
> 0 && rectLabelSideSpacing
.Width
> Position
.Right
- plottingRect
.Right
)
1772 plottingRect
.Width
+= (Position
.Right
- plottingRect
.Right
) - rectLabelSideSpacing
.Width
;
1776 //******************************************************
1777 //** Plotting area must be square for the circular
1778 //** chart area (in pixels).
1779 //******************************************************
1780 if(this.chartAreaIsCurcular
)
1782 // Adjust area to fit the axis title
1783 float xTitleSize
= (float)Math
.Max(this.AxisY
.titleSize
, this.AxisY2
.titleSize
);
1786 plottingRect
.X
+= xTitleSize
;
1787 plottingRect
.Width
-= 2f
* xTitleSize
;
1789 float yTitleSize
= (float)Math
.Max(this.AxisX
.titleSize
, this.AxisX2
.titleSize
);
1792 plottingRect
.Y
+= yTitleSize
;
1793 plottingRect
.Height
-= 2f
* yTitleSize
;
1796 // Make a square plotting rect
1797 RectangleF rect
= chartGraph
.GetAbsoluteRectangle( plottingRect
);
1798 if(rect
.Width
> rect
.Height
)
1800 rect
.X
+= (rect
.Width
- rect
.Height
) / 2f
;
1801 rect
.Width
= rect
.Height
;
1805 rect
.Y
+= (rect
.Height
- rect
.Width
) / 2f
;
1806 rect
.Height
= rect
.Width
;
1808 plottingRect
= chartGraph
.GetRelativeRectangle( rect
);
1810 // Remember circular chart area center
1811 this.circularCenter
= new PointF(plottingRect
.X
+ plottingRect
.Width
/2f
, plottingRect
.Y
+ plottingRect
.Height
/2f
);
1813 // Calculate auto-fit font of the circular axis labels and update area position
1814 FitCircularLabels(chartGraph
, this.PlotAreaPosition
, ref plottingRect
, xTitleSize
, yTitleSize
);
1817 //******************************************************
1818 //** Set plotting area position
1819 //******************************************************
1820 if(plottingRect
.Width
< 0f
)
1822 plottingRect
.Width
= 0f
;
1824 if(plottingRect
.Height
< 0f
)
1826 plottingRect
.Height
= 0f
;
1828 PlotAreaPosition
.FromRectangleF(plottingRect
);
1829 InnerPlotPosition
.SetPositionNoAuto(
1830 (float)Math
.Round((plottingRect
.X
- Position
.X
) / (Position
.Width
/ 100F
), 5),
1831 (float)Math
.Round((plottingRect
.Y
- Position
.Y
) / (Position
.Height
/ 100F
), 5),
1832 (float)Math
.Round(plottingRect
.Width
/ (Position
.Width
/ 100F
), 5),
1833 (float)Math
.Round(plottingRect
.Height
/ (Position
.Height
/ 100F
), 5));
1836 //******************************************************
1837 //** Adjust label font size for axis, which were
1838 //** automatically calculated after the opposite axis
1839 //** change the size of plotting area.
1840 //******************************************************
1841 this.AxisY2
.AdjustLabelFontAtSecondPass(chartGraph
, InnerPlotPosition
.Auto
);
1842 this.AxisY
.AdjustLabelFontAtSecondPass(chartGraph
, InnerPlotPosition
.Auto
);
1843 if(InnerPlotPosition
.Auto
)
1845 this.AxisX2
.AdjustLabelFontAtSecondPass(chartGraph
, InnerPlotPosition
.Auto
);
1846 this.AxisX
.AdjustLabelFontAtSecondPass(chartGraph
, InnerPlotPosition
.Auto
);
1852 /// Finds axis by it's position. Can be Null.
1854 /// <param name="axisPosition">Axis position to find</param>
1855 /// <returns>Found axis.</returns>
1856 private Axis
FindAxis(AxisPosition axisPosition
)
1858 foreach(Axis axis
in this.Axes
)
1860 if(axis
.AxisPosition
== axisPosition
)
1869 /// Shift top/bottom labels so they will not overlap with left/right labels.
1871 /// <param name="axis">Axis to shift up/down.</param>
1872 private void PreventTopBottomAxesLabelsOverlapping(Axis axis
)
1874 // If axis is not on the edge of the chart area do not
1875 // try to adjust it's position when axis labels overlap
1876 // labels of the oppositie axis.
1877 if( !axis
.IsAxisOnAreaEdge
)
1882 // Shift bottom axis labels down
1883 if(axis
.AxisPosition
== AxisPosition
.Bottom
)
1885 // Get labels position
1886 float labelsPosition
= (float)axis
.GetAxisPosition();
1887 if( !axis
.GetIsMarksNextToAxis() )
1889 labelsPosition
= axis
.PlotAreaPosition
.Bottom
;
1892 // Only adjust labels outside plotting area
1893 if(Math
.Round(labelsPosition
, 2) < Math
.Round(axis
.PlotAreaPosition
.Bottom
, 2))
1898 // Check if labels may overlap with Left axis
1899 Axis leftAxis
= FindAxis(AxisPosition
.Left
);
1900 if(leftAxis
!= null &&
1902 leftAxis
.labelFarOffset
!= 0 &&
1903 leftAxis
.labelFarOffset
> labelsPosition
&&
1904 axis
.labelNearOffset
!= 0 &&
1905 axis
.labelNearOffset
< PlotAreaPosition
.X
)
1907 float overlap
= (float)(leftAxis
.labelFarOffset
- labelsPosition
) * 0.75f
;
1908 if(overlap
> axis
.markSize
)
1910 axis
.markSize
+= overlap
- axis
.markSize
;
1914 // Check if labels may overlap with Right axis
1915 Axis rightAxis
= FindAxis(AxisPosition
.Right
);
1916 if(rightAxis
!= null &&
1917 rightAxis
.enabled
&&
1918 rightAxis
.labelFarOffset
!= 0 &&
1919 rightAxis
.labelFarOffset
> labelsPosition
&&
1920 axis
.labelFarOffset
!= 0 &&
1921 axis
.labelFarOffset
> PlotAreaPosition
.Right
)
1923 float overlap
= (float)(rightAxis
.labelFarOffset
- labelsPosition
) * 0.75f
;
1924 if(overlap
> axis
.markSize
)
1926 axis
.markSize
+= overlap
- axis
.markSize
;
1931 // Shift top axis labels up
1932 else if(axis
.AxisPosition
== AxisPosition
.Top
)
1934 // Get labels position
1935 float labelsPosition
= (float)axis
.GetAxisPosition();
1936 if( !axis
.GetIsMarksNextToAxis() )
1938 labelsPosition
= axis
.PlotAreaPosition
.Y
;
1941 // Only adjust labels outside plotting area
1942 if(Math
.Round(labelsPosition
, 2) < Math
.Round(axis
.PlotAreaPosition
.Y
, 2))
1947 // Check if labels may overlap with Left axis
1948 Axis leftAxis
= FindAxis(AxisPosition
.Left
);
1949 if(leftAxis
!= null &&
1951 leftAxis
.labelNearOffset
!= 0 &&
1952 leftAxis
.labelNearOffset
< labelsPosition
&&
1953 axis
.labelNearOffset
!= 0 &&
1954 axis
.labelNearOffset
< PlotAreaPosition
.X
)
1956 float overlap
= (float)(labelsPosition
- leftAxis
.labelNearOffset
) * 0.75f
;
1957 if(overlap
> axis
.markSize
)
1959 axis
.markSize
+= overlap
- axis
.markSize
;
1963 // Check if labels may overlap with Right axis
1964 Axis rightAxis
= FindAxis(AxisPosition
.Right
);
1965 if(rightAxis
!= null &&
1966 rightAxis
.enabled
&&
1967 rightAxis
.labelNearOffset
!= 0 &&
1968 rightAxis
.labelNearOffset
< labelsPosition
&&
1969 axis
.labelFarOffset
!= 0 &&
1970 axis
.labelFarOffset
> PlotAreaPosition
.Right
)
1972 float overlap
= (float)(labelsPosition
- rightAxis
.labelNearOffset
) * 0.75f
;
1973 if(overlap
> axis
.markSize
)
1975 axis
.markSize
+= overlap
- axis
.markSize
;
1984 #region Painting and Selection Methods
1987 /// Draws chart area background and/or border.
1989 /// <param name="graph">Chart graphics.</param>
1990 /// <param name="position">Background position.</param>
1991 /// <param name="borderOnly">Draws chart area border only.</param>
1992 private void PaintAreaBack(ChartGraphics graph
, RectangleF position
, bool borderOnly
)
1997 if(!this.Area3DStyle
.Enable3D
|| !requireAxes
|| chartAreaIsCurcular
)
1999 // 3D Pie Chart doesn't need scene
2000 // Draw 2D background
2001 graph
.FillRectangleRel(
2007 BackImageTransparentColor
,
2011 (requireAxes
) ? Color
.Empty
: BorderColor
,
2012 (requireAxes
) ? 0 : BorderWidth
,
2016 PenAlignment
.Outset
,
2017 chartAreaIsCurcular
,
2018 (chartAreaIsCurcular
&& this.CircularUsePolygons
) ? this.CircularSectorsNumber
: 0,
2019 this.Area3DStyle
.Enable3D
);
2023 // Draw chart area 3D scene
2024 this.DrawArea3DScene(graph
, position
);
2029 if(!this.Area3DStyle
.Enable3D
|| !requireAxes
|| chartAreaIsCurcular
)
2031 // Draw chart area border
2032 if(BorderColor
!= Color
.Empty
&& BorderWidth
> 0)
2034 graph
.FillRectangleRel( position
,
2036 ChartHatchStyle
.None
,
2038 ChartImageWrapMode
.Tile
,
2040 ChartImageAlignmentStyle
.Center
,
2048 PenAlignment
.Outset
,
2049 chartAreaIsCurcular
,
2050 (chartAreaIsCurcular
&& this.CircularUsePolygons
) ? this.CircularSectorsNumber
: 0,
2051 this.Area3DStyle
.Enable3D
);
2059 /// Paint the chart area.
2061 /// <param name="graph">Chart graphics.</param>
2062 internal void Paint( ChartGraphics graph
)
2064 // Check if plot area position was recalculated.
2065 // If not and non-auto InnerPlotPosition & Position were
2066 // specified - do all needed calculations
2067 if (PlotAreaPosition
.Width
== 0 &&
2068 PlotAreaPosition
.Height
== 0 &&
2069 !InnerPlotPosition
.Auto
2072 // Initialize plotting area position
2073 RectangleF plottingRect
= Position
.ToRectangleF();
2074 if (!InnerPlotPosition
.Auto
)
2076 plottingRect
.X
+= (Position
.Width
/ 100F
) * InnerPlotPosition
.X
;
2077 plottingRect
.Y
+= (Position
.Height
/ 100F
) * InnerPlotPosition
.Y
;
2078 plottingRect
.Width
= (Position
.Width
/ 100F
) * InnerPlotPosition
.Width
;
2079 plottingRect
.Height
= (Position
.Height
/ 100F
) * InnerPlotPosition
.Height
;
2082 PlotAreaPosition
.FromRectangleF(plottingRect
);
2085 // Get background position rectangle.
2086 RectangleF backgroundPositionWithScrollBars
= GetBackgroundPosition(true);
2087 RectangleF backgroundPosition
= GetBackgroundPosition(false);
2089 // Add hot region for plotting area.
2090 if (Common
.ProcessModeRegions
)
2092 Common
.HotRegionsList
.AddHotRegion(backgroundPosition
, this, ChartElementType
.PlottingArea
, true);
2095 PaintAreaBack(graph
, backgroundPositionWithScrollBars
, false);
2097 // Call BackPaint event
2098 Common
.Chart
.CallOnPrePaint(new ChartPaintEventArgs(this, graph
, Common
, PlotAreaPosition
));
2100 // Draw chart types without axes - Pie.
2101 if (!requireAxes
&& ChartTypes
.Count
!= 0)
2103 // Find first chart type that do not require axis (like Pie) and draw it.
2104 // Chart types that do not require axes (circular charts) cannot be combined with
2105 // any other chart types.
2106 // NOTE: Fixes issues #4672 and #4692
2107 for (int chartTypeIndex
= 0; chartTypeIndex
< ChartTypes
.Count
; chartTypeIndex
++)
2109 IChartType chartType
= Common
.ChartTypeRegistry
.GetChartType((string)ChartTypes
[chartTypeIndex
]);
2110 if (!chartType
.RequireAxes
)
2112 chartType
.Paint(graph
, Common
, this, null);
2118 Common
.Chart
.CallOnPostPaint(new ChartPaintEventArgs(this, graph
, Common
, PlotAreaPosition
));
2124 // Reset Smart Labels
2125 this.smartLabels
.Reset();
2129 // Set values for optimized drawing
2130 foreach (Axis currentAxis
in this._axisArray
)
2132 currentAxis
.optimizedGetPosition
= true;
2133 currentAxis
.paintViewMax
= currentAxis
.ViewMaximum
;
2134 currentAxis
.paintViewMin
= currentAxis
.ViewMinimum
;
2135 currentAxis
.paintRange
= currentAxis
.paintViewMax
- currentAxis
.paintViewMin
;
2136 currentAxis
.paintAreaPosition
= PlotAreaPosition
.ToRectangleF();
2137 if (currentAxis
.ChartArea
!= null && currentAxis
.ChartArea
.chartAreaIsCurcular
)
2139 // Update position for circular chart area
2140 currentAxis
.paintAreaPosition
.Width
/= 2.0f
;
2141 currentAxis
.paintAreaPosition
.Height
/= 2.0f
;
2143 currentAxis
.paintAreaPositionBottom
= currentAxis
.paintAreaPosition
.Y
+ currentAxis
.paintAreaPosition
.Height
;
2144 currentAxis
.paintAreaPositionRight
= currentAxis
.paintAreaPosition
.X
+ currentAxis
.paintAreaPosition
.Width
;
2145 if (currentAxis
.AxisPosition
== AxisPosition
.Top
|| currentAxis
.AxisPosition
== AxisPosition
.Bottom
)
2146 currentAxis
.paintChartAreaSize
= currentAxis
.paintAreaPosition
.Width
;
2148 currentAxis
.paintChartAreaSize
= currentAxis
.paintAreaPosition
.Height
;
2150 currentAxis
.valueMultiplier
= 0.0;
2151 if (currentAxis
.paintRange
!= 0)
2153 currentAxis
.valueMultiplier
= currentAxis
.paintChartAreaSize
/ currentAxis
.paintRange
;
2157 // Draw Axis Striplines (only when StripWidth > 0)
2158 bool useScaleSegments
= false;
2159 Axis
[] axesArray
= new Axis
[] { axisY, axisY2, axisX, axisX2 }
;
2160 foreach (Axis currentAxis
in axesArray
)
2163 useScaleSegments
= (currentAxis
.ScaleSegments
.Count
> 0);
2165 if (!useScaleSegments
)
2167 currentAxis
.PaintStrips(graph
, false);
2172 foreach (AxisScaleSegment scaleSegment
in currentAxis
.ScaleSegments
)
2174 scaleSegment
.SetTempAxisScaleAndInterval();
2176 currentAxis
.PaintStrips(graph
, false);
2178 scaleSegment
.RestoreAxisScaleAndInterval();
2184 axesArray
= new Axis
[] { axisY, axisX2, axisY2, axisX }
;
2185 foreach (Axis currentAxis
in axesArray
)
2188 useScaleSegments
= (currentAxis
.ScaleSegments
.Count
> 0);
2190 if (!useScaleSegments
)
2192 currentAxis
.PaintGrids(graph
);
2197 foreach (AxisScaleSegment scaleSegment
in currentAxis
.ScaleSegments
)
2199 scaleSegment
.SetTempAxisScaleAndInterval();
2201 currentAxis
.PaintGrids(graph
);
2203 scaleSegment
.RestoreAxisScaleAndInterval();
2209 // Draw Axis Striplines (only when StripWidth == 0)
2210 foreach (Axis currentAxis
in axesArray
)
2213 useScaleSegments
= (currentAxis
.ScaleSegments
.Count
> 0);
2215 if (!useScaleSegments
)
2217 currentAxis
.PaintStrips(graph
, true);
2222 foreach (AxisScaleSegment scaleSegment
in currentAxis
.ScaleSegments
)
2224 scaleSegment
.SetTempAxisScaleAndInterval();
2226 currentAxis
.PaintStrips(graph
, true);
2228 scaleSegment
.RestoreAxisScaleAndInterval();
2234 // Draw Axis elements on the back of the 3D scene
2235 if (this.Area3DStyle
.Enable3D
&& !this.chartAreaIsCurcular
)
2237 foreach (Axis currentAxis
in axesArray
)
2240 useScaleSegments
= (currentAxis
.ScaleSegments
.Count
> 0);
2242 if (!useScaleSegments
)
2244 currentAxis
.PrePaint(graph
);
2249 foreach (AxisScaleSegment scaleSegment
in currentAxis
.ScaleSegments
)
2251 scaleSegment
.SetTempAxisScaleAndInterval();
2253 currentAxis
.PrePaint(graph
);
2255 scaleSegment
.RestoreAxisScaleAndInterval();
2263 // Draws chart area border
2264 bool borderDrawn
= false;
2265 if (this.Area3DStyle
.Enable3D
|| !IsBorderOnTopSeries())
2268 PaintAreaBack(graph
, backgroundPosition
, true);
2272 if (!this.Area3DStyle
.Enable3D
|| this.chartAreaIsCurcular
)
2274 // Drawing in 2D space
2276 // NOTE: Fixes issue #6443 and #5385
2277 // If two chart series of the same type (for example Line) are separated
2278 // by other series (for example Area) the order is not correct.
2279 // Old implementation draws ALL series that belongs to the chart type.
2280 ArrayList typeAndSeries
= this.GetChartTypesAndSeriesToDraw();
2282 // Draw series by chart type or by series
2283 foreach (ChartTypeAndSeriesInfo chartTypeInfo
in typeAndSeries
)
2285 this.IterationCounter
= 0;
2286 IChartType type
= Common
.ChartTypeRegistry
.GetChartType(chartTypeInfo
.ChartType
);
2288 // If 'chartTypeInfo.Series' set to NULL all series of that chart type are drawn at once
2289 type
.Paint(graph
, Common
, this, chartTypeInfo
.Series
);
2294 // Drawing in 3D space
2295 PaintChartSeries3D(graph
);
2298 // Draw area border if it wasn't drawn prior to the series
2301 PaintAreaBack(graph
, backgroundPosition
, true);
2305 foreach (Axis currentAxis
in axesArray
)
2308 useScaleSegments
= (currentAxis
.ScaleSegments
.Count
> 0);
2310 if (!useScaleSegments
)
2312 // Paint axis and Reset temp axis offset for side-by-side charts like column
2313 currentAxis
.Paint(graph
);
2318 // Some of the axis elements like grid lines and tickmarks
2319 // are drawn for each segment
2320 foreach (AxisScaleSegment scaleSegment
in currentAxis
.ScaleSegments
)
2322 scaleSegment
.SetTempAxisScaleAndInterval();
2324 currentAxis
.PaintOnSegmentedScalePassOne(graph
);
2326 scaleSegment
.RestoreAxisScaleAndInterval();
2329 // Other elements like labels, title, axis line are drawn once
2330 currentAxis
.PaintOnSegmentedScalePassTwo(graph
);
2336 Common
.Chart
.CallOnPostPaint(new ChartPaintEventArgs(this, graph
, Common
, PlotAreaPosition
));
2338 // Draw axis scale break lines
2339 axesArray
= new Axis
[] { axisY, axisY2 }
;
2340 foreach (Axis currentAxis
in axesArray
)
2342 for (int segmentIndex
= 0; segmentIndex
< (currentAxis
.ScaleSegments
.Count
- 1); segmentIndex
++)
2344 currentAxis
.ScaleSegments
[segmentIndex
].PaintBreakLine(graph
, currentAxis
.ScaleSegments
[segmentIndex
+ 1]);
2349 // Reset values for optimized drawing
2350 foreach (Axis curentAxis
in this._axisArray
)
2352 curentAxis
.optimizedGetPosition
= false;
2355 // Reset preffered number of intervals on the axis
2356 curentAxis
.prefferedNumberofIntervals
= 5;
2358 // Reset flag that scale segments are used
2359 curentAxis
.scaleSegmentsUsed
= false;
2366 /// Checks if chart area border should be drawn on top of series.
2368 /// <returns>True if border should be darwn on top.</returns>
2369 private bool IsBorderOnTopSeries()
2371 // For most of the chart types chart area border is drawn on top.
2373 foreach( Series series
in this.Common
.Chart
.Series
)
2375 if(series
.ChartArea
== this.Name
)
2377 // It is common for the Bubble and Point chart types to draw markers
2378 // partially outside of the chart area. By drawing the border before
2379 // series we avoiding the possibility of drawing the border line on
2380 // top of the marker.
2381 if(series
.ChartType
== SeriesChartType
.Bubble
||
2382 series
.ChartType
== SeriesChartType
.Point
)
2392 /// Paint the chart area cursors.
2394 /// <param name="graph">Chart graphics.</param>
2395 /// <param name="cursorOnly">Indicates that only cursors are redrawn.</param>
2396 [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification
= "These parameters are used when compiling for the Microsoft version of Chart")]
2397 internal void PaintCursors( ChartGraphics graph
, bool cursorOnly
)
2399 // Cursors and selection are supoorted only in 2D charts
2400 if(this.Area3DStyle
.Enable3D
== true)
2405 // Do not draw cursor/selection for chart types that do not require axis (like Pie)
2406 if(!this.requireAxes
)
2411 // Cursors and selection are not supoorted in circular areas
2412 if(this.chartAreaIsCurcular
)
2417 // Do not draw cursor/selection while printing
2418 if(this.Common
!= null &&
2419 this.Common
.ChartPicture
!= null &&
2420 this.Common
.ChartPicture
.isPrinting
)
2425 // Do not draw cursor/selection when chart area is not visible
2426 // because either width or height is set to zero
2427 if(this.Position
.Width
== 0f
||
2428 this.Position
.Height
== 0f
)
2433 #if Microsoft_CONTROL
2435 Chart chart
= this.Common
.Chart
;
2436 ChartPicture chartPicture
= Common
.ChartPicture
;
2438 // Check if cursor should be drawn
2439 if(!double.IsNaN(_cursorX
.SelectionStart
) ||
2440 !double.IsNaN(_cursorX
.SelectionEnd
) ||
2441 !double.IsNaN(_cursorX
.Position
) ||
2442 !double.IsNaN(_cursorY
.SelectionStart
) ||
2443 !double.IsNaN(_cursorY
.SelectionEnd
) ||
2444 !double.IsNaN(_cursorY
.Position
))
2447 if(!chartPicture
.backgroundRestored
&&
2448 !chartPicture
.isSelectionMode
)
2450 chartPicture
.backgroundRestored
= true;
2452 Rectangle chartPosition
= new Rectangle(0, 0, chartPicture
.Width
, chartPicture
.Height
);
2454 // Get chart area position
2455 Rectangle absAreaPlotPosition
= Rectangle
.Round(graph
.GetAbsoluteRectangle(PlotAreaPosition
.ToRectangleF()));
2456 int maxCursorWidth
= (CursorY
.LineWidth
> CursorX
.LineWidth
) ? CursorY
.LineWidth
+ 1 : CursorX
.LineWidth
+ 1;
2457 absAreaPlotPosition
.Inflate(maxCursorWidth
, maxCursorWidth
);
2458 absAreaPlotPosition
.Intersect(new Rectangle(0, 0, chart
.Width
, chart
.Height
));
2460 // Create area buffer bitmap
2461 if(areaBufferBitmap
== null ||
2462 chartPicture
.nonTopLevelChartBuffer
== null ||
2465 // Dispose previous bitmap
2466 if(areaBufferBitmap
!= null)
2468 areaBufferBitmap
.Dispose();
2469 areaBufferBitmap
= null;
2471 if(chartPicture
.nonTopLevelChartBuffer
!= null)
2473 chartPicture
.nonTopLevelChartBuffer
.Dispose();
2474 chartPicture
.nonTopLevelChartBuffer
= null;
2478 // Copy chart area plotting rectangle from the chart's dubble buffer image into area dubble buffer image
2479 if(chart
.paintBufferBitmap
!= null)
2481 areaBufferBitmap
= chart
.paintBufferBitmap
.Clone(absAreaPlotPosition
, chart
.paintBufferBitmap
.PixelFormat
);
2484 // Copy whole chart from the chart's dubble buffer image into area dubble buffer image
2485 if(chart
.paintBufferBitmap
!= null &&
2486 chart
.paintBufferBitmap
.Size
.Width
>= chartPosition
.Size
.Width
&&
2487 chart
.paintBufferBitmap
.Size
.Height
>= chartPosition
.Size
.Height
)
2489 chartPicture
.nonTopLevelChartBuffer
= chart
.paintBufferBitmap
.Clone(
2490 chartPosition
, chart
.paintBufferBitmap
.PixelFormat
);
2494 else if(cursorOnly
&& chartPicture
.nonTopLevelChartBuffer
!= null)
2496 // Restore previous background
2497 chart
.paintBufferBitmapGraphics
.DrawImageUnscaled(
2498 chartPicture
.nonTopLevelChartBuffer
,
2503 // Draw chart area cursors and range selection
2505 _cursorY
.Paint(graph
);
2506 _cursorX
.Paint(graph
);
2509 #endif // Microsoft_CONTROL
2515 #region Circular chart area methods
2518 /// Gets a circular chart type interface that belongs to this chart area.
2520 /// <returns>ICircularChartType interface or null.</returns>
2521 internal ICircularChartType
GetCircularChartType()
2523 // Get number of sectors in circular chart area
2524 foreach(Series series
in this.Common
.DataManager
.Series
)
2526 if(series
.IsVisible() && series
.ChartArea
== this.Name
)
2528 ICircularChartType type
= Common
.ChartTypeRegistry
.GetChartType(series
.ChartTypeName
) as ICircularChartType
;;
2539 /// Calculate size of the circular axis labels and sets auto-fit font.
2541 /// <param name="chartGraph">Chart graphics object.</param>
2542 /// <param name="chartAreaPosition">The Chart area position.</param>
2543 /// <param name="plotArea">Plotting area size.</param>
2544 /// <param name="xTitleSize">Size of title on the axis.</param>
2545 /// <param name="yTitleSize">Size of title on the axis.</param>
2546 internal void FitCircularLabels(
2547 ChartGraphics chartGraph
,
2548 ElementPosition chartAreaPosition
,
2549 ref RectangleF plotArea
,
2553 // Check if axis labels are enabled
2554 if(!this.AxisX
.LabelStyle
.Enabled
)
2559 // Get absolute titles size
2560 SizeF titleSize
= chartGraph
.GetAbsoluteSize(new SizeF(xTitleSize
, yTitleSize
));
2562 // Get absolute position of area
2563 RectangleF plotAreaRectAbs
= chartGraph
.GetAbsoluteRectangle( plotArea
);
2564 RectangleF areaRectAbs
= chartGraph
.GetAbsoluteRectangle( chartAreaPosition
.ToRectangleF());
2566 // Get absolute markers size and spacing
2567 float spacing
= chartGraph
.GetAbsolutePoint(new PointF(0, this.AxisX
.markSize
+ Axis
.elementSpacing
)).Y
;
2569 // Get circular axis list
2570 ArrayList axisList
= GetCircularAxisList();
2572 // Get circular axis labels style
2573 CircularAxisLabelsStyle labelsStyle
= GetCircularAxisLabelsStyle();
2575 //*****************************************************************
2576 //** Calculate the auto-fit font if required
2577 //*****************************************************************
2578 if(this.AxisX
.LabelStyle
.Enabled
&& this.AxisX
.IsLabelAutoFit
)
2580 // Set max auto fit font
2581 this.AxisX
.autoLabelFont
= Common
.ChartPicture
.FontCache
.GetFont(
2582 this.AxisX
.LabelStyle
.Font
.FontFamily
,
2584 this.AxisX
.LabelStyle
.Font
.Style
,
2585 GraphicsUnit
.Point
);
2587 // Get estimated labels size
2588 float labelsSizeEstimate
= GetCircularLabelsSize(chartGraph
, areaRectAbs
, plotAreaRectAbs
, titleSize
);
2589 labelsSizeEstimate
= (float)Math
.Min(labelsSizeEstimate
* 1.1f
, plotAreaRectAbs
.Width
/ 5f
);
2590 labelsSizeEstimate
+= spacing
;
2592 // Calculate auto-fit font
2593 this.AxisX
.GetCircularAxisLabelsAutoFitFont(chartGraph
, axisList
, labelsStyle
, plotAreaRectAbs
, areaRectAbs
, labelsSizeEstimate
);
2596 //*****************************************************************
2597 //** Shrink plot area size proportionally
2598 //*****************************************************************
2601 float labelsSize
= GetCircularLabelsSize(chartGraph
, areaRectAbs
, plotAreaRectAbs
, titleSize
);
2603 // Check if change size is smaller than radius
2604 labelsSize
= (float)Math
.Min(labelsSize
, plotAreaRectAbs
.Width
/ 2.5f
);
2605 labelsSize
+= spacing
;
2607 plotAreaRectAbs
.X
+= labelsSize
;
2608 plotAreaRectAbs
.Width
-= 2f
* labelsSize
;
2609 plotAreaRectAbs
.Y
+= labelsSize
;
2610 plotAreaRectAbs
.Height
-= 2f
* labelsSize
;
2612 // Restrict minimum plot area size
2613 if(plotAreaRectAbs
.Width
< 1.0f
)
2615 plotAreaRectAbs
.Width
= 1.0f
;
2617 if(plotAreaRectAbs
.Height
< 1.0f
)
2619 plotAreaRectAbs
.Height
= 1.0f
;
2622 plotArea
= chartGraph
.GetRelativeRectangle( plotAreaRectAbs
);
2625 //*****************************************************************
2626 //** Set axes labels size
2627 //*****************************************************************
2628 SizeF relativeLabelSize
= chartGraph
.GetRelativeSize(new SizeF(labelsSize
, labelsSize
));
2629 this.AxisX
.labelSize
= relativeLabelSize
.Height
;
2630 this.AxisX2
.labelSize
= relativeLabelSize
.Height
;
2631 this.AxisY
.labelSize
= relativeLabelSize
.Width
;
2632 this.AxisY2
.labelSize
= relativeLabelSize
.Width
;
2637 /// Calculate size of the circular axis labels.
2639 /// <param name="chartGraph">Chart graphics object.</param>
2640 /// <param name="areaRectAbs">The Chart area position.</param>
2641 /// <param name="plotAreaRectAbs">Plotting area size.</param>
2642 /// <param name="titleSize">Size of title on the axes.</param>
2643 /// <returns>Circulat labels style.</returns>
2644 internal float GetCircularLabelsSize(
2645 ChartGraphics chartGraph
,
2646 RectangleF areaRectAbs
,
2647 RectangleF plotAreaRectAbs
,
2650 // Find current horiz. and vert. spacing between plotting and chart areas
2651 SizeF areaDiff
= new SizeF(plotAreaRectAbs
.X
- areaRectAbs
.X
, plotAreaRectAbs
.Y
- areaRectAbs
.Y
);
2652 areaDiff
.Width
-= titleSize
.Width
;
2653 areaDiff
.Height
-= titleSize
.Height
;
2655 // Get absolute center of the area
2656 PointF areaCenterAbs
= chartGraph
.GetAbsolutePoint(this.circularCenter
);
2658 // Get circular axis list
2659 ArrayList axisList
= GetCircularAxisList();
2661 // Get circular axis labels style
2662 CircularAxisLabelsStyle labelsStyle
= GetCircularAxisLabelsStyle();
2664 // Defines on how much (pixels) the circular chart area radius should be reduced
2665 float labelsSize
= 0f
;
2667 //*****************************************************************
2668 //** Loop through all axis labels
2669 //*****************************************************************
2670 foreach(CircularChartAreaAxis axis
in axisList
)
2672 //*****************************************************************
2673 //** Measure label text
2674 //*****************************************************************
2675 SizeF textSize
= chartGraph
.MeasureString(
2676 axis
.Title
.Replace("\\n", "\n"),
2677 (this.AxisX
.autoLabelFont
== null) ? this.AxisX
.LabelStyle
.Font
: this.AxisX
.autoLabelFont
);
2678 textSize
.Width
= (float)Math
.Ceiling(textSize
.Width
* 1.1f
);
2679 textSize
.Height
= (float)Math
.Ceiling(textSize
.Height
* 1.1f
);
2682 //*****************************************************************
2683 //** Calculate area size change depending on labels style
2684 //*****************************************************************
2685 if(labelsStyle
== CircularAxisLabelsStyle
.Circular
)
2687 labelsSize
= (float)Math
.Max(
2691 else if(labelsStyle
== CircularAxisLabelsStyle
.Radial
)
2693 float textAngle
= axis
.AxisPosition
+ 90;
2695 // For angled text find it's X and Y components
2696 float width
= (float)Math
.Cos(textAngle
/180F
*Math
.PI
) * textSize
.Width
;
2697 float height
= (float)Math
.Sin(textAngle
/180F
*Math
.PI
) * textSize
.Width
;
2698 width
= (float)Math
.Abs(Math
.Ceiling(width
));
2699 height
= (float)Math
.Abs(Math
.Ceiling(height
));
2701 // Reduce text size by current spacing between plotting area and chart area
2702 width
-= areaDiff
.Width
;
2703 height
-= areaDiff
.Height
;
2710 labelsSize
= (float)Math
.Max(
2712 Math
.Max(width
, height
));
2714 else if(labelsStyle
== CircularAxisLabelsStyle
.Horizontal
)
2717 float textAngle
= axis
.AxisPosition
;
2718 if(textAngle
> 180f
)
2723 // Get label rotated position
2724 PointF
[] labelPosition
= new PointF
[] { new PointF(areaCenterAbs.X, plotAreaRectAbs.Y) }
;
2725 Matrix newMatrix
= new Matrix();
2726 newMatrix
.RotateAt(textAngle
, areaCenterAbs
);
2727 newMatrix
.TransformPoints(labelPosition
);
2730 float width
= textSize
.Width
;
2731 width
-= areaRectAbs
.Right
- labelPosition
[0].X
;
2737 labelsSize
= (float)Math
.Max(
2739 Math
.Max(width
, textSize
.Height
));
2747 /// True if polygons should be used instead of the circles for the chart area.
2749 internal bool CircularUsePolygons
2753 // Check if value was precalculated
2754 if(this._circularUsePolygons
== int.MinValue
)
2756 _circularUsePolygons
= 0;
2758 // Look for custom properties in series
2759 foreach(Series series
in this.Common
.DataManager
.Series
)
2761 if(series
.ChartArea
== this.Name
&& series
.IsVisible())
2763 // Get custom attribute
2764 if (series
.IsCustomPropertySet(CustomPropertyName
.AreaDrawingStyle
))
2766 if(String
.Compare(series
[CustomPropertyName
.AreaDrawingStyle
], "Polygon", StringComparison
.OrdinalIgnoreCase
) == 0)
2768 _circularUsePolygons
= 1;
2770 else if (String
.Compare(series
[CustomPropertyName
.AreaDrawingStyle
], "Circle", StringComparison
.OrdinalIgnoreCase
) == 0)
2772 _circularUsePolygons
= 0;
2776 throw(new InvalidOperationException(SR
.ExceptionCustomAttributeValueInvalid( series
[CustomPropertyName
.AreaDrawingStyle
], "AreaDrawingStyle")));
2784 return (this._circularUsePolygons
== 1);
2789 /// Gets circular area axis labels style.
2791 /// <returns>Axis labels style.</returns>
2792 internal CircularAxisLabelsStyle
GetCircularAxisLabelsStyle()
2794 CircularAxisLabelsStyle style
= CircularAxisLabelsStyle
.Auto
;
2796 // Get maximum number of points in all series
2797 foreach(Series series
in this.Common
.DataManager
.Series
)
2799 if(series
.IsVisible() && series
.ChartArea
== this.Name
&& series
.IsCustomPropertySet(CustomPropertyName
.CircularLabelsStyle
))
2801 string styleName
= series
[CustomPropertyName
.CircularLabelsStyle
];
2802 if(String
.Compare( styleName
, "Auto", StringComparison
.OrdinalIgnoreCase
) == 0 )
2804 style
= CircularAxisLabelsStyle
.Auto
;
2806 else if(String
.Compare( styleName
,"Circular", StringComparison
.OrdinalIgnoreCase
) == 0)
2808 style
= CircularAxisLabelsStyle
.Circular
;
2810 else if(String
.Compare( styleName
,"Radial", StringComparison
.OrdinalIgnoreCase
) == 0)
2812 style
= CircularAxisLabelsStyle
.Radial
;
2814 else if (String
.Compare(styleName
, "Horizontal", StringComparison
.OrdinalIgnoreCase
) == 0)
2816 style
= CircularAxisLabelsStyle
.Horizontal
;
2820 throw(new InvalidOperationException(SR
.ExceptionCustomAttributeValueInvalid( styleName
, "CircularLabelsStyle")));
2827 if(style
== CircularAxisLabelsStyle
.Auto
)
2829 int sectorNumber
= CircularSectorsNumber
;
2830 style
= CircularAxisLabelsStyle
.Horizontal
;
2831 if(sectorNumber
> 30)
2833 style
= CircularAxisLabelsStyle
.Radial
;
2841 /// Number of sectors in the circular area.
2843 internal int CircularSectorsNumber
2847 // Check if value was precalculated
2848 if(this._circularSectorNumber
== int.MinValue
)
2850 this._circularSectorNumber
= GetCircularSectorNumber();
2853 return this._circularSectorNumber
;
2858 /// Gets number of sectors in the circular chart area.
2860 /// <returns>Number of sectors.</returns>
2861 private int GetCircularSectorNumber()
2863 ICircularChartType type
= this.GetCircularChartType();
2866 return type
.GetNumerOfSectors(this, this.Common
.DataManager
.Series
);
2872 /// Fills a list of circular axis.
2874 /// <returns>Axes list.</returns>
2875 internal ArrayList
GetCircularAxisList()
2877 // Check if list was already created
2878 if(_circularAxisList
== null)
2880 _circularAxisList
= new ArrayList();
2882 // Loop through all sectors
2883 int sectorNumber
= GetCircularSectorNumber();
2884 for(int sectorIndex
= 0; sectorIndex
< sectorNumber
; sectorIndex
++)
2886 // Create new axis object
2887 CircularChartAreaAxis axis
= new CircularChartAreaAxis(sectorIndex
* 360f
/sectorNumber
);
2889 // Check if custom X axis labels will be used
2890 if(this.AxisX
.CustomLabels
.Count
> 0)
2892 if(sectorIndex
< this.AxisX
.CustomLabels
.Count
)
2894 axis
.Title
= this.AxisX
.CustomLabels
[sectorIndex
].Text
;
2895 axis
.TitleForeColor
= this.AxisX
.CustomLabels
[sectorIndex
].ForeColor
;
2900 // Get axis title from all series
2901 foreach(Series series
in this.Common
.DataManager
.Series
)
2903 if(series
.IsVisible() && series
.ChartArea
== this.Name
&& sectorIndex
< series
.Points
.Count
)
2905 if(series
.Points
[sectorIndex
].AxisLabel
.Length
> 0)
2907 axis
.Title
= series
.Points
[sectorIndex
].AxisLabel
;
2914 // Add axis into the list
2915 _circularAxisList
.Add(axis
);
2919 return _circularAxisList
;
2923 /// Converts circular position of the X axis to angle in degrees.
2925 /// <param name="position">X axis position.</param>
2926 /// <returns>Angle in degrees.</returns>
2927 internal float CircularPositionToAngle(double position
)
2929 // Get X axis scale size
2930 double scaleRatio
= 360.0 / Math
.Abs(this.AxisX
.Maximum
- this.AxisX
.Minimum
);
2932 return (float)(position
* scaleRatio
+ this.AxisX
.Crossing
);
2937 #region 2D Series drawing order methods
2940 /// Helper method that returns a list of 'ChartTypeAndSeriesInfo' objects.
2941 /// This list is used for chart area series drawing in 2D mode. Each
2942 /// object may represent an individual series or all series that belong
2943 /// to one chart type.
2945 /// This method is intended to fix issues #6443 and #5385 when area chart
2946 /// type incorrectly overlaps point or line chart type.
2948 /// <returns>List of 'ChartTypeAndSeriesInfo' objects.</returns>
2949 private ArrayList
GetChartTypesAndSeriesToDraw()
2951 ArrayList resultList
= new ArrayList();
2953 // Build chart type or series position based lists
2954 if (this.ChartTypes
.Count
> 1 &&
2955 (this.ChartTypes
.Contains(ChartTypeNames
.Area
)
2956 || this.ChartTypes
.Contains(ChartTypeNames
.SplineArea
)
2960 // Array of chart type names that do not require furher processing
2961 ArrayList processedChartType
= new ArrayList();
2962 ArrayList splitChartType
= new ArrayList();
2964 // Draw using the exact order in the series collection
2965 int seriesIndex
= 0;
2966 foreach (Series series
in this.Common
.DataManager
.Series
)
2968 // Check if series is visible and belongs to the chart area
2969 if (series
.ChartArea
==this.Name
&& series
.IsVisible() && series
.Points
.Count
> 0)
2971 // Check if this chart type was already processed
2972 if (!processedChartType
.Contains(series
.ChartTypeName
))
2974 // Check if curent chart type can be individually processed
2975 bool canBeIndividuallyProcessed
= false;
2976 if (series
.ChartType
== SeriesChartType
.Point
||
2977 series
.ChartType
== SeriesChartType
.Line
||
2978 series
.ChartType
== SeriesChartType
.Spline
||
2979 series
.ChartType
== SeriesChartType
.StepLine
)
2981 canBeIndividuallyProcessed
= true;
2984 if (!canBeIndividuallyProcessed
)
2986 // Add a record to process all series of that chart type at once
2987 resultList
.Add(new ChartTypeAndSeriesInfo(series
.ChartTypeName
));
2988 processedChartType
.Add(series
.ChartTypeName
);
2992 // Check if curent chart type has more that 1 series and they are split
2994 bool chartTypeIsSplit
= false;
2996 if (splitChartType
.Contains(series
.ChartTypeName
))
2998 chartTypeIsSplit
= true;
3002 bool otherChartTypeFound
= false;
3003 for (int curentSeriesIndex
= seriesIndex
+ 1; curentSeriesIndex
< this.Common
.DataManager
.Series
.Count
; curentSeriesIndex
++)
3005 if (series
.ChartTypeName
== this.Common
.DataManager
.Series
[curentSeriesIndex
].ChartTypeName
)
3007 if (otherChartTypeFound
)
3009 chartTypeIsSplit
= true;
3010 splitChartType
.Add(series
.ChartTypeName
);
3015 if (this.Common
.DataManager
.Series
[curentSeriesIndex
].ChartType
== SeriesChartType
.Area
||
3016 this.Common
.DataManager
.Series
[curentSeriesIndex
].ChartType
== SeriesChartType
.SplineArea
)
3018 otherChartTypeFound
= true;
3024 if (chartTypeIsSplit
)
3026 // Add a record to process this series individually
3027 resultList
.Add(new ChartTypeAndSeriesInfo(series
));
3031 // Add a record to process all series of that chart type at once
3032 resultList
.Add(new ChartTypeAndSeriesInfo(series
.ChartTypeName
));
3033 processedChartType
.Add(series
.ChartTypeName
);
3044 foreach (string chartType
in this.ChartTypes
)
3046 resultList
.Add(new ChartTypeAndSeriesInfo(chartType
));
3054 /// Internal data structure that stores chart type name and optionally series object.
3056 internal class ChartTypeAndSeriesInfo
3059 /// Object constructor.
3061 public ChartTypeAndSeriesInfo()
3066 /// Object constructor.
3068 /// <param name="chartType">Chart type name to initialize with.</param>
3069 public ChartTypeAndSeriesInfo(string chartType
)
3071 this.ChartType
= chartType
;
3075 /// Object constructor.
3077 /// <param name="series">Series to initialize with.</param>
3078 public ChartTypeAndSeriesInfo(Series series
)
3080 this.ChartType
= series
.ChartTypeName
;
3081 this.Series
= series
;
3085 internal string ChartType
= string.Empty
;
3087 // Series object. Can be set to NULL!
3088 internal Series Series
= null;
3092 #endregion // 2D Series drawing order methods
3094 #region IDisposable Members
3097 /// Releases unmanaged and - optionally - managed resources
3099 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
3100 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId
= "axisX")]
3101 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId
= "axisX2")]
3102 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId
= "axisY")]
3103 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId
= "axisY2")]
3104 protected override void Dispose(bool disposing
)
3108 // Dispose managed resources
3109 if (this._axisArray
!= null)
3111 foreach (Axis axis
in this._axisArray
)
3115 this._axisArray
= null;
3117 if ( this._areaPosition
!= null)
3119 this._areaPosition
.Dispose();
3120 this._areaPosition
= null;
3122 if (this._innerPlotPosition
!= null)
3124 this._innerPlotPosition
.Dispose();
3125 this._innerPlotPosition
= null;
3127 if (this.PlotAreaPosition
!= null)
3129 this.PlotAreaPosition
.Dispose();
3130 this.PlotAreaPosition
= null;
3132 #if Microsoft_CONTROL
3133 if (this.areaBufferBitmap
!= null)
3135 this.areaBufferBitmap
.Dispose();
3136 this.areaBufferBitmap
= null;
3138 if (this._cursorX
!= null)
3140 this._cursorX
.Dispose();
3141 this._cursorX
= null;
3143 if (this._cursorY
!= null)
3145 this._cursorY
.Dispose();
3146 this._cursorY
= null;
3150 base.Dispose(disposing
);