Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / General / ChartArea.cs
blob86b909780d48becd3f74d947fc2ec270f7352a38
1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: ChartArea.cs
9 //
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
12 // Classes: ChartArea
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
26 using System;
27 using System.Collections;
28 using System.Collections.Specialized;
29 using System.ComponentModel;
30 using System.ComponentModel.Design;
31 using System.Data;
32 using System.Drawing;
33 using System.Drawing.Design;
34 using System.Drawing.Drawing2D;
35 using System.Globalization;
36 using System.Diagnostics.CodeAnalysis;
38 #if Microsoft_CONTROL
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;
48 #else
49 using System.Web;
50 using System.Web.UI;
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;
56 #endif
59 #endregion
61 #if Microsoft_CONTROL
62 namespace System.Windows.Forms.DataVisualization.Charting
63 #else
64 namespace System.Web.UI.DataVisualization.Charting
65 #endif
67 #region Chart area aligment enumerations
69 /// <summary>
70 /// An enumeration of the alignment orientations of a ChartArea
71 /// </summary>
72 [Flags]
73 public enum AreaAlignmentOrientations
75 /// <summary>
76 /// Chart areas are not automatically aligned.
77 /// </summary>
78 None = 0,
80 /// <summary>
81 /// Chart areas are aligned vertically.
82 /// </summary>
83 Vertical = 1,
85 /// <summary>
86 /// Chart areas are aligned horizontally.
87 /// </summary>
88 Horizontal = 2,
90 /// <summary>
91 /// Chart areas are aligned using all values (horizontally and vertically).
92 /// </summary>
93 All = Vertical | Horizontal
96 /// <summary>
97 /// An enumeration of the alignment styles of a ChartArea
98 /// </summary>
99 [Flags]
100 public enum AreaAlignmentStyles
102 /// <summary>
103 /// Chart areas are not automatically aligned.
104 /// </summary>
105 None = 0,
107 /// <summary>
108 /// Chart areas are aligned by positions.
109 /// </summary>
110 Position = 1,
112 /// <summary>
113 /// Chart areas are aligned by inner plot positions.
114 /// </summary>
115 PlotPosition = 2,
117 /// <summary>
118 /// Chart areas are aligned by axes views.
119 /// </summary>
120 AxesView = 4,
122 #if Microsoft_CONTROL
124 /// <summary>
125 /// Cursor and Selection alignment.
126 /// </summary>
127 Cursor = 8,
129 /// <summary>
130 /// Complete alignment.
131 /// </summary>
132 All = Position | PlotPosition | Cursor | AxesView
133 #else // Microsoft_CONTROL
135 /// <summary>
136 /// Complete alignment.
137 /// </summary>
138 All = Position | PlotPosition | AxesView
140 #endif // Microsoft_CONTROL
143 #endregion
145 /// <summary>
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
151 /// is unlimited.
152 ///
153 /// ChartArea class exposes all the properties and methods
154 /// of its base ChartArea3D class.
155 /// </summary>
157 DefaultProperty("Axes"),
158 SRDescription("DescriptionAttributeChartArea_ChartArea"),
160 #if ASPPERM_35
161 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
162 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
163 #endif
164 public partial class ChartArea : ChartNamedElement
166 #region Chart Area Fields
168 /// <summary>
169 /// Plot area position
170 /// </summary>
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();
225 #endif
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;
233 #endregion
235 #region Chart Area Cursor properties
237 #if Microsoft_CONTROL
239 /// <summary>
240 /// Gets or sets a Cursor object that is used for cursors and selected ranges along the X-axis.
241 /// </summary>
243 SRCategory("CategoryAttributeCursor"),
244 Bindable(true),
245 DefaultValue(null),
246 SRDescription("DescriptionAttributeChartArea_CursorX"),
247 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
248 TypeConverter(typeof(NoNameExpandableObjectConverter)),
250 public Cursor CursorX
254 return _cursorX;
258 _cursorX = value;
260 // Initialize chart object
261 _cursorX.Initialize(this, AxisName.X);
265 /// <summary>
266 /// Gets or sets a Cursor object that is used for cursors and selected ranges along the Y-axis.
267 /// </summary>
269 SRCategory("CategoryAttributeCursor"),
270 Bindable(true),
271 DefaultValue(null),
272 SRDescription("DescriptionAttributeChartArea_CursorY"),
273 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
274 TypeConverter(typeof(NoNameExpandableObjectConverter)),
276 public Cursor CursorY
280 return _cursorY;
284 _cursorY = value;
286 // Initialize chart object
287 _cursorY.Initialize(this, AxisName.Y);
291 #endif // Microsoft_CONTROL
293 #endregion
295 #region Chart Area properties
297 /// <summary>
298 /// Gets or sets a flag that specifies whether the chart area is visible.
299 /// </summary>
300 /// <remarks>
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.
303 /// </remarks>
304 /// <value>
305 /// <b>True</b> if the chart area is visible; <b>false</b> otherwise.
306 /// </value>
308 SRCategory("CategoryAttributeAppearance"),
309 DefaultValue(true),
310 SRDescription("DescriptionAttributeChartArea_Visible"),
311 ParenthesizePropertyNameAttribute(true),
313 virtual public bool Visible
317 return _visible;
321 _visible = value;
322 this.Invalidate();
326 /// <summary>
327 /// Gets or sets the name of the ChartArea object to which this chart area should be aligned.
328 /// </summary>
330 SRCategory("CategoryAttributeAlignment"),
331 Bindable(true),
332 DefaultValue(Constants.NotSetValue),
333 SRDescription("DescriptionAttributeChartArea_AlignWithChartArea"),
334 TypeConverter(typeof(LegendAreaNameConverter)),
335 #if !Microsoft_CONTROL
336 PersistenceMode(PersistenceMode.Attribute)
337 #endif
339 public string AlignWithChartArea
343 return _alignWithChartArea;
347 if (value != _alignWithChartArea)
349 if (String.IsNullOrEmpty(value))
351 _alignWithChartArea = Constants.NotSetValue;
353 else
355 if (Chart != null && Chart.ChartAreas != null)
357 Chart.ChartAreas.VerifyNameReference(value);
359 _alignWithChartArea = value;
361 Invalidate();
366 /// <summary>
367 /// Gets or sets the alignment orientation of a chart area.
368 /// </summary>
370 SRCategory("CategoryAttributeAlignment"),
371 Bindable(true),
372 DefaultValue(AreaAlignmentOrientations.Vertical),
373 SRDescription("DescriptionAttributeChartArea_AlignOrientation"),
374 Editor(Editors.FlagsEnumUITypeEditor.Editor, Editors.FlagsEnumUITypeEditor.Base),
375 #if !Microsoft_CONTROL
376 PersistenceMode(PersistenceMode.Attribute)
377 #endif
379 public AreaAlignmentOrientations AlignmentOrientation
383 return _alignmentOrientation;
387 _alignmentOrientation = value;
388 Invalidate();
393 /// <summary>
394 /// Gets or sets the alignment style of the ChartArea.
395 /// </summary>
397 SRCategory("CategoryAttributeAlignment"),
398 Bindable(true),
399 DefaultValue(AreaAlignmentStyles.All),
400 SRDescription("DescriptionAttributeChartArea_AlignType"),
401 Editor(Editors.FlagsEnumUITypeEditor.Editor, Editors.FlagsEnumUITypeEditor.Base),
402 #if !Microsoft_CONTROL
403 PersistenceMode(PersistenceMode.Attribute)
404 #endif
406 public AreaAlignmentStyles AlignmentStyle
410 return _alignmentStyle;
414 _alignmentStyle = value;
415 Invalidate();
419 /// <summary>
420 /// Gets or sets an array that represents all axes for a chart area.
421 /// </summary>
423 SRCategory("CategoryAttributeAxes"),
424 Bindable(true),
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")]
432 public Axis[] Axes
436 return _axisArray;
440 AxisX = value[0];
441 AxisY = value[1];
442 AxisX2 = value[2];
443 AxisY2 = value[3];
444 Invalidate();
448 /// <summary>
449 /// Avoid serialization of the axes array
450 /// </summary>
451 [EditorBrowsableAttribute(EditorBrowsableState.Never)]
452 internal bool ShouldSerializeAxes()
454 return false;
457 /// <summary>
458 /// Gets or sets an Axis object that represents the primary Y-axis.
459 /// </summary>
461 SRCategory("CategoryAttributeAxis"),
462 Bindable(true),
463 Browsable(false),
464 SRDescription("DescriptionAttributeChartArea_AxisY"),
465 #if Microsoft_CONTROL
466 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
467 #else
468 PersistenceMode(PersistenceMode.InnerProperty),
469 #endif
470 TypeConverter(typeof(NoNameExpandableObjectConverter))
472 public Axis AxisY
476 return axisY;
480 axisY = value;
481 axisY.Initialize(this, AxisName.Y);
482 _axisArray[1] = axisY;
483 Invalidate();
487 /// <summary>
488 /// Gets or sets an Axis object that represents the primary X-axis.
489 /// </summary>
491 SRCategory("CategoryAttributeAxis"),
492 Bindable(true),
493 Browsable(false),
494 SRDescription("DescriptionAttributeChartArea_AxisX"),
495 #if Microsoft_CONTROL
496 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
497 #else
498 PersistenceMode(PersistenceMode.InnerProperty),
499 #endif
500 TypeConverter(typeof(NoNameExpandableObjectConverter))
502 public Axis AxisX
506 return axisX;
510 axisX = value;
511 axisX.Initialize(this, AxisName.X);
512 _axisArray[0] = axisX;
513 Invalidate();
517 /// <summary>
518 /// Gets or sets an Axis object that represents the secondary X-axis.
519 /// </summary>
521 SRCategory("CategoryAttributeAxis"),
522 Bindable(true),
523 Browsable(false),
524 SRDescription("DescriptionAttributeChartArea_AxisX2"),
525 #if Microsoft_CONTROL
526 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
527 #else
528 PersistenceMode(PersistenceMode.InnerProperty),
529 #endif
530 TypeConverter(typeof(NoNameExpandableObjectConverter))
532 public Axis AxisX2
536 return axisX2;
540 axisX2 = value;
541 axisX2.Initialize(this, AxisName.X2);
542 _axisArray[2] = axisX2;
543 Invalidate();
547 /// <summary>
548 /// Gets or sets an Axis object that represents the secondary Y-axis.
549 /// </summary>
551 SRCategory("CategoryAttributeAxis"),
552 Bindable(true),
553 Browsable(false),
554 SRDescription("DescriptionAttributeChartArea_AxisY2"),
555 #if Microsoft_CONTROL
556 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
557 #else
558 PersistenceMode(PersistenceMode.InnerProperty),
559 #endif
560 TypeConverter(typeof(NoNameExpandableObjectConverter))
562 public Axis AxisY2
566 return axisY2;
570 axisY2 = value;
571 axisY2.Initialize(this, AxisName.Y2);
572 _axisArray[3] = axisY2;
573 Invalidate();
577 /// <summary>
578 /// Gets or sets an ElementPosition object, which defines the position of a chart area object within the chart image.
579 /// </summary>
581 SRCategory("CategoryAttributeAppearance"),
582 Bindable(true),
583 SRDescription("DescriptionAttributeChartArea_Position"),
584 #if Microsoft_CONTROL
585 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
586 #else
587 PersistenceMode(PersistenceMode.InnerProperty),
588 #endif
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();
604 else
606 ElementPosition newPosition = new ElementPosition();
607 #if Microsoft_CONTROL
608 newPosition.Auto = false;
609 #else
610 newPosition.Auto = true;
611 #endif
612 newPosition.SetPositionNoAuto(_areaPosition.X, _areaPosition.Y, _areaPosition.Width, _areaPosition.Height);
613 return newPosition;
616 return _areaPosition;
620 _areaPosition = value;
621 _areaPosition.Parent = this;
622 _areaPosition.resetAreaAutoPosition = true;
623 Invalidate();
627 /// <summary>
628 /// Determoines if this position should be serialized.
629 /// </summary>
630 /// <returns></returns>
631 internal bool ShouldSerializePosition()
633 return !this.Position.Auto;
636 /// <summary>
637 /// Gets or sets an ElementPosition object, which defines the inner plot position of a chart area object.
638 /// </summary>
640 SRCategory("CategoryAttributeAppearance"),
641 Bindable(true),
642 SRDescription("DescriptionAttributeChartArea_InnerPlotPosition"),
643 #if Microsoft_CONTROL
644 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
645 #else
646 PersistenceMode(PersistenceMode.InnerProperty),
647 #endif
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();
663 else
665 ElementPosition newPosition = new ElementPosition();
666 #if Microsoft_CONTROL
667 newPosition.Auto = false;
668 #else
669 newPosition.Auto = true;
670 #endif
671 newPosition.SetPositionNoAuto(_innerPlotPosition.X, _innerPlotPosition.Y, _innerPlotPosition.Width, _innerPlotPosition.Height);
672 return newPosition;
675 return _innerPlotPosition;
679 _innerPlotPosition = value;
680 _innerPlotPosition.Parent = this;
681 Invalidate();
685 /// <summary>
686 /// Determoines if this position should be serialized.
687 /// </summary>
688 /// <returns></returns>
689 internal bool ShouldSerializeInnerPlotPosition()
691 return !this.InnerPlotPosition.Auto;
694 /// <summary>
695 /// Gets or sets the background color of a ChartArea object.
696 /// </summary>
699 SRCategory("CategoryAttributeAppearance"),
700 Bindable(true),
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)
708 #endif
710 public Color BackColor
714 return _backColor;
718 _backColor = value;
719 Invalidate();
723 /// <summary>
724 /// Gets or sets the hatching style of a ChartArea object.
725 /// </summary>
729 SRCategory("CategoryAttributeAppearance"),
730 Bindable(true),
731 DefaultValue(ChartHatchStyle.None),
732 NotifyParentPropertyAttribute(true),
733 SRDescription("DescriptionAttributeBackHatchStyle"),
734 #if !Microsoft_CONTROL
735 PersistenceMode(PersistenceMode.Attribute),
736 #endif
737 Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
740 public ChartHatchStyle BackHatchStyle
744 return _backHatchStyle;
748 _backHatchStyle = value;
749 Invalidate();
753 /// <summary>
754 /// Gets or sets the background image of a ChartArea object.
755 /// </summary>
757 SRCategory("CategoryAttributeAppearance"),
758 Bindable(true),
759 DefaultValue(""),
760 SRDescription("DescriptionAttributeBackImage"),
761 Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
762 #if !Microsoft_CONTROL
763 PersistenceMode(PersistenceMode.Attribute),
764 #endif
765 NotifyParentPropertyAttribute(true)
767 public string BackImage
771 return _backImage;
775 _backImage = value;
776 Invalidate();
780 /// <summary>
781 /// Gets or sets the drawing mode of the background image of a ChartArea object.
782 /// </summary>
784 SRCategory("CategoryAttributeAppearance"),
785 Bindable(true),
786 DefaultValue(ChartImageWrapMode.Tile),
787 NotifyParentPropertyAttribute(true),
788 SRDescription("DescriptionAttributeImageWrapMode"),
789 #if !Microsoft_CONTROL
790 PersistenceMode(PersistenceMode.Attribute)
791 #endif
793 public ChartImageWrapMode BackImageWrapMode
797 return _backImageWrapMode;
801 _backImageWrapMode = value;
802 Invalidate();
806 /// <summary>
807 /// Gets or sets the color of a ChartArea object's background image that will be drawn as transparent.
808 /// </summary>
810 SRCategory("CategoryAttributeAppearance"),
811 Bindable(true),
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)
819 #endif
821 public Color BackImageTransparentColor
825 return _backImageTransparentColor;
829 _backImageTransparentColor = value;
830 Invalidate();
834 /// <summary>
835 /// Gets or sets the alignment of a ChartArea object.
836 /// </summary>
838 SRCategory("CategoryAttributeAppearance"),
839 Bindable(true),
840 DefaultValue(ChartImageAlignmentStyle.TopLeft),
841 NotifyParentPropertyAttribute(true),
842 SRDescription("DescriptionAttributeBackImageAlign"),
843 #if !Microsoft_CONTROL
844 PersistenceMode(PersistenceMode.Attribute)
845 #endif
847 public ChartImageAlignmentStyle BackImageAlignment
851 return _backImageAlignment;
855 _backImageAlignment = value;
856 Invalidate();
860 /// <summary>
861 /// Gets or sets the orientation of a chart element's gradient,
862 /// and also determines whether or not a gradient is used.
863 /// </summary>
866 SRCategory("CategoryAttributeAppearance"),
867 Bindable(true),
868 DefaultValue(GradientStyle.None),
869 NotifyParentPropertyAttribute(true),
870 SRDescription("DescriptionAttributeBackGradientStyle"),
871 #if !Microsoft_CONTROL
872 PersistenceMode(PersistenceMode.Attribute),
873 #endif
874 Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
876 public GradientStyle BackGradientStyle
880 return _backGradientStyle;
884 _backGradientStyle = value;
885 Invalidate();
889 /// <summary>
890 /// Gets or sets the secondary color of a ChartArea object.
891 /// </summary>
894 SRCategory("CategoryAttributeAppearance"),
895 Bindable(true),
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)
903 #endif
905 public Color BackSecondaryColor
909 return _backSecondaryColor;
913 _backSecondaryColor = value;
914 Invalidate();
918 /// <summary>
919 /// Gets or sets the shadow color of a ChartArea object.
920 /// </summary>
922 SRCategory("CategoryAttributeAppearance"),
923 Bindable(true),
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)
931 #endif
933 public Color ShadowColor
937 return _shadowColor;
941 _shadowColor = value;
942 Invalidate();
946 /// <summary>
947 /// Gets or sets the shadow offset (in pixels) of a ChartArea object.
948 /// </summary>
950 SRCategory("CategoryAttributeAppearance"),
951 Bindable(true),
952 DefaultValue(0),
953 SRDescription("DescriptionAttributeShadowOffset"),
954 NotifyParentPropertyAttribute(true),
955 #if !Microsoft_CONTROL
956 PersistenceMode(PersistenceMode.Attribute)
957 #endif
959 public int ShadowOffset
963 return _shadowOffset;
967 _shadowOffset = value;
968 Invalidate();
972 /// <summary>
973 /// Gets or sets the border color of a ChartArea object.
974 /// </summary>
977 SRCategory("CategoryAttributeAppearance"),
978 Bindable(true),
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)
986 #endif
988 public Color BorderColor
992 return _borderColor;
996 _borderColor = value;
997 Invalidate();
1001 /// <summary>
1002 /// Gets or sets the border width of a ChartArea object.
1003 /// </summary>
1006 SRCategory("CategoryAttributeAppearance"),
1007 Bindable(true),
1008 DefaultValue(1),
1009 SRDescription("DescriptionAttributeBorderWidth"),
1010 NotifyParentPropertyAttribute(true),
1011 #if !Microsoft_CONTROL
1012 PersistenceMode(PersistenceMode.Attribute)
1013 #endif
1015 public int BorderWidth
1019 return _borderWidth;
1023 if(value < 0)
1025 throw (new ArgumentOutOfRangeException("value", SR.ExceptionBorderWidthIsNegative));
1027 _borderWidth = value;
1028 Invalidate();
1032 /// <summary>
1033 /// Gets or sets the style of the border line of a ChartArea object.
1034 /// </summary>
1037 SRCategory("CategoryAttributeAppearance"),
1038 Bindable(true),
1039 DefaultValue(ChartDashStyle.NotSet),
1040 SRDescription("DescriptionAttributeBorderDashStyle"),
1041 NotifyParentPropertyAttribute(true),
1042 #if !Microsoft_CONTROL
1043 PersistenceMode(PersistenceMode.Attribute)
1044 #endif
1046 public ChartDashStyle BorderDashStyle
1050 return _borderDashStyle;
1054 _borderDashStyle = value;
1055 Invalidate();
1059 /// <summary>
1060 /// Gets or sets the unique name of a ChartArea object.
1061 /// </summary>
1064 SRCategory("CategoryAttributeMisc"),
1065 Bindable(true),
1066 SRDescription("DescriptionAttributeChartArea_Name"),
1067 NotifyParentPropertyAttribute(true),
1068 #if !Microsoft_CONTROL
1069 PersistenceMode(PersistenceMode.Attribute)
1070 #endif
1072 public override string Name
1076 return base.Name;
1080 base.Name = value;
1084 /// <summary>
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.
1087 /// </summary>
1089 SRCategory("CategoryAttributeAppearance"),
1090 Bindable(true),
1091 DefaultValue(false),
1092 SRDescription("DescriptionAttributeChartArea_EquallySizedAxesFont"),
1093 NotifyParentPropertyAttribute(true),
1094 #if !Microsoft_CONTROL
1095 PersistenceMode(PersistenceMode.Attribute)
1096 #endif
1098 public bool IsSameFontSizeForAllAxes
1102 return _isSameFontSizeForAllAxes;
1106 _isSameFontSizeForAllAxes = value;
1107 Invalidate();
1112 #endregion
1114 #region Constructors
1115 /// <summary>
1116 /// ChartArea constructor.
1117 /// </summary>
1118 public ChartArea()
1120 Initialize();
1123 /// <summary>
1124 /// ChartArea constructor.
1125 /// </summary>
1126 /// <param name="name">The name.</param>
1127 public ChartArea(string name) : base(name)
1129 Initialize();
1131 #endregion
1133 #region Chart Area Methods
1134 /// <summary>
1135 /// Restores series order and X axis reversed mode for the 3D charts.
1136 /// </summary>
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;
1150 /// <summary>
1151 /// Sets series order and X axis reversed mode for the 3D charts.
1152 /// </summary>
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;
1171 // Check if Y angle
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
1191 else
1193 this.AxisY.IsReversed = !this.AxisY.IsReversed;
1194 this.AxisY2.IsReversed = !this.AxisY2.IsReversed;
1197 // Adjust Y angle
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;
1210 /// <summary>
1211 /// Save all automatic values like Minimum and Maximum.
1212 /// </summary>
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();
1238 /// <summary>
1239 /// Load all automatic values like Minimum and Maximum with original values.
1240 /// </summary>
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;
1265 /// <summary>
1266 /// Initialize Chart area and axes
1267 /// </summary>
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( );
1279 // Initialize axes;
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
1312 /// <summary>
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.
1318 /// </summary>
1319 internal void ResetMinMaxFromData()
1321 _axisArray[0].refreshMinMaxFromData = true;
1322 _axisArray[1].refreshMinMaxFromData = true;
1323 _axisArray[2].refreshMinMaxFromData = true;
1324 _axisArray[3].refreshMinMaxFromData = true;
1327 /// <summary>
1328 /// Recalculates the axes scale of a chart area.
1329 /// </summary>
1330 public void RecalculateAxesScale()
1332 // Read axis Max/Min from data
1333 ResetMinMaxFromData();
1335 #if Microsoft_CONTROL
1336 Set3DAnglesAndReverseMode();
1337 SetTempValues();
1338 #endif
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
1348 SetData();
1350 #if Microsoft_CONTROL
1351 Restore3DAnglesAndReverseMode();
1352 GetTempValues();
1353 #endif
1356 /// <summary>
1357 /// RecalculateAxesScale the chart area
1358 /// </summary>
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
1369 SetData();
1373 /// <summary>
1374 /// Reset auto calculated chart area values.
1375 /// </summary>
1376 internal void ResetAutoValues()
1378 _axisArray[0].ResetAutoValues();
1379 _axisArray[1].ResetAutoValues();
1380 _axisArray[2].ResetAutoValues();
1381 _axisArray[3].ResetAutoValues();
1384 /// <summary>
1385 /// Calculates Position for the background.
1386 /// </summary>
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
1393 // area position.
1394 RectangleF backgroundPosition = PlotAreaPosition.ToRectangleF();
1395 if( !requireAxes )
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
1411 if( requireAxes )
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;
1446 /// <summary>
1447 /// Call when the chart area is resized.
1448 /// </summary>
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)
1469 if(axis.enabled)
1471 if(axis.AxisPosition == AxisPosition.Bottom)
1473 ++horizontalAxes;
1475 else if(axis.AxisPosition == AxisPosition.Top)
1477 ++horizontalAxes;
1479 else if(axis.AxisPosition == AxisPosition.Left)
1481 ++verticalAxes;
1483 else if(axis.AxisPosition == AxisPosition.Right)
1485 ++verticalAxes;
1489 if(horizontalAxes <= 0 )
1491 horizontalAxes = 1;
1493 if(verticalAxes <= 0 )
1495 verticalAxes = 1;
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
1511 if(axis.enabled)
1513 // Resize axis
1514 if(axis.AxisPosition == AxisPosition.Bottom || axis.AxisPosition == AxisPosition.Top)
1516 axis.Resize(chartGraph, this.PlotAreaPosition, plottingRect, horizontalAxes, InnerPlotPosition.Auto);
1518 else
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;
1568 continue;
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);
1578 else
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;
1629 #if SUBAXES
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;
1638 #endif // SUBAXES
1640 // Adjust depending on the axis position
1641 axisSize -= axisPosition;
1642 if(axisSize < 0)
1644 axisSize = 0;
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);
1710 else
1712 rectLabelSideSpacing.Width = (float)Math.Max(plottingRect.Width * 0.3f, rectLabelSideSpacing.Width);
1717 else
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);
1738 else
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);
1784 if(xTitleSize > 0)
1786 plottingRect.X += xTitleSize;
1787 plottingRect.Width -= 2f * xTitleSize;
1789 float yTitleSize = (float)Math.Max(this.AxisX.titleSize, this.AxisX2.titleSize);
1790 if(yTitleSize > 0)
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;
1803 else
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);
1851 /// <summary>
1852 /// Finds axis by it's position. Can be Null.
1853 /// </summary>
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)
1862 return axis;
1865 return null;
1868 /// <summary>
1869 /// Shift top/bottom labels so they will not overlap with left/right labels.
1870 /// </summary>
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 )
1879 return;
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))
1895 return;
1898 // Check if labels may overlap with Left axis
1899 Axis leftAxis = FindAxis(AxisPosition.Left);
1900 if(leftAxis != null &&
1901 leftAxis.enabled &&
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))
1944 return;
1947 // Check if labels may overlap with Left axis
1948 Axis leftAxis = FindAxis(AxisPosition.Left);
1949 if(leftAxis != null &&
1950 leftAxis.enabled &&
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;
1982 #endregion
1984 #region Painting and Selection Methods
1986 /// <summary>
1987 /// Draws chart area background and/or border.
1988 /// </summary>
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)
1994 if(!borderOnly)
1996 // Draw background
1997 if(!this.Area3DStyle.Enable3D || !requireAxes || chartAreaIsCurcular)
1999 // 3D Pie Chart doesn't need scene
2000 // Draw 2D background
2001 graph.FillRectangleRel(
2002 position,
2003 BackColor,
2004 BackHatchStyle,
2005 BackImage,
2006 BackImageWrapMode,
2007 BackImageTransparentColor,
2008 BackImageAlignment,
2009 BackGradientStyle,
2010 BackSecondaryColor,
2011 (requireAxes) ? Color.Empty : BorderColor,
2012 (requireAxes) ? 0 : BorderWidth,
2013 BorderDashStyle,
2014 ShadowColor,
2015 ShadowOffset,
2016 PenAlignment.Outset,
2017 chartAreaIsCurcular,
2018 (chartAreaIsCurcular && this.CircularUsePolygons) ? this.CircularSectorsNumber : 0,
2019 this.Area3DStyle.Enable3D);
2021 else
2023 // Draw chart area 3D scene
2024 this.DrawArea3DScene(graph, position);
2027 else
2029 if(!this.Area3DStyle.Enable3D || !requireAxes || chartAreaIsCurcular)
2031 // Draw chart area border
2032 if(BorderColor != Color.Empty && BorderWidth > 0)
2034 graph.FillRectangleRel( position,
2035 Color.Transparent,
2036 ChartHatchStyle.None,
2037 "",
2038 ChartImageWrapMode.Tile,
2039 Color.Empty,
2040 ChartImageAlignmentStyle.Center,
2041 GradientStyle.None,
2042 Color.Empty,
2043 BorderColor,
2044 BorderWidth,
2045 BorderDashStyle,
2046 Color.Empty,
2048 PenAlignment.Outset,
2049 chartAreaIsCurcular,
2050 (chartAreaIsCurcular && this.CircularUsePolygons) ? this.CircularSectorsNumber : 0,
2051 this.Area3DStyle.Enable3D);
2058 /// <summary>
2059 /// Paint the chart area.
2060 /// </summary>
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
2070 && !Position.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);
2094 // Draw background
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);
2113 break;
2117 // Call Paint event
2118 Common.Chart.CallOnPostPaint(new ChartPaintEventArgs(this, graph, Common, PlotAreaPosition));
2119 return;
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;
2147 else
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);
2170 else
2172 foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
2174 scaleSegment.SetTempAxisScaleAndInterval();
2176 currentAxis.PaintStrips(graph, false);
2178 scaleSegment.RestoreAxisScaleAndInterval();
2183 // Draw Axis Grids
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);
2195 else
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);
2220 else
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);
2247 else
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())
2267 borderDrawn = true;
2268 PaintAreaBack(graph, backgroundPosition, true);
2271 // Draw chart types
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);
2292 else
2294 // Drawing in 3D space
2295 PaintChartSeries3D(graph);
2298 // Draw area border if it wasn't drawn prior to the series
2299 if (!borderDrawn)
2301 PaintAreaBack(graph, backgroundPosition, true);
2304 // Draw Axis
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);
2316 else
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);
2335 // Call Paint event
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;
2365 /// <summary>
2366 /// Checks if chart area border should be drawn on top of series.
2367 /// </summary>
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.
2372 bool result = true;
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)
2384 return false;
2388 return result;
2391 /// <summary>
2392 /// Paint the chart area cursors.
2393 /// </summary>
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)
2402 return;
2405 // Do not draw cursor/selection for chart types that do not require axis (like Pie)
2406 if(!this.requireAxes)
2408 return;
2411 // Cursors and selection are not supoorted in circular areas
2412 if(this.chartAreaIsCurcular)
2414 return;
2417 // Do not draw cursor/selection while printing
2418 if(this.Common != null &&
2419 this.Common.ChartPicture != null &&
2420 this.Common.ChartPicture.isPrinting)
2422 return;
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)
2430 return;
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 ||
2463 !cursorOnly)
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,
2499 chartPosition);
2503 // Draw chart area cursors and range selection
2505 _cursorY.Paint(graph);
2506 _cursorX.Paint(graph);
2509 #endif // Microsoft_CONTROL
2513 #endregion
2515 #region Circular chart area methods
2517 /// <summary>
2518 /// Gets a circular chart type interface that belongs to this chart area.
2519 /// </summary>
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;;
2529 if(type != null)
2531 return type;
2535 return null;
2538 /// <summary>
2539 /// Calculate size of the circular axis labels and sets auto-fit font.
2540 /// </summary>
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,
2550 float xTitleSize,
2551 float yTitleSize)
2553 // Check if axis labels are enabled
2554 if(!this.AxisX.LabelStyle.Enabled)
2556 return;
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,
2583 14,
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 //*****************************************************************
2600 // Get labels size
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;
2636 /// <summary>
2637 /// Calculate size of the circular axis labels.
2638 /// </summary>
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,
2648 SizeF titleSize)
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(
2688 labelsSize,
2689 textSize.Height);
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;
2704 if(width < 0)
2705 width = 0;
2706 if(height < 0)
2707 height = 0;
2710 labelsSize = (float)Math.Max(
2711 labelsSize,
2712 Math.Max(width, height));
2714 else if(labelsStyle == CircularAxisLabelsStyle.Horizontal)
2716 // Get text angle
2717 float textAngle = axis.AxisPosition;
2718 if(textAngle > 180f)
2720 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);
2729 // Calculate width
2730 float width = textSize.Width;
2731 width -= areaRectAbs.Right - labelPosition[0].X;
2732 if(width < 0f)
2734 width = 0f;
2737 labelsSize = (float)Math.Max(
2738 labelsSize,
2739 Math.Max(width, textSize.Height));
2743 return labelsSize;
2746 /// <summary>
2747 /// True if polygons should be used instead of the circles for the chart area.
2748 /// </summary>
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;
2774 else
2776 throw(new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid( series[CustomPropertyName.AreaDrawingStyle], "AreaDrawingStyle")));
2778 break;
2784 return (this._circularUsePolygons == 1);
2788 /// <summary>
2789 /// Gets circular area axis labels style.
2790 /// </summary>
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;
2818 else
2820 throw(new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid( styleName, "CircularLabelsStyle")));
2826 // Get auto style
2827 if(style == CircularAxisLabelsStyle.Auto)
2829 int sectorNumber = CircularSectorsNumber;
2830 style = CircularAxisLabelsStyle.Horizontal;
2831 if(sectorNumber > 30)
2833 style = CircularAxisLabelsStyle.Radial;
2837 return style;
2840 /// <summary>
2841 /// Number of sectors in the circular area.
2842 /// </summary>
2843 internal int CircularSectorsNumber
2847 // Check if value was precalculated
2848 if(this._circularSectorNumber == int.MinValue)
2850 this._circularSectorNumber = GetCircularSectorNumber();
2853 return this._circularSectorNumber;
2857 /// <summary>
2858 /// Gets number of sectors in the circular chart area.
2859 /// </summary>
2860 /// <returns>Number of sectors.</returns>
2861 private int GetCircularSectorNumber()
2863 ICircularChartType type = this.GetCircularChartType();
2864 if(type != null)
2866 return type.GetNumerOfSectors(this, this.Common.DataManager.Series);
2868 return 0;
2871 /// <summary>
2872 /// Fills a list of circular axis.
2873 /// </summary>
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;
2898 else
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;
2908 break;
2914 // Add axis into the list
2915 _circularAxisList.Add(axis);
2919 return _circularAxisList;
2922 /// <summary>
2923 /// Converts circular position of the X axis to angle in degrees.
2924 /// </summary>
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);
2935 #endregion
2937 #region 2D Series drawing order methods
2939 /// <summary>
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.
2944 ///
2945 /// This method is intended to fix issues #6443 and #5385 when area chart
2946 /// type incorrectly overlaps point or line chart type.
2947 /// </summary>
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);
2990 else
2992 // Check if curent chart type has more that 1 series and they are split
2993 // by other series
2994 bool chartTypeIsSplit = false;
2996 if (splitChartType.Contains(series.ChartTypeName))
2998 chartTypeIsSplit = true;
3000 else
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);
3013 else
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));
3029 else
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);
3039 ++seriesIndex;
3042 else
3044 foreach (string chartType in this.ChartTypes)
3046 resultList.Add(new ChartTypeAndSeriesInfo(chartType));
3050 return resultList;
3053 /// <summary>
3054 /// Internal data structure that stores chart type name and optionally series object.
3055 /// </summary>
3056 internal class ChartTypeAndSeriesInfo
3058 /// <summary>
3059 /// Object constructor.
3060 /// </summary>
3061 public ChartTypeAndSeriesInfo()
3065 /// <summary>
3066 /// Object constructor.
3067 /// </summary>
3068 /// <param name="chartType">Chart type name to initialize with.</param>
3069 public ChartTypeAndSeriesInfo(string chartType)
3071 this.ChartType = chartType;
3074 /// <summary>
3075 /// Object constructor.
3076 /// </summary>
3077 /// <param name="series">Series to initialize with.</param>
3078 public ChartTypeAndSeriesInfo(Series series)
3080 this.ChartType = series.ChartTypeName;
3081 this.Series = series;
3084 // Chart type name
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
3096 /// <summary>
3097 /// Releases unmanaged and - optionally - managed resources
3098 /// </summary>
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)
3106 if (disposing)
3108 // Dispose managed resources
3109 if (this._axisArray != null)
3111 foreach (Axis axis in this._axisArray)
3113 axis.Dispose();
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;
3148 #endif
3150 base.Dispose(disposing);
3154 #endregion