// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // Purpose: Base class for all anotation objects. Provides // basic set of properties and methods. // using System; using System.Windows.Forms; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Globalization; using FastReport.DataVisualization.Charting.Utilities; namespace FastReport.DataVisualization.Charting { #region Enumerations /// /// Annotation object selection points style. /// /// /// Enumeration is for internal use only and should not be part of the documentation. /// internal enum SelectionPointsStyle { /// /// Selection points are displayed top left and bottom right corners /// TwoPoints, /// /// Selection points are displayed on all sides and corners of the rectangle. /// Rectangle, } /// /// Annotation object resizing\moving mode. /// /// /// Enumeration is for internal use only and should not be part of the documentation. /// internal enum ResizingMode { /// /// Top Left selection handle is used. /// TopLeftHandle = 0, /// /// Top selection handle is used. /// TopHandle = 1, /// /// Top Right selection handle is used. /// TopRightHandle = 2, /// /// Right selection handle is used. /// RightHandle = 3, /// /// Bottom Right selection handle is used. /// BottomRightHandle = 4, /// /// Bottom selection handle is used. /// BottomHandle = 5, /// /// Bottom Left selection handle is used. /// BottomLeftHandle = 6, /// /// Left selection handle is used. /// LeftHandle = 7, /// /// Anchor selection handle is used. /// AnchorHandle = 8, /// /// No selection handles used - moving mode. /// Moving = 16, /// /// Moving points of the annotation path. /// MovingPathPoints = 32, /// /// No moving or resizing. /// None = 64, } #endregion /// /// Annotation is an abstract class that defines properties and methods /// common to all annotations. /// /// /// All annotations are derived from the Annotation class, which can be /// used to set properties common to all annotation objects (e.g. color, position, /// anchoring and others). /// [ SRDescription("DescriptionAttributeAnnotation_Annotation"), DefaultProperty("Name"), ] public abstract class Annotation : ChartNamedElement { #region Fields // Name of the chart area the annotation is clipped to private string _clipToChartArea = Constants.NotSetValue; // Indicates that annotation is selected private bool _isSelected = false; // Indicates that annotation size is defined in relative chart coordinates private bool _isSizeAlwaysRelative = true; // Position attribute fields private double _x = double.NaN; private double _y = double.NaN; private double _width = double.NaN; private double _height = double.NaN; // Annotation axes attaching fields private string _axisXName = String.Empty; private string _axisYName = String.Empty; private Axis _axisX = null; private Axis _axisY = null; // Visual attribute fields private bool _visible = true; private ContentAlignment _alignment = ContentAlignment.MiddleCenter; private Color _foreColor = Color.Black; private FontCache _fontCache = new FontCache(); private Font _textFont; private TextStyle _textStyle = TextStyle.Default; internal Color lineColor = Color.Black; private int _lineWidth = 1; private ChartDashStyle _lineDashStyle = ChartDashStyle.Solid; private Color _backColor = Color.Empty; private ChartHatchStyle _backHatchStyle = ChartHatchStyle.None; private GradientStyle _backGradientStyle = GradientStyle.None; private Color _backSecondaryColor = Color.Empty; private Color _shadowColor = Color.FromArgb(128, 0, 0, 0); private int _shadowOffset = 0; // Anchor position attribute fields private string _anchorDataPointName = String.Empty; private DataPoint _anchorDataPoint = null; private DataPoint _anchorDataPoint2 = null; private double _anchorX = double.NaN; private double _anchorY = double.NaN; internal double anchorOffsetX = 0.0; internal double anchorOffsetY = 0.0; internal ContentAlignment anchorAlignment = ContentAlignment.BottomCenter; // Selection handles position (starting top-left and moving clockwise) internal RectangleF[] selectionRects = null; // Annotation tooltip private string _tooltip = String.Empty; // Selection handles size internal const int selectionMarkerSize = 6; // Pre calculated relative position of annotation and anchor point internal RectangleF currentPositionRel = new RectangleF(float.NaN, float.NaN, float.NaN, float.NaN); internal PointF currentAnchorLocationRel = new PointF(float.NaN, float.NaN); // Smart labels style private AnnotationSmartLabelStyle _smartLabelStyle = null; // Index of last selected point in the annotation path internal int currentPathPointIndex = -1; // Group this annotation belongs too internal AnnotationGroup annotationGroup = null; // Selection and editing permissions private bool _allowSelecting = false; private bool _allowMoving = false; private bool _allowAnchorMoving = false; private bool _allowResizing = false; private bool _allowTextEditing = false; private bool _allowPathEditing = false; // Indicates that annotation position was changed. Flag used to fire events. internal bool positionChanged = false; // Relative location of last placement position internal PointF lastPlacementPosition = PointF.Empty; // Relative location of annotation anchor, when it's started to move internal PointF startMoveAnchorLocationRel = PointF.Empty; // Relative position of annotation, when it's started to move/resize internal RectangleF startMovePositionRel = RectangleF.Empty; // Relative position of annotation, when it's started to move/resize internal GraphicsPath startMovePathRel = null; /// /// Limit of annotation width and height. /// internal static double WidthHightLimit = 290000000; #endregion #region Constructors /// /// Initializes a new instance of the class. /// protected Annotation() { _textFont = _fontCache.DefaultFont; } #endregion #region Properties #region Miscellaneous /// /// Gets or sets an annotation's unique name. /// /// /// A string that represents an annotation's unique name. /// [ SRCategory("CategoryAttributeMisc"), Bindable(true), SRDescription("DescriptionAttributeName4"), ParenthesizePropertyNameAttribute(true), ] public override string Name { get { return base.Name; } set { base.Name = value; CallOnModifing(); } } /// /// Gets or sets an annotation's type name. /// /// /// This property is used to get the name of each annotation Style /// (e.g. Line, Rectangle, Ellipse). /// /// This property is for internal use and is hidden at design and run time. /// /// [ SRCategory("CategoryAttributeMisc"), Bindable(true), Browsable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), SRDescription("DescriptionAttributeAnnotation_AnnotationType"), ] public abstract string AnnotationType { get; } /// /// Gets or sets the name of the chart area which an annotation is clipped to. /// /// /// A string which represents the name of an existing chart area. /// /// /// If the chart area name is specified, an annotation will only be drawn inside the /// plotting area of the chart area specified. All parts of the annotation /// outside of the plotting area will be clipped. /// /// To disable chart area clipping, set the property to "NotSet" or an empty string. /// /// [ SRCategory("CategoryAttributeMisc"), DefaultValue(Constants.NotSetValue), SRDescription("DescriptionAttributeAnnotationClipToChartArea"), TypeConverter(typeof(LegendAreaNameConverter)) ] virtual public string ClipToChartArea { get { return _clipToChartArea; } set { if (value != _clipToChartArea) { if (String.IsNullOrEmpty(value)) { _clipToChartArea = Constants.NotSetValue; } else { if (Chart != null && Chart.ChartAreas != null) { Chart.ChartAreas.VerifyNameReference(value); } _clipToChartArea = value; } this.Invalidate(); CallOnModifing(); } } } /// /// Gets or sets the smart labels style of an annotation. /// /// /// An object that represents an annotation's /// smart labels style properties. /// /// /// Smart labels are used to prevent an annotation from overlapping data point labels /// and other annotations. /// /// Note that data point labels must also have smart labels enabled. /// /// [ Browsable(true), SRCategory("CategoryAttributeMisc"), Bindable(true), SRDescription("DescriptionAttributeSmartLabels"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), ] public AnnotationSmartLabelStyle SmartLabelStyle { get { if (this._smartLabelStyle == null) { this._smartLabelStyle = new AnnotationSmartLabelStyle(this); } return _smartLabelStyle; } set { value.chartElement = this; _smartLabelStyle = value; this.Invalidate(); CallOnModifing(); } } /// /// Gets the group, if any, the annotation belongs to. /// [ Browsable(false), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), ] public AnnotationGroup AnnotationGroup { get { return this.annotationGroup; } } #endregion #region Position /// /// Gets or sets a flag that specifies whether the size of an annotation is always /// defined in relative chart coordinates. /// /// /// /// /// True if an annotation's and are always /// in chart relative coordinates, false otherwise. /// /// /// An annotation's width and height may be set in relative chart or axes coordinates. /// By default, relative chart coordinates are used. /// /// To use axes coordinates for size set the IsSizeAlwaysRelative property to /// false and either anchor the annotation to a data point or set the /// or properties. /// /// [ SRCategory("CategoryAttributePosition"), DefaultValue(true), SRDescription("DescriptionAttributeSizeAlwaysRelative"), ] virtual public bool IsSizeAlwaysRelative { get { return _isSizeAlwaysRelative; } set { _isSizeAlwaysRelative = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the X coordinate of an annotation. /// /// /// /// /// A Double value that represents the X coordinate of an annotation. /// /// /// The X coordinate of an annotation is in relative chart coordinates or axes coordinates. Chart /// relative coordinates are used by default. /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties. /// /// /// Set the X position to Double.NaN ("NotSet") to achieve automatic position calculation /// when the annotation is anchored using the property or /// the and properties. /// /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnnotationBaseX"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X")] virtual public double X { get { return _x; } set { _x = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the Y coordinate of an annotation. /// /// /// /// /// A Double value that represents the Y coordinate of an annotation. /// /// /// The Y coordinate of an annotation is in relative chart coordinates or axes coordinates. Chart /// relative coordinates are used by default. /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties. /// /// /// Set the Y position to Double.NaN ("NotSet") to achieve automatic position calculation /// when the annotation is anchored using the property or /// the and properties. /// /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnnotationBaseY"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y")] virtual public double Y { get { return _y; } set { _y = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation's width. /// /// /// /// /// A Double value that represents an annotation's width. /// /// /// An annotation's width can be a negative value, in which case the annotation orientation /// is switched. /// /// Annotation width can be in relative chart or axes coordinates. Chart /// relative coordinates are used by default. /// /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties /// and set the property to false. /// /// /// Set the width to Double.NaN ("NotSet") to achieve automatic size calculation for /// annotations with text. The size will automatically be calculated based on /// the annotation text and font size. /// /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnnotationWidth"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double Width { get { return _width; } set { if (value < -WidthHightLimit || value > WidthHightLimit) { throw new ArgumentException(SR.ExceptionValueMustBeInRange("Width", (-WidthHightLimit).ToString(CultureInfo.CurrentCulture), WidthHightLimit.ToString(CultureInfo.CurrentCulture))); } _width = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation's height. /// /// /// /// /// A Double value that represents an annotation's height. /// /// /// An annotation's height can be a negative value, in which case the annotation orientation /// is switched. /// /// Annotation height can be in relative chart or axes coordinates. Chart /// relative coordinates are used by default. /// /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties /// and set the property to false. /// /// /// Set the height to Double.NaN ("NotSet") to achieve automatic size calculation for /// annotations with text. The size will automatically be calculated based on /// the annotation text and font size. /// /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnnotationHeight"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double Height { get { return _height; } set { if (value < -WidthHightLimit || value > WidthHightLimit) { throw new ArgumentException(SR.ExceptionValueMustBeInRange("Height", (-WidthHightLimit).ToString(CultureInfo.CurrentCulture), WidthHightLimit.ToString(CultureInfo.CurrentCulture))); } _height = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation position's right boundary. /// /// /// /// /// A Double value that represents the position of an annotation's right boundary. /// /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties /// and set the property to false. /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeRight3"), RefreshPropertiesAttribute(RefreshProperties.All), Browsable(false), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double Right { get { return _x + _width; } set { _width = value - _x; this.ResetCurrentRelativePosition(); Invalidate(); } } /// /// Gets or sets an annotation position's bottom boundary. /// /// /// /// /// A Double value that represents the position of an annotation's bottom boundary. /// /// /// To use axes coordinates, anchor /// an annotation to a data point using the property, or /// set the annotation axes using the or properties /// and set the property to false. /// [ SRCategory("CategoryAttributePosition"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeBottom"), RefreshPropertiesAttribute(RefreshProperties.All), Browsable(false), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double Bottom { get { return _y + _height; } set { _height = value - _y; this.ResetCurrentRelativePosition(); Invalidate(); } } #endregion #region Visual Attributes /// /// Gets or sets a flag that determines if an annotation is selected. /// /// /// /// True if the annotation is selected, false otherwise. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(false), Browsable(false), SRDescription("DescriptionAttributeSelected"), ] virtual public bool IsSelected { get { return _isSelected; } set { _isSelected = value; Invalidate(); } } /// /// Gets or sets an annotation selection points style. /// /// /// A value that represents annotation /// selection style. /// /// /// This property is for internal use and is hidden at design and run time. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(SelectionPointsStyle.Rectangle), ParenthesizePropertyNameAttribute(true), Browsable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), SRDescription("DescriptionAttributeSelectionPointsStyle"), ] virtual internal SelectionPointsStyle SelectionPointsStyle { get { return SelectionPointsStyle.Rectangle; } } /// /// Gets or sets a flag that specifies whether an annotation is visible. /// /// /// True if the annotation is visible, false otherwise. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(true), SRDescription("DescriptionAttributeVisible"), ParenthesizePropertyNameAttribute(true), ] virtual public bool Visible { get { return _visible; } set { _visible = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation's content alignment. /// /// /// A value that represents the content alignment. /// /// /// This property is used to align text for , , /// and objects, and to align /// a non-scaled image inside an object. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(ContentAlignment), "MiddleCenter"), SRDescription("DescriptionAttributeAlignment"), ] virtual public ContentAlignment Alignment { get { return _alignment; } set { _alignment = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the text color of an annotation. /// /// /// /// A value used for the text color of an annotation. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Color), "Black"), SRDescription("DescriptionAttributeForeColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] virtual public Color ForeColor { get { return _foreColor; } set { _foreColor = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the font of an annotation's text. /// /// /// /// A object used for an annotation's text. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Font), "Microsoft Sans Serif, 8pt"), SRDescription("DescriptionAttributeTextFont"), ] virtual public Font Font { get { return _textFont; } set { _textFont = value; this.Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation's text style. /// /// /// /// /// A value used to draw an annotation's text. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(TextStyle), "Default"), SRDescription("DescriptionAttributeTextStyle"), ] virtual public TextStyle TextStyle { get { return _textStyle; } set { _textStyle = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the color of an annotation line. /// /// /// /// /// A value used to draw an annotation line. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Color), "Black"), SRDescription("DescriptionAttributeLineColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] virtual public Color LineColor { get { return lineColor; } set { lineColor = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the width of an annotation line. /// /// /// /// /// An integer value defining the width of an annotation line in pixels. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(1), SRDescription("DescriptionAttributeLineWidth"), ] virtual public int LineWidth { get { return _lineWidth; } set { if (value < 0) { throw (new ArgumentOutOfRangeException("value", SR.ExceptionAnnotationLineWidthIsNegative)); } _lineWidth = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the style of an annotation line. /// /// /// /// /// A value used to draw an annotation line. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(ChartDashStyle.Solid), SRDescription("DescriptionAttributeLineDashStyle"), ] virtual public ChartDashStyle LineDashStyle { get { return _lineDashStyle; } set { _lineDashStyle = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the background color of an annotation. /// /// /// /// /// /// A value used for the background of an annotation. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Color), ""), SRDescription("DescriptionAttributeBackColor"), NotifyParentPropertyAttribute(true), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] virtual public Color BackColor { get { return _backColor; } set { _backColor = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the background hatch style of an annotation. /// /// /// /// /// /// A value used for the background of an annotation. /// /// /// Two colors are used to draw the hatching, and . /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(ChartHatchStyle.None), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeBackHatchStyle"), #if DESIGNER Editor(typeof(HatchStyleEditor), typeof(UITypeEditor)) #endif ] virtual public ChartHatchStyle BackHatchStyle { get { return _backHatchStyle; } set { _backHatchStyle = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the background gradient style of an annotation. /// /// /// /// /// /// A value used for the background of an annotation. /// /// /// Two colors are used to draw the gradient, and . /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(GradientStyle.None), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeBackGradientStyle"), #if DESIGNER Editor(typeof(GradientEditor), typeof(UITypeEditor)) #endif ] virtual public GradientStyle BackGradientStyle { get { return _backGradientStyle; } set { _backGradientStyle = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the secondary background color of an annotation. /// /// /// /// /// /// A value used for the secondary color of an annotation background with /// hatching or gradient fill. /// /// /// This color is used with when or /// are used. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Color), ""), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeBackSecondaryColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] virtual public Color BackSecondaryColor { get { return _backSecondaryColor; } set { _backSecondaryColor = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the color of an annotation's shadow. /// /// /// /// A value used to draw an annotation's shadow. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(typeof(Color), "128,0,0,0"), SRDescription("DescriptionAttributeShadowColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] virtual public Color ShadowColor { get { return _shadowColor; } set { _shadowColor = value; Invalidate(); CallOnModifing(); } } /// /// Gets or sets the offset between an annotation and its shadow. /// /// /// /// An integer value that represents the offset between an annotation and its shadow. /// [ SRCategory("CategoryAttributeAppearance"), DefaultValue(0), SRDescription("DescriptionAttributeShadowOffset"), ] virtual public int ShadowOffset { get { return _shadowOffset; } set { _shadowOffset = value; Invalidate(); CallOnModifing(); } } #endregion #region Axes Attaching /// /// Gets or sets the name of the X axis which an annotation is attached to. /// /// /// A string value that represents the name of the X axis which an annotation /// is attached to. /// /// /// This property is for internal use and is hidden at design and run time. /// [ SRCategory("CategoryAttributeAnchorAxes"), DefaultValue(""), Browsable(false), Bindable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), SRDescription("DescriptionAttributeAxisXName"), ] virtual public string AxisXName { get { if (_axisXName.Length == 0 && _axisX != null) { _axisXName = GetAxisName(_axisX); } return _axisXName; } set { _axisXName = value; _axisX = null; this.ResetCurrentRelativePosition(); Invalidate(); } } /// /// Gets or sets the name of the Y axis which an annotation is attached to. /// /// /// A string value that represents the name of the Y axis which an annotation /// is attached to. /// /// /// This property is for internal use and is hidden at design and run time. /// [ SRCategory("CategoryAttributeAnchorAxes"), Browsable(false), Bindable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DefaultValue(""), SRDescription("DescriptionAttributeAxisYName"), SerializationVisibilityAttribute(SerializationVisibility.Hidden), ] virtual public string AxisYName { get { // Always return empty string to prevent property serialization // "YAxisName" property will be used instead. return string.Empty; } set { this.YAxisName = value; } } /// /// Gets or sets the name of the Y axis which an annotation is attached to. /// NOTE: "AxisYName" property was used before but the name was changed to solve the /// duplicated hash value during the serialization with the "TitleSeparator" property. /// /// /// A string value that represents the name of the Y axis which an annotation /// is attached to. /// /// /// This property is for internal use and is hidden at design and run time. /// [ SRCategory("CategoryAttributeAnchorAxes"), Browsable(false), Bindable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DefaultValue(""), SRDescription("DescriptionAttributeAxisYName"), ] virtual public string YAxisName { get { if (_axisYName.Length == 0 && _axisY != null) { _axisYName = GetAxisName(_axisY); } return _axisYName; } set { _axisYName = value; _axisY = null; this.ResetCurrentRelativePosition(); Invalidate(); } } /// /// Gets or sets the X axis which an annotation is attached to. /// /// /// /// /// object which an annotation is attached to. /// /// /// When an annotation is attached to an axis, its X position is always in /// axis coordinates. To define an annotation's size in axis coordinates as well, /// make sure the property is set to false. /// /// Set this value to null or nothing to disable attachment to the axis. /// /// [ SRCategory("CategoryAttributeAnchorAxes"), DefaultValue(null), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), SRDescription("DescriptionAttributeAxisX"), #if DESIGNER Editor(typeof(AnnotationAxisUITypeEditor), typeof(UITypeEditor)), #endif TypeConverter(typeof(AnnotationAxisValueConverter)), ] virtual public Axis AxisX { get { if (_axisX == null && _axisXName.Length > 0) { _axisX = GetAxisByName(_axisXName); } return _axisX; } set { _axisX = value; _axisXName = String.Empty; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the Y axis which an annotation is attached to. /// /// /// /// /// object which an annotation is attached to. /// /// /// When an annotation is attached to an axis, its Y position is always in /// axis coordinates. To define an annotation's size in axis coordinates as well, /// make sure property is set to false. /// /// Set this value to null or nothing to disable annotation attachment to an axis. /// /// [ SRCategory("CategoryAttributeAnchorAxes"), DefaultValue(null), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), SRDescription("DescriptionAttributeAxisY"), #if DESIGNER Editor(typeof(AnnotationAxisUITypeEditor), typeof(UITypeEditor)), #endif TypeConverter(typeof(AnnotationAxisValueConverter)), ] virtual public Axis AxisY { get { if (_axisY == null && _axisYName.Length > 0) { _axisY = GetAxisByName(_axisYName); } return _axisY; } set { _axisY = value; _axisYName = String.Empty; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } #endregion #region Anchor /// /// Gets or sets the name of a data point which an annotation is anchored to. /// /// /// A string value that represents the name of the data point which an /// annotation is anchored to. /// /// /// This property is for internal use and is hidden at design and run time. /// [ SRCategory("CategoryAttributeAnchor"), Browsable(false), Bindable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DefaultValue(""), SRDescription("DescriptionAttributeAnchorDataPointName"), ] virtual public string AnchorDataPointName { get { if (_anchorDataPointName.Length == 0 && _anchorDataPoint != null) { _anchorDataPointName = GetDataPointName(_anchorDataPoint); } return _anchorDataPointName; } set { _anchorDataPointName = value; _anchorDataPoint = null; this.ResetCurrentRelativePosition(); Invalidate(); } } /// /// Gets or sets the data point an annotation is anchored to. /// /// /// /// /// /// /// /// /// /// A object an annotation is anchored to. /// /// /// The annotation is anchored to the X and Y values of the specified data point, /// and automatically uses the same axes coordinates as the data point. /// /// To automatically position an annotation relative to an anchor point, make sure /// its and properties are set to Double.NaN. /// The property may be used to change an annotation's /// automatic position alignment to an anchor point. The and /// properties may be used to add extra spacing. /// /// /// When using this property, make sure the and /// properties are set to Double.NaN (they have precedence). /// /// /// Set this value to null or nothing to disable annotation anchoring to a data point. /// /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(null), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), SRDescription("DescriptionAttributeAnchorDataPoint"), #if DESIGNER Editor(typeof(AnchorPointUITypeEditor), typeof(UITypeEditor)), #endif TypeConverter(typeof(AnchorPointValueConverter)), ] virtual public DataPoint AnchorDataPoint { get { if (_anchorDataPoint == null && _anchorDataPointName.Length > 0) { _anchorDataPoint = GetDataPointByName(_anchorDataPointName); } return _anchorDataPoint; } set { _anchorDataPoint = value; _anchorDataPointName = String.Empty; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the X coordinate the annotation is anchored to. /// /// /// /// /// /// /// /// A double value that represents the X coordinate which an annotation is anchored to. /// /// /// The annotation is anchored to the X coordinate specified in relative or axis coordinates, /// depending on the property value. /// /// To automatically position an annotation relative to an anchor point, make sure /// its property is set to Double.NaN. /// The property may be used to change the annotation's /// automatic position alignment to the anchor point. The and /// properties may be used to add extra spacing. /// /// /// This property has a higher priority than the property. /// /// /// Set this value to Double.NaN to disable annotation anchoring to the value. /// /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnchorX"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double AnchorX { get { return _anchorX; } set { _anchorX = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the Y coordinate which an annotation is anchored to. /// /// /// /// /// /// /// /// A double value that represents the Y coordinate which an annotation is anchored to. /// /// /// The annotation is anchored to the Y coordinate specified in relative or axis coordinates, /// depending on the property value. /// /// To automatically position an annotation relative to an anchor point, make sure /// its property is set to Double.NaN. /// The property may be used to change the annotation's /// automatic position alignment to the anchor point. The and /// properties may be used to add extra spacing. /// /// /// This property has a higher priority than the property. /// /// /// Set this value to Double.NaN to disable annotation anchoring to the value. /// /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(double.NaN), SRDescription("DescriptionAttributeAnchorY"), RefreshPropertiesAttribute(RefreshProperties.All), TypeConverter(typeof(DoubleNanValueConverter)), ] virtual public double AnchorY { get { return _anchorY; } set { _anchorY = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the x-coordinate offset between the positions of an annotation and its anchor point. /// /// /// /// /// /// /// A double value that represents the x-coordinate offset between the positions of an annotation and its anchor point. /// /// /// The annotation must be anchored using the or /// properties, and its property must be set /// to Double.NaN. /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(0.0), SRDescription("DescriptionAttributeAnchorOffsetX3"), RefreshPropertiesAttribute(RefreshProperties.All), ] virtual public double AnchorOffsetX { get { return anchorOffsetX; } set { if (value > 100.0 || value < -100.0) { throw (new ArgumentOutOfRangeException("value", SR.ExceptionAnnotationAnchorOffsetInvalid)); } anchorOffsetX = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets the y-coordinate offset between the positions of an annotation and its anchor point. /// /// /// /// /// /// /// A double value that represents the y-coordinate offset between the positions of an annotation and its anchor point. /// /// /// Annotation must be anchored using or /// properties and it's property must be set /// to Double.NaN. /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(0.0), SRDescription("DescriptionAttributeAnchorOffsetY3"), RefreshPropertiesAttribute(RefreshProperties.All), ] virtual public double AnchorOffsetY { get { return anchorOffsetY; } set { if (value > 100.0 || value < -100.0) { throw (new ArgumentOutOfRangeException("value", SR.ExceptionAnnotationAnchorOffsetInvalid)); } anchorOffsetY = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } /// /// Gets or sets an annotation position's alignment to the anchor point. /// /// /// /// /// /// /// /// A value that represents the annotation's alignment to /// the anchor point. /// /// /// The annotation must be anchored using either , or the /// and properties. Its and /// properties must be set to Double.NaN. /// [ SRCategory("CategoryAttributeAnchor"), DefaultValue(typeof(ContentAlignment), "BottomCenter"), SRDescription("DescriptionAttributeAnchorAlignment"), ] virtual public ContentAlignment AnchorAlignment { get { return anchorAlignment; } set { anchorAlignment = value; this.ResetCurrentRelativePosition(); Invalidate(); CallOnModifing(); } } #endregion // Anchoring #region Editing Permissions /// /// Gets or sets a flag that specifies whether an annotation may be selected /// with a mouse by the end user. /// /// /// True if the annotation may be selected, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowSelecting"), ] virtual public bool AllowSelecting { get { return _allowSelecting; } set { _allowSelecting = value; CallOnModifing(); } } /// /// Gets or sets a flag that specifies whether an annotation may be moved /// with a mouse by the end user. /// /// /// True if the annotation may be moved, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowMoving"), ] virtual public bool AllowMoving { get { return _allowMoving; } set { _allowMoving = value; CallOnModifing(); } } /// /// Gets or sets a flag that specifies whether an annotation anchor may be moved /// with a mouse by the end user. /// /// /// True if the annotation anchor may be moved, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowAnchorMoving3"), ] virtual public bool AllowAnchorMoving { get { return _allowAnchorMoving; } set { _allowAnchorMoving = value; CallOnModifing(); } } /// /// Gets or sets a flag that specifies whether an annotation may be resized /// with a mouse by the end user. /// /// /// True if the annotation may be resized, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowResizing"), ] virtual public bool AllowResizing { get { return _allowResizing; } set { _allowResizing = value; CallOnModifing(); } } /// /// Gets or sets a flag that specifies whether an annotation's text may be edited /// when the end user double clicks on the text. /// /// /// True if the annotation text may be edited, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowTextEditing"), ] virtual public bool AllowTextEditing { get { return _allowTextEditing; } set { _allowTextEditing = value; CallOnModifing(); } } /// /// Gets or sets a flag that specifies whether a polygon annotation's points /// may be moved with a mouse by the end user. /// /// /// True if the polygon annotation's points may be moved, false otherwise. /// [ SRCategory("CategoryAttributeEditing"), DefaultValue(false), SRDescription("DescriptionAttributeAllowPathEditing3"), ] virtual public bool AllowPathEditing { get { return _allowPathEditing; } set { _allowPathEditing = value; CallOnModifing(); } } #endregion #region Interactivity /// /// Gets or sets an annotation's tooltip text. /// /// /// A string value. /// /// /// Special keywords can be used in the text when an annotation is anchored to /// a data point using the property. For a listing of /// these keywords, refer to the "Annotations" help topic. /// [ SRCategory("CategoryAttributeMisc"), DefaultValue(""), SRDescription("DescriptionAttributeToolTip"), ] virtual public string ToolTip { get { return _tooltip; } set { _tooltip = value; CallOnModifing(); } } #endregion // Interactivity #endregion #region Methods #region Painting /// /// Paints the annotation object using the specified graphics. /// /// /// A object used to paint the annotation object. /// /// /// Reference to the annotation's control owner. /// abstract internal void Paint(Chart chart, ChartGraphics graphics); /// /// Paints annotation selection markers. /// /// Chart graphics used for painting. /// Selection rectangle. /// Selection path. virtual internal void PaintSelectionHandles(ChartGraphics chartGraphics, RectangleF rect, GraphicsPath path) { // Define markers appearance style Color markerBorderColor = Color.Black; Color markerColor = Color.FromArgb(200, 255, 255, 255); MarkerStyle markerStyle = MarkerStyle.Square; int markerSize = selectionMarkerSize; Boolean selected = this.IsSelected; SizeF markerSizeRel = chartGraphics.GetRelativeSize(new SizeF(markerSize, markerSize)); if (this.Common.ProcessModePaint && !this.Common.ChartPicture.isPrinting) { // Clear selection rectangles this.selectionRects = null; // Check if annotation is selected if (selected) { // Create selection rectangles this.selectionRects = new RectangleF[9]; // Draw selection handles for single dimension annotations like line. if (this.SelectionPointsStyle == SelectionPointsStyle.TwoPoints) { // Save selection handles position in array elements 0 and 4 this.selectionRects[(int)ResizingMode.TopLeftHandle] = new RectangleF( rect.X - markerSizeRel.Width / 2f, rect.Y - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); this.selectionRects[(int)ResizingMode.BottomRightHandle] = new RectangleF( rect.Right - markerSizeRel.Width / 2f, rect.Bottom - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); // Draw selection handle chartGraphics.DrawMarkerRel( rect.Location, markerStyle, markerSize, markerColor, markerBorderColor, 1, "", Color.Empty, 0, Color.FromArgb(128, 0, 0, 0), RectangleF.Empty); chartGraphics.DrawMarkerRel( new PointF(rect.Right, rect.Bottom), markerStyle, markerSize, markerColor, markerBorderColor, 1, "", Color.Empty, 0, Color.FromArgb(128, 0, 0, 0), RectangleF.Empty); } else if (this.SelectionPointsStyle == SelectionPointsStyle.Rectangle) { for (int index = 0; index < 8; index++) { // Get handle position PointF handlePosition = PointF.Empty; switch ((ResizingMode)index) { case ResizingMode.TopLeftHandle: handlePosition = rect.Location; break; case ResizingMode.TopHandle: handlePosition = new PointF(rect.X + rect.Width / 2f, rect.Y); break; case ResizingMode.TopRightHandle: handlePosition = new PointF(rect.Right, rect.Y); break; case ResizingMode.RightHandle: handlePosition = new PointF(rect.Right, rect.Y + rect.Height / 2f); break; case ResizingMode.BottomRightHandle: handlePosition = new PointF(rect.Right, rect.Bottom); break; case ResizingMode.BottomHandle: handlePosition = new PointF(rect.X + rect.Width / 2f, rect.Bottom); break; case ResizingMode.BottomLeftHandle: handlePosition = new PointF(rect.X, rect.Bottom); break; case ResizingMode.LeftHandle: handlePosition = new PointF(rect.X, rect.Y + rect.Height / 2f); break; } // Save selection handles position in array elements 0 and 4 this.selectionRects[index] = new RectangleF( handlePosition.X - markerSizeRel.Width / 2f, handlePosition.Y - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); // Draw selection handle chartGraphics.DrawMarkerRel( handlePosition, markerStyle, markerSize, markerColor, markerBorderColor, 1, "", Color.Empty, 0, Color.FromArgb(128, 0, 0, 0), RectangleF.Empty); } } //******************************************************************** //** Draw anchor selection handle //******************************************************************** // Get vertical and horizontal axis Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); // Get anchor position double anchorX = double.NaN; double anchorY = double.NaN; bool relativeX = false; bool relativeY = false; this.GetAnchorLocation(ref anchorX, ref anchorY, ref relativeX, ref relativeY); // Convert anchor location to relative coordinates if (!double.IsNaN(anchorX) && !double.IsNaN(anchorY)) { if (!relativeX && horizAxis != null) { anchorX = horizAxis.ValueToPosition(anchorX); } if (!relativeY && vertAxis != null) { anchorY = vertAxis.ValueToPosition(anchorY); } // Apply 3D transforamtion if required ChartArea chartArea = null; if (horizAxis != null && horizAxis.ChartArea != null) { chartArea = horizAxis.ChartArea; } if (vertAxis != null && vertAxis.ChartArea != null) { chartArea = vertAxis.ChartArea; } if (chartArea != null && chartArea.Area3DStyle.Enable3D == true && !chartArea.chartAreaIsCurcular && chartArea.requireAxes && chartArea.matrix3D.IsInitialized()) { // Get anotation Z coordinate (use scene depth or anchored point Z position) float positionZ = chartArea.areaSceneDepth; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null) { float depth = 0f; chartArea.GetSeriesZPositionAndDepth( this.AnchorDataPoint.series, out depth, out positionZ); positionZ += depth / 2f; } // Define 3D points of annotation object Point3D[] annot3DPoints = new Point3D[1]; annot3DPoints[0] = new Point3D((float)anchorX, (float)anchorY, positionZ); // Tranform cube coordinates chartArea.matrix3D.TransformPoints(annot3DPoints); // Get transformed coordinates anchorX = annot3DPoints[0].X; anchorY = annot3DPoints[0].Y; } // Save selection handles position in array elements 0 and 4 this.selectionRects[(int)ResizingMode.AnchorHandle] = new RectangleF( (float)anchorX - markerSizeRel.Width / 2f, (float)anchorY - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); // Draw circular selection handle chartGraphics.DrawMarkerRel( new PointF((float)anchorX, (float)anchorY), MarkerStyle.Cross, selectionMarkerSize + 3, markerColor, markerBorderColor, 1, "", Color.Empty, 0, Color.FromArgb(128, 0, 0, 0), RectangleF.Empty); } //******************************************************************** //** Draw path selection handles //******************************************************************** if (path != null && AllowPathEditing) { // Create selection rectangles for each point PointF[] pathPoints = path.PathPoints; RectangleF[] newSelectionRects = new RectangleF[pathPoints.Length + 9]; // Copy previous rectangles (first nine elements) for (int index = 0; index < this.selectionRects.Length; index++) { newSelectionRects[index] = this.selectionRects[index]; } this.selectionRects = newSelectionRects; // Loop through all points for (int index = 0; index < pathPoints.Length; index++) { // Get handle position PointF handlePosition = chartGraphics.GetRelativePoint(pathPoints[index]); // Save selection handles position in array elements 0 and 4 this.selectionRects[9 + index] = new RectangleF( handlePosition.X - markerSizeRel.Width / 2f, handlePosition.Y - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); // Draw selection handle chartGraphics.DrawMarkerRel( handlePosition, MarkerStyle.Circle, selectionMarkerSize + 1, markerColor, markerBorderColor, 1, "", Color.Empty, 0, Color.FromArgb(128, 0, 0, 0), RectangleF.Empty); } } } } } #endregion #region Position and Size /// /// Resizes an annotation according to its content size. /// /// /// Sets the annotation width and height to fit the specified text. This method applies to /// , , /// and objects only. /// virtual public void ResizeToContent() { RectangleF position = GetContentPosition(); if (!double.IsNaN(position.Width)) { this.Width = position.Width; } if (!double.IsNaN(position.Height)) { this.Height = position.Height; } } /// /// Gets an annotation's content position. /// /// Annotation's content size. virtual internal RectangleF GetContentPosition() { return new RectangleF(float.NaN, float.NaN, float.NaN, float.NaN); } /// /// Gets an annotation's anchor point location. /// /// Returns the anchor X coordinate. /// Returns the anchor Y coordinate. /// Indicates if X coordinate is in relative chart coordinates. /// Indicates if Y coordinate is in relative chart coordinates. private void GetAnchorLocation(ref double anchorX, ref double anchorY, ref bool inRelativeAnchorX, ref bool inRelativeAnchorY) { anchorX = this.AnchorX; anchorY = this.AnchorY; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null && this.Chart != null && this.Chart.chartPicture != null) { // Anchor data point is not allowed for gropped annotations if (this.AnnotationGroup != null) { throw (new InvalidOperationException(SR.ExceptionAnnotationGroupedAnchorDataPointMustBeEmpty)); } // Get data point relative coordinate if (double.IsNaN(anchorX) || double.IsNaN(anchorY)) { // Get X value from data point if (double.IsNaN(anchorX)) { anchorX = this.AnchorDataPoint.positionRel.X; inRelativeAnchorX = true; } // Get Y value from data point if (double.IsNaN(anchorY)) { anchorY = this.AnchorDataPoint.positionRel.Y; inRelativeAnchorY = true; } } } } /// /// Gets annotation object position in relative coordinates. /// /// Returns annotation location. /// Returns annotation size. /// Returns annotation anchor point location. virtual internal void GetRelativePosition(out PointF location, out SizeF size, out PointF anchorLocation) { bool saveCurrentPosition = true; //*********************************************************************** //** Check if position was precalculated //*********************************************************************** if (!double.IsNaN(currentPositionRel.X) && !double.IsNaN(currentPositionRel.X)) { location = currentPositionRel.Location; size = currentPositionRel.Size; anchorLocation = currentAnchorLocationRel; return; } //*********************************************************************** //** Get vertical and horizontal axis //*********************************************************************** Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); //*********************************************************************** //** Check if annotation was anchored to 2 points. //*********************************************************************** if (this._anchorDataPoint != null && this._anchorDataPoint2 != null) { // Annotation size is in axis coordinates this.IsSizeAlwaysRelative = false; // Set annotation size this.Height = vertAxis.PositionToValue(this._anchorDataPoint2.positionRel.Y, false) - vertAxis.PositionToValue(this._anchorDataPoint.positionRel.Y, false); this.Width = horizAxis.PositionToValue(this._anchorDataPoint2.positionRel.X, false) - horizAxis.PositionToValue(this._anchorDataPoint.positionRel.X, false); // Reset second anchor point after setting width and height this._anchorDataPoint2 = null; } //*********************************************************************** //** Flags which indicate that coordinate was already transformed //** into chart relative coordinate system. //*********************************************************************** bool inRelativeX = false; bool inRelativeY = false; bool inRelativeWidth = (_isSizeAlwaysRelative) ? true : false; bool inRelativeHeight = (_isSizeAlwaysRelative) ? true : false; bool inRelativeAnchorX = false; bool inRelativeAnchorY = false; //*********************************************************************** //** Get anchoring coordinates from anchored Data Point. //*********************************************************************** double anchorX = this.AnchorX; double anchorY = this.AnchorY; GetAnchorLocation(ref anchorX, ref anchorY, ref inRelativeAnchorX, ref inRelativeAnchorY); //*********************************************************************** //** Calculate scaling and translation for the annotations in the group. //*********************************************************************** AnnotationGroup group = this.AnnotationGroup; PointF groupLocation = PointF.Empty; double groupScaleX = 1.0; double groupScaleY = 1.0; if (group != null) { // Do not save relative position of annotations inside the group saveCurrentPosition = false; // Take relative position of the group SizeF groupSize = SizeF.Empty; PointF groupAnchorLocation = PointF.Empty; group.GetRelativePosition(out groupLocation, out groupSize, out groupAnchorLocation); // Calculate Scale groupScaleX = groupSize.Width / 100.0; groupScaleY = groupSize.Height / 100.0; } //*********************************************************************** //** Get annotation automatic size. //*********************************************************************** double relativeWidth = this._width; double relativeHeight = this._height; // Get annotation content position RectangleF contentPosition = GetContentPosition(); // Set annotation size if not set to custom value if (double.IsNaN(relativeWidth)) { relativeWidth = contentPosition.Width; inRelativeWidth = true; } else { relativeWidth *= groupScaleX; } if (double.IsNaN(relativeHeight)) { relativeHeight = contentPosition.Height; inRelativeHeight = true; } else { relativeHeight *= groupScaleY; } //*********************************************************************** //** Provide "dummy" size at design time //*********************************************************************** if (this.Chart != null && this.Chart.IsDesignMode()) { if (this.IsSizeAlwaysRelative || (vertAxis == null && horizAxis == null)) { if (double.IsNaN(relativeWidth)) { relativeWidth = 20.0; saveCurrentPosition = false; } if (double.IsNaN(relativeHeight)) { relativeHeight = 20.0; saveCurrentPosition = false; } } } //*********************************************************************** //** Get annotation location. //*********************************************************************** double relativeX = this.X; double relativeY = this.Y; // Check if annotation location Y coordinate is defined if (double.IsNaN(relativeY) && !double.IsNaN(anchorY)) { inRelativeY = true; double relativeAnchorY = anchorY; if (!inRelativeAnchorY && vertAxis != null) { relativeAnchorY = vertAxis.ValueToPosition(anchorY); } if (this.AnchorAlignment == ContentAlignment.TopCenter || this.AnchorAlignment == ContentAlignment.TopLeft || this.AnchorAlignment == ContentAlignment.TopRight) { relativeY = relativeAnchorY + this.AnchorOffsetY; relativeY *= groupScaleY; } else if (this.AnchorAlignment == ContentAlignment.BottomCenter || this.AnchorAlignment == ContentAlignment.BottomLeft || this.AnchorAlignment == ContentAlignment.BottomRight) { relativeY = relativeAnchorY - this.AnchorOffsetY; relativeY *= groupScaleY; if (relativeHeight != 0f && !double.IsNaN(relativeHeight)) { if (inRelativeHeight) { relativeY -= relativeHeight; } else if (vertAxis != null) { float yValue = (float)vertAxis.PositionToValue(relativeY); float bottomRel = (float)vertAxis.ValueToPosition(yValue + relativeHeight); relativeY -= bottomRel - relativeY; } } } else { relativeY = relativeAnchorY + this.AnchorOffsetY; relativeY *= groupScaleY; if (relativeHeight != 0f && !double.IsNaN(relativeHeight)) { if (inRelativeHeight) { relativeY -= relativeHeight / 2f; } else if (vertAxis != null) { float yValue = (float)vertAxis.PositionToValue(relativeY); float bottomRel = (float)vertAxis.ValueToPosition(yValue + relativeHeight); relativeY -= (bottomRel - relativeY) / 2f; } } } } else { relativeY *= groupScaleY; } // Check if annotation location X coordinate is defined if (double.IsNaN(relativeX) && !double.IsNaN(anchorX)) { inRelativeX = true; double relativeAnchorX = anchorX; if (!inRelativeAnchorX && horizAxis != null) { relativeAnchorX = horizAxis.ValueToPosition(anchorX); } if (this.AnchorAlignment == ContentAlignment.BottomLeft || this.AnchorAlignment == ContentAlignment.MiddleLeft || this.AnchorAlignment == ContentAlignment.TopLeft) { relativeX = relativeAnchorX + this.AnchorOffsetX; relativeX *= groupScaleX; } else if (this.AnchorAlignment == ContentAlignment.BottomRight || this.AnchorAlignment == ContentAlignment.MiddleRight || this.AnchorAlignment == ContentAlignment.TopRight) { relativeX = relativeAnchorX - this.AnchorOffsetX; relativeX *= groupScaleX; if (relativeWidth != 0f && !double.IsNaN(relativeWidth)) { if (inRelativeWidth) { relativeX -= relativeWidth; } else if (horizAxis != null) { float xValue = (float)horizAxis.PositionToValue(relativeX); relativeX -= horizAxis.ValueToPosition(xValue + relativeWidth) - relativeX; } } } else { relativeX = relativeAnchorX + this.AnchorOffsetX; relativeX *= groupScaleX; if (relativeWidth != 0f && !double.IsNaN(relativeWidth)) { if (inRelativeWidth) { relativeX -= relativeWidth / 2f; } else if (horizAxis != null) { float xValue = (float)horizAxis.PositionToValue(relativeX); relativeX -= (horizAxis.ValueToPosition(xValue + relativeWidth) - relativeX) / 2f; } } } } else { relativeX *= groupScaleX; } // Translate relativeX += groupLocation.X; relativeY += groupLocation.Y; //*********************************************************************** //** Get annotation automatic location. //*********************************************************************** // Set annotation size if not set to custom value if (double.IsNaN(relativeX)) { relativeX = contentPosition.X * groupScaleX; inRelativeX = true; } if (double.IsNaN(relativeY)) { relativeY = contentPosition.Y * groupScaleY; inRelativeY = true; } //*********************************************************************** //** Convert coordinates from axes values to relative coordinates. //*********************************************************************** // Check if values are set in axis values if (horizAxis != null) { if (!inRelativeX) { relativeX = horizAxis.ValueToPosition(relativeX); } if (!inRelativeAnchorX) { anchorX = horizAxis.ValueToPosition(anchorX); } if (!inRelativeWidth) { relativeWidth = horizAxis.ValueToPosition( horizAxis.PositionToValue(relativeX, false) + relativeWidth) - relativeX; } } if (vertAxis != null) { if (!inRelativeY) { relativeY = vertAxis.ValueToPosition(relativeY); } if (!inRelativeAnchorY) { anchorY = vertAxis.ValueToPosition(anchorY); } if (!inRelativeHeight) { relativeHeight = vertAxis.ValueToPosition( vertAxis.PositionToValue(relativeY, false) + relativeHeight) - relativeY; } } bool isTextAnnotation = this is TextAnnotation; //*********************************************************************** //** Apply 3D transforamtion if required //*********************************************************************** ChartArea chartArea = null; if (horizAxis != null && horizAxis.ChartArea != null) { chartArea = horizAxis.ChartArea; } if (vertAxis != null && vertAxis.ChartArea != null) { chartArea = vertAxis.ChartArea; } if (chartArea != null && chartArea.Area3DStyle.Enable3D == true && !chartArea.chartAreaIsCurcular && chartArea.requireAxes && chartArea.matrix3D.IsInitialized()) { // Get anotation Z coordinate (use scene depth or anchored point Z position) float positionZ = chartArea.areaSceneDepth; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null) { float depth = 0f; chartArea.GetSeriesZPositionAndDepth( this.AnchorDataPoint.series, out depth, out positionZ); positionZ += depth / 2f; } // Define 3D points of annotation object Point3D[] annot3DPoints = new Point3D[3]; annot3DPoints[0] = new Point3D((float)relativeX, (float)relativeY, positionZ); annot3DPoints[1] = new Point3D((float)(relativeX + relativeWidth), (float)(relativeY + relativeHeight), positionZ); annot3DPoints[2] = new Point3D((float)anchorX, (float)anchorY, positionZ); // Tranform cube coordinates chartArea.matrix3D.TransformPoints(annot3DPoints); // Get transformed coordinates relativeX = annot3DPoints[0].X; relativeY = annot3DPoints[0].Y; anchorX = annot3DPoints[2].X; anchorY = annot3DPoints[2].Y; // Don't adjust size for text annotation if (!(isTextAnnotation && this.IsSizeAlwaysRelative)) { relativeWidth = annot3DPoints[1].X - relativeX; relativeHeight = annot3DPoints[1].Y - relativeY; } } //*********************************************************************** //** Provide "dummy" position at design time //*********************************************************************** if (this.Chart != null && this.Chart.IsDesignMode()) { if (double.IsNaN(relativeX)) { relativeX = groupLocation.X; saveCurrentPosition = false; } if (double.IsNaN(relativeY)) { relativeY = groupLocation.Y; saveCurrentPosition = false; } if (double.IsNaN(relativeWidth)) { relativeWidth = 20.0 * groupScaleX; saveCurrentPosition = false; } if (double.IsNaN(relativeHeight)) { relativeHeight = 20.0 * groupScaleY; saveCurrentPosition = false; } } //*********************************************************************** //** Initialize returned values //*********************************************************************** location = new PointF((float)relativeX, (float)relativeY); size = new SizeF((float)relativeWidth, (float)relativeHeight); anchorLocation = new PointF((float)anchorX, (float)anchorY); //*********************************************************************** //** Adjust text based annotaion position using SmartLabelStyle. //*********************************************************************** // Check if smart labels are enabled if (this.SmartLabelStyle.Enabled && isTextAnnotation && group == null) { // Anchor point must be set if (!double.IsNaN(anchorX) && !double.IsNaN(anchorY) && double.IsNaN(this.X) && double.IsNaN(this.Y)) { if (this.Chart != null && this.Chart.chartPicture != null) { // Remember old movement distance restriction double oldMinMovingDistance = this.SmartLabelStyle.MinMovingDistance; double oldMaxMovingDistance = this.SmartLabelStyle.MaxMovingDistance; // Increase annotation moving restrictions according to the anchor offset PointF anchorOffsetAbs = this.GetGraphics().GetAbsolutePoint( new PointF((float)this.AnchorOffsetX, (float)this.AnchorOffsetY)); float maxAnchorOffsetAbs = Math.Max(anchorOffsetAbs.X, anchorOffsetAbs.Y); if (maxAnchorOffsetAbs > 0.0) { this.SmartLabelStyle.MinMovingDistance += maxAnchorOffsetAbs; this.SmartLabelStyle.MaxMovingDistance += maxAnchorOffsetAbs; } // Adjust label position using SmartLabelStyle algorithm LabelAlignmentStyles labelAlignment = LabelAlignmentStyles.Bottom; using (StringFormat format = new StringFormat()) { SizeF markerSizeRel = new SizeF((float)this.AnchorOffsetX, (float)this.AnchorOffsetY); PointF newlocation = this.Chart.chartPicture.annotationSmartLabel.AdjustSmartLabelPosition( this.Common, this.Chart.chartPicture.ChartGraph, chartArea, this.SmartLabelStyle, location, size, format, anchorLocation, markerSizeRel, labelAlignment, (this is CalloutAnnotation)); // Restore old movement distance restriction this.SmartLabelStyle.MinMovingDistance = oldMinMovingDistance; this.SmartLabelStyle.MaxMovingDistance = oldMaxMovingDistance; // Check if annotation should be hidden if (newlocation.IsEmpty) { location = new PointF(float.NaN, float.NaN); } else { // Get new position using alignment in format RectangleF newPosition = this.Chart.chartPicture.annotationSmartLabel.GetLabelPosition( this.Chart.chartPicture.ChartGraph, newlocation, size, format, false); // Set new location location = newPosition.Location; } } } } else { // Add annotation position into the list (to prevent overlapping) using (StringFormat format = new StringFormat()) { this.Chart.chartPicture.annotationSmartLabel.AddSmartLabelPosition( this.Chart.chartPicture.ChartGraph, location, size, format); } } } //*********************************************************************** //** Save calculated position //*********************************************************************** if (saveCurrentPosition) { currentPositionRel = new RectangleF(location, size); currentAnchorLocationRel = new PointF(anchorLocation.X, anchorLocation.Y); } } /// /// Set annotation object position using rectangle in relative coordinates. /// Automatically converts relative coordinates to axes values if required. /// /// Position in relative coordinates. /// Anchor location in relative coordinates. internal void SetPositionRelative(RectangleF position, PointF anchorPoint) { SetPositionRelative(position, anchorPoint, false); } /// /// Set annotation object position using rectangle in relative coordinates. /// Automatically converts relative coordinates to axes values if required. /// /// Position in relative coordinates. /// Anchor location in relative coordinates. /// Indicates if position changing was a result of the user input. internal void SetPositionRelative(RectangleF position, PointF anchorPoint, bool userInput) { double newX = position.X; double newY = position.Y; double newRight = position.Right; double newBottom = position.Bottom; double newWidth = position.Width; double newHeight = position.Height; double newAnchorX = anchorPoint.X; double newAnchorY = anchorPoint.Y; //*********************************************************************** //** Set pre calculated position and anchor location //*********************************************************************** this.currentPositionRel = new RectangleF(position.Location, position.Size); this.currentAnchorLocationRel = new PointF(anchorPoint.X, anchorPoint.Y); //*********************************************************************** //** Get vertical and horizontal axis //*********************************************************************** Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); //*********************************************************************** //** Disable anchoring to point and axes in 3D //** This is done due to the issues of moving elements in 3D space. //*********************************************************************** ChartArea chartArea = null; if (horizAxis != null && horizAxis.ChartArea != null) { chartArea = horizAxis.ChartArea; } if (vertAxis != null && vertAxis.ChartArea != null) { chartArea = vertAxis.ChartArea; } if (chartArea != null && chartArea.Area3DStyle.Enable3D == true) { // If anchor point was set - get its relative position and use it as an anchor point if (this.AnchorDataPoint != null) { bool inRelativeCoordX = true; bool inRelativeCoordY = true; this.GetAnchorLocation(ref newAnchorX, ref newAnchorY, ref inRelativeCoordX, ref inRelativeCoordY); this.currentAnchorLocationRel = new PointF((float)newAnchorX, (float)newAnchorY); } // In 3D always use relative annotation coordinates // Disconnect annotation from axes and anchor point this.AnchorDataPoint = null; this.AxisX = null; this.AxisY = null; horizAxis = null; vertAxis = null; } //*********************************************************************** //** Convert relative coordinates to axis values //*********************************************************************** if (horizAxis != null) { newX = horizAxis.PositionToValue(newX, false); if (!double.IsNaN(newAnchorX)) { newAnchorX = horizAxis.PositionToValue(newAnchorX, false); } // Adjust for the IsLogarithmic axis if (horizAxis.IsLogarithmic) { newX = Math.Pow(horizAxis.logarithmBase, newX); if (!double.IsNaN(newAnchorX)) { newAnchorX = Math.Pow(horizAxis.logarithmBase, newAnchorX); } } if (!this.IsSizeAlwaysRelative) { if (float.IsNaN(position.Right) && !float.IsNaN(position.Width) && !float.IsNaN(anchorPoint.X)) { newRight = horizAxis.PositionToValue(anchorPoint.X + position.Width, false); if (horizAxis.IsLogarithmic) { newRight = Math.Pow(horizAxis.logarithmBase, newRight); } newWidth = newRight - newAnchorX; } else { newRight = horizAxis.PositionToValue(position.Right, false); if (horizAxis.IsLogarithmic) { newRight = Math.Pow(horizAxis.logarithmBase, newRight); } newWidth = newRight - newX; } } } if (vertAxis != null) { newY = vertAxis.PositionToValue(newY, false); if (!double.IsNaN(newAnchorY)) { newAnchorY = vertAxis.PositionToValue(newAnchorY, false); } // NOTE: Fixes issue #4113 // Adjust for the IsLogarithmic axis if (vertAxis.IsLogarithmic) { newY = Math.Pow(vertAxis.logarithmBase, newY); if (!double.IsNaN(newAnchorY)) { newAnchorY = Math.Pow(vertAxis.logarithmBase, newAnchorY); } } if (!this.IsSizeAlwaysRelative) { if (float.IsNaN(position.Bottom) && !float.IsNaN(position.Height) && !float.IsNaN(anchorPoint.Y)) { newBottom = vertAxis.PositionToValue(anchorPoint.Y + position.Height, false); if (vertAxis.IsLogarithmic) { newBottom = Math.Pow(vertAxis.logarithmBase, newBottom); } newHeight = newBottom - newAnchorY; } else { newBottom = vertAxis.PositionToValue(position.Bottom, false); if (vertAxis.IsLogarithmic) { newBottom = Math.Pow(vertAxis.logarithmBase, newBottom); } newHeight = newBottom - newY; } } } // Fire position changing event when position changed by user. if (userInput) { // Set flag that annotation position was changed this.positionChanged = true; // Fire position changing event if (this.Chart != null) { AnnotationPositionChangingEventArgs args = new AnnotationPositionChangingEventArgs(); args.NewLocationX = newX; args.NewLocationY = newY; args.NewSizeWidth = newWidth; args.NewSizeHeight = newHeight; args.NewAnchorLocationX = newAnchorX; args.NewAnchorLocationY = newAnchorY; args.Annotation = this; if (this.Chart.OnAnnotationPositionChanging(ref args)) { // Get user changed position/anchor newX = args.NewLocationX; newY = args.NewLocationY; newWidth = args.NewSizeWidth; newHeight = args.NewSizeHeight; newAnchorX = args.NewAnchorLocationX; newAnchorY = args.NewAnchorLocationY; } } } // Adjust location & size this.X = newX; this.Y = newY; this.Width = newWidth; this.Height = newHeight; this.AnchorX = newAnchorX; this.AnchorY = newAnchorY; // Invalidate annotation this.Invalidate(); return; } /// /// Adjust annotation location and\or size as a result of user action. /// /// Distance to resize/move the annotation. /// Resizing mode. virtual internal void AdjustLocationSize(SizeF movingDistance, ResizingMode resizeMode) { AdjustLocationSize(movingDistance, resizeMode, true); } /// /// Adjust annotation location and\or size as a result of user action. /// /// Distance to resize/move the annotation. /// Resizing mode. /// Distance is in pixels, otherwise relative. virtual internal void AdjustLocationSize(SizeF movingDistance, ResizingMode resizeMode, bool pixelCoord) { AdjustLocationSize(movingDistance, resizeMode, pixelCoord, false); } /// /// Adjust annotation location and\or size as a result of user action. /// /// Distance to resize/move the annotation. /// Resizing mode. /// Distance is in pixels, otherwise relative. /// Indicates if position changing was a result of the user input. virtual internal void AdjustLocationSize(SizeF movingDistance, ResizingMode resizeMode, bool pixelCoord, bool userInput) { if (!movingDistance.IsEmpty) { // Convert pixel coordinates into relative if (pixelCoord) { movingDistance = Chart.chartPicture.ChartGraph.GetRelativeSize(movingDistance); } // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; if (userInput) { if (this.startMovePositionRel.X == 0f && this.startMovePositionRel.Y == 0f && this.startMovePositionRel.Width == 0f && this.startMovePositionRel.Height == 0f) { GetRelativePosition(out firstPoint, out size, out anchorPoint); this.startMovePositionRel = new RectangleF(firstPoint, size); this.startMoveAnchorLocationRel = new PointF(anchorPoint.X, anchorPoint.Y); } firstPoint = this.startMovePositionRel.Location; size = this.startMovePositionRel.Size; anchorPoint = this.startMoveAnchorLocationRel; } else { GetRelativePosition(out firstPoint, out size, out anchorPoint); } if (resizeMode == ResizingMode.TopLeftHandle) { firstPoint.X -= movingDistance.Width; firstPoint.Y -= movingDistance.Height; size.Width += movingDistance.Width; size.Height += movingDistance.Height; } else if (resizeMode == ResizingMode.TopHandle) { firstPoint.Y -= movingDistance.Height; size.Height += movingDistance.Height; } else if (resizeMode == ResizingMode.TopRightHandle) { firstPoint.Y -= movingDistance.Height; size.Width -= movingDistance.Width; size.Height += movingDistance.Height; } else if (resizeMode == ResizingMode.RightHandle) { size.Width -= movingDistance.Width; } else if (resizeMode == ResizingMode.BottomRightHandle) { size.Width -= movingDistance.Width; size.Height -= movingDistance.Height; } else if (resizeMode == ResizingMode.BottomHandle) { size.Height -= movingDistance.Height; } else if (resizeMode == ResizingMode.BottomLeftHandle) { firstPoint.X -= movingDistance.Width; size.Width += movingDistance.Width; size.Height -= movingDistance.Height; } else if (resizeMode == ResizingMode.LeftHandle) { firstPoint.X -= movingDistance.Width; size.Width += movingDistance.Width; } else if (resizeMode == ResizingMode.AnchorHandle) { anchorPoint.X -= movingDistance.Width; anchorPoint.Y -= movingDistance.Height; } else if (resizeMode == ResizingMode.Moving) { firstPoint.X -= movingDistance.Width; firstPoint.Y -= movingDistance.Height; } // Make sure we do not override automatic Width and Heigth if (resizeMode == ResizingMode.Moving) { if (double.IsNaN(this.Width)) { size.Width = float.NaN; } if (double.IsNaN(this.Height)) { size.Height = float.NaN; } } // Make sure we do not override automatic X and Y if (resizeMode == ResizingMode.AnchorHandle) { if (double.IsNaN(this.X)) { firstPoint.X = float.NaN; } if (double.IsNaN(this.Y)) { firstPoint.Y = float.NaN; } } else if (double.IsNaN(this.AnchorX) || double.IsNaN(this.AnchorY)) { anchorPoint = new PointF(float.NaN, float.NaN); } // Set annotation position from rectangle in relative coordinates SetPositionRelative(new RectangleF(firstPoint, size), anchorPoint, userInput); } return; } #endregion #region Anchor Point and Axes Converters /// /// Checks if annotation draw anything in the anchor position (except selection handle) /// /// True if annotation "connects" itself and anchor point visually. virtual internal bool IsAnchorDrawn() { return false; } /// /// Gets data point by name. /// /// Data point name to find. /// Data point. internal DataPoint GetDataPointByName(string dataPointName) { DataPoint dataPoint = null; if (Chart != null && dataPointName.Length > 0) { // Split series name and point index int separatorIndex = dataPointName.IndexOf("\\r", StringComparison.Ordinal); if (separatorIndex > 0) { string seriesName = dataPointName.Substring(0, separatorIndex); string pointIndex = dataPointName.Substring(separatorIndex + 2); int index = 0; if (int.TryParse(pointIndex, NumberStyles.Any, CultureInfo.InvariantCulture, out index)) { dataPoint = Chart.Series[seriesName].Points[index]; } } } return dataPoint; } /// /// Gets axis by name. /// /// Axis name to find. /// Data point. private Axis GetAxisByName(string axisName) { Debug.Assert(axisName != null, "GetAxisByName: handed a null axis name"); Axis axis = null; try { if (Chart != null && axisName.Length > 0) { // Split series name and point index int separatorIndex = axisName.IndexOf("\\r", StringComparison.Ordinal); if (separatorIndex > 0) { string areaName = axisName.Substring(0, separatorIndex); string axisType = axisName.Substring(separatorIndex + 2); switch ((AxisName)Enum.Parse(typeof(AxisName), axisType)) { case (AxisName.X): axis = Chart.ChartAreas[areaName].AxisX; break; case (AxisName.Y): axis = Chart.ChartAreas[areaName].AxisY; break; case (AxisName.X2): axis = Chart.ChartAreas[areaName].AxisX2; break; case (AxisName.Y2): axis = Chart.ChartAreas[areaName].AxisY2; break; } } } } catch (ArgumentNullException) { axis = null; } catch (ArgumentException) { axis = null; } return axis; } /// /// Gets data point unique name. /// /// Data point to get the name for. /// Data point name. internal string GetDataPointName(DataPoint dataPoint) { string name = String.Empty; if (dataPoint.series != null) { int pointIndex = dataPoint.series.Points.IndexOf(dataPoint); if (pointIndex >= 0) { name = dataPoint.series.Name + "\\r" + pointIndex.ToString(CultureInfo.InvariantCulture); } } return name; } /// /// Gets axis unique name. /// /// Axis to get the name for. /// Axis name. private string GetAxisName(Axis axis) { string name = String.Empty; if (axis.ChartArea != null) { name = axis.ChartArea.Name + "\\r" + axis.AxisName.ToString(); } return name; } #endregion #region Z Order Methods /// /// Sends an annotation to the back of all annotations. /// /// virtual public void SendToBack() { // Find collection of annotation objects this annotation belongs too AnnotationCollection collection = null; if (Chart != null) { collection = Chart.Annotations; } // Check if annotation belongs to the group AnnotationGroup group = AnnotationGroup; if (group != null) { collection = group.Annotations; } // Check if annotation is found if (collection != null) { Annotation annot = collection.FindByName(this.Name); if (annot != null) { // Reinsert annotation at the beginning of the collection collection.Remove(annot); collection.Insert(0, annot); } } } /// /// Brings an annotation to the front of all annotations. /// /// virtual public void BringToFront() { // Find collection of annotation objects this annotation belongs too AnnotationCollection collection = null; if (Chart != null) { collection = Chart.Annotations; } // Check if annotation belongs to the group AnnotationGroup group = AnnotationGroup; if (group != null) { collection = group.Annotations; } // Check if annotation is found if (collection != null) { Annotation annot = collection.FindByName(this.Name); if (annot != null) { // Reinsert annotation at the end of the collection collection.Remove(annot); collection.Add(this); } } } #endregion // Z Order Methods #region Group Related Methods #endregion // Group Related Methods #region SmartLabelStyle methods /// /// Adds anchor position to the list. Used to check SmartLabelStyle overlapping. /// /// List to add to. internal void AddSmartLabelMarkerPositions(ArrayList list) { // Anchor position is added to the list of non-overlapped markers if (this.Visible && this.IsAnchorDrawn()) { // Get vertical and horizontal axis Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); // Get anchor position double anchorX = double.NaN; double anchorY = double.NaN; bool relativeX = false; bool relativeY = false; this.GetAnchorLocation(ref anchorX, ref anchorY, ref relativeX, ref relativeY); // Convert anchor location to relative coordinates if (!double.IsNaN(anchorX) && !double.IsNaN(anchorY)) { if (!relativeX && horizAxis != null) { anchorX = horizAxis.ValueToPosition(anchorX); } if (!relativeY && vertAxis != null) { anchorY = vertAxis.ValueToPosition(anchorY); } // Apply 3D transforamtion if required ChartArea chartArea = null; if (horizAxis != null && horizAxis.ChartArea != null) { chartArea = horizAxis.ChartArea; } if (vertAxis != null && vertAxis.ChartArea != null) { chartArea = vertAxis.ChartArea; } if (chartArea != null && chartArea.Area3DStyle.Enable3D == true && !chartArea.chartAreaIsCurcular && chartArea.requireAxes && chartArea.matrix3D.IsInitialized()) { // Get anotation Z coordinate (use scene depth or anchored point Z position) float positionZ = chartArea.areaSceneDepth; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null) { float depth = 0f; chartArea.GetSeriesZPositionAndDepth( this.AnchorDataPoint.series, out depth, out positionZ); positionZ += depth / 2f; } // Define 3D points of annotation object Point3D[] annot3DPoints = new Point3D[1]; annot3DPoints[0] = new Point3D((float)anchorX, (float)anchorY, positionZ); // Tranform cube coordinates chartArea.matrix3D.TransformPoints(annot3DPoints); // Get transformed coordinates anchorX = annot3DPoints[0].X; anchorY = annot3DPoints[0].Y; } // Save selection handles position in array elements 0 and 4 if (this.GetGraphics() != null) { SizeF markerSizeRel = this.GetGraphics().GetRelativeSize( new SizeF(1f, 1f)); RectangleF anchorRect = new RectangleF( (float)anchorX - markerSizeRel.Width / 2f, (float)anchorY - markerSizeRel.Height / 2f, markerSizeRel.Width, markerSizeRel.Height); list.Add(anchorRect); } } } } #endregion #region Public Anchoring Methods /// /// Anchors an annotation to a data point. /// /// /// /// /// /// to be anchored to. /// /// /// Anchors an annotation to the specified data point. /// public void SetAnchor(DataPoint dataPoint) { SetAnchor(dataPoint, null); } /// /// Anchors an annotation to two data points. /// /// /// /// /// /// First anchor . /// /// /// Second anchor . /// /// /// Anchors an annotation's top/left and bottom/right corners to the /// specified data points. /// public void SetAnchor(DataPoint dataPoint1, DataPoint dataPoint2) { // Set annotation position to automatic this.X = double.NaN; this.Y = double.NaN; // Reset anchor point if any this.AnchorX = double.NaN; this.AnchorY = double.NaN; // Set anchor point this.AnchorDataPoint = dataPoint1; // Get vertical and horizontal axis Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); // Set Width and Height in axis coordinates if (dataPoint2 != null && dataPoint1 != null) { this._anchorDataPoint2 = dataPoint2; } // Invalidate annotation this.Invalidate(); } #endregion // Public Anchoring Methods #region Placement Methods /// /// Begins end user placement of an annotation using the mouse. /// /// /// When this method is called, the end user is allowed to place an annotation using the /// mouse. /// /// Placement will finish when the end user specifies all required points, or /// the method is called. /// virtual public void BeginPlacement() { // Can't place annotations inside the group if (this.AnnotationGroup != null) { throw (new InvalidOperationException(SR.ExceptionAnnotationGroupedUnableToStartPlacement)); } if (this.Chart != null) { // Set the annotation object which is currently placed this.Chart.Annotations.placingAnnotation = this; } else { throw (new InvalidOperationException(SR.ExceptionAnnotationNotInCollection)); } } /// /// Ends user placement of an annotation. /// /// /// Ends an annotation placement operation previously started by a /// method call. /// /// Calling this method is not required, since placement will automatically /// end when an end user enters all required points. However, it is useful when an annotation /// placement operation needs to be aborted for some reason. /// /// virtual public void EndPlacement() { if (this.Chart != null) { // Reset currently placed annotation object this.Chart.Annotations.placingAnnotation = null; // Restore default cursor this.Chart.Cursor = this.Chart.defaultCursor; // Clear last placement mouse position this.lastPlacementPosition = PointF.Empty; // Fire annotation placed event this.Chart.OnAnnotationPlaced(this); } } /// /// Handles mouse down event during annotation placement. /// /// Mouse cursor position in pixels. /// Mouse button down. internal virtual void PlacementMouseDown(PointF point, MouseButtons buttons) { if (buttons == MouseButtons.Right) { // Stop any pacement this.EndPlacement(); } if (buttons == MouseButtons.Left && IsValidPlacementPosition(point.X, point.Y)) { if (this.lastPlacementPosition.IsEmpty) { // Remeber position where mouse was clicked this.lastPlacementPosition = this.GetGraphics().GetRelativePoint(point); // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; this.GetRelativePosition(out firstPoint, out size, out anchorPoint); // Set annotation X, Y coordinate if (this.AllowMoving) { firstPoint = this.GetGraphics().GetRelativePoint(point); // Do not change default position if (double.IsNaN(this.AnchorX)) { anchorPoint.X = float.NaN; } if (double.IsNaN(this.AnchorY)) { anchorPoint.Y = float.NaN; } } else if (this.AllowAnchorMoving) { anchorPoint = this.GetGraphics().GetRelativePoint(point); // Do not change default position if (double.IsNaN(this.X)) { firstPoint.X = float.NaN; } if (double.IsNaN(this.Y)) { firstPoint.Y = float.NaN; } } // Do not change default size if (double.IsNaN(this.Width)) { size.Width = float.NaN; } if (double.IsNaN(this.Height)) { size.Height = float.NaN; } // Set annotation position this.positionChanged = true; this.SetPositionRelative( new RectangleF(firstPoint, size), anchorPoint, true); // Invalidate and update the chart if (Chart != null) { Invalidate(); Chart.UpdateAnnotations(); } } } } /// /// Handles mouse up event during annotation placement. /// /// Mouse cursor position in pixels. /// Mouse button Up. /// Return true when placing finished. internal virtual bool PlacementMouseUp(PointF point, MouseButtons buttons) { bool result = false; if (buttons == MouseButtons.Left) { // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; this.GetRelativePosition(out firstPoint, out size, out anchorPoint); if (this.AllowResizing) { PointF pointRel = this.GetGraphics().GetRelativePoint(point); size = new SizeF( pointRel.X - this.lastPlacementPosition.X, pointRel.Y - this.lastPlacementPosition.Y); } else { // Do not change default size if (double.IsNaN(this.Width)) { size.Width = float.NaN; } if (double.IsNaN(this.Height)) { size.Height = float.NaN; } } // Do not change default position if (double.IsNaN(this.X)) { firstPoint.X = float.NaN; } if (double.IsNaN(this.Y)) { firstPoint.Y = float.NaN; } if (double.IsNaN(this.AnchorX)) { anchorPoint.X = float.NaN; } if (double.IsNaN(this.AnchorY)) { anchorPoint.Y = float.NaN; } // Set annotation position this.positionChanged = true; this.SetPositionRelative( new RectangleF(firstPoint, size), anchorPoint, true); // End placement if (!size.IsEmpty || !this.AllowResizing) { result = true; this.EndPlacement(); } // Invalidate and update the chart if (Chart != null) { Invalidate(); Chart.UpdateAnnotations(); } } return result; } /// /// Handles mouse move event during annotation placement. /// /// Mouse cursor position in pixels. internal virtual void PlacementMouseMove(PointF point) { // Check if annotation was moved if (this.GetGraphics() != null && !this.lastPlacementPosition.IsEmpty) { // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; this.GetRelativePosition(out firstPoint, out size, out anchorPoint); if (this.AllowResizing) { PointF pointRel = this.GetGraphics().GetRelativePoint(point); size = new SizeF( pointRel.X - this.lastPlacementPosition.X, pointRel.Y - this.lastPlacementPosition.Y); } // Do not change default position if (double.IsNaN(this.X)) { firstPoint.X = float.NaN; } if (double.IsNaN(this.Y)) { firstPoint.Y = float.NaN; } if (double.IsNaN(this.AnchorX)) { anchorPoint.X = float.NaN; } if (double.IsNaN(this.AnchorY)) { anchorPoint.Y = float.NaN; } // Set annotation position this.positionChanged = true; this.SetPositionRelative( new RectangleF(firstPoint, size), anchorPoint, true); // Invalidate and update the chart if (this.Chart != null) { Invalidate(); this.Chart.UpdateAnnotations(); } } } /// /// Checks if specified position is valid for placement. /// /// X coordinate. /// Y coordinate. /// True if annotation can be placed at specified coordinates. virtual internal bool IsValidPlacementPosition(float x, float y) { if (this.Chart != null && this.GetGraphics() != null) { // Check if cursor is over the area where placement allowed // If so - change cursor to cross RectangleF placementRect = new RectangleF(0f, 0f, 100f, 100f); if (this.ClipToChartArea.Length > 0 && this.ClipToChartArea != Constants.NotSetValue) { ChartArea area = Chart.ChartAreas[this.ClipToChartArea]; placementRect = area.PlotAreaPosition.ToRectangleF(); } placementRect = this.GetGraphics().GetAbsoluteRectangle(placementRect); if (placementRect.Contains(x, y)) { return true; } } return false; } #endregion // Placement Methods #region Helper Methods /// /// Helper method that checks if annotation is visible. /// /// True if annotation is visible. internal bool IsVisible() { if (this.Visible) { if (this.Chart != null) { // Check if annotation is anchored to the data point ChartArea area = null; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null) { if (this.Chart.ChartAreas.IndexOf(this.AnchorDataPoint.series.ChartArea) >= 0) { area = this.Chart.ChartAreas[this.AnchorDataPoint.series.ChartArea]; } } if (area == null && this._anchorDataPoint2 != null && this._anchorDataPoint2.series != null) { if (this.Chart.ChartAreas.IndexOf(this._anchorDataPoint2.series.ChartArea) >= 0) { area = this.Chart.ChartAreas[this._anchorDataPoint2.series.ChartArea]; } } // Check if annotation uses chart area axis values if (area == null && this.AxisX != null) { area = this.AxisX.ChartArea; } if (area == null && this.AxisY != null) { area = this.AxisY.ChartArea; } // Check if associated area is visible if (area != null && !area.Visible) { return false; } } return true; } return false; } /// /// Resets pre-calculated annotation position. /// internal void ResetCurrentRelativePosition() { this.currentPositionRel = new RectangleF(float.NaN, float.NaN, float.NaN, float.NaN); this.currentAnchorLocationRel = new PointF(float.NaN, float.NaN); } /// /// Replaces predefined keyword inside the string with their values if /// annotation is anchored to the data point. /// /// Original string with keywords. /// Modified string. internal string ReplaceKeywords(string strOriginal) { if (this.AnchorDataPoint != null) { return this.AnchorDataPoint.ReplaceKeywords(strOriginal); } return strOriginal; } /// /// Checks if anchor point of the annotation is visible. /// /// True if anchor point is visible. internal bool IsAnchorVisible() { // Get axes objects Axis vertAxis = null; Axis horizAxis = null; GetAxes(ref vertAxis, ref horizAxis); // Get anchor position bool inRelativeAnchorX = false; bool inRelativeAnchorY = false; double anchorX = this.AnchorX; double anchorY = this.AnchorY; GetAnchorLocation(ref anchorX, ref anchorY, ref inRelativeAnchorX, ref inRelativeAnchorY); // Check if anchor is set if (!double.IsNaN(anchorX) && !double.IsNaN(anchorY)) { // Check if anchor is in axes coordinates if (this.AnchorDataPoint != null || this.AxisX != null || this.AxisY != null) { // Convert anchor point to relative coordinates if (!inRelativeAnchorX && horizAxis != null) { anchorX = horizAxis.ValueToPosition(anchorX); } if (!inRelativeAnchorY && vertAxis != null) { anchorY = vertAxis.ValueToPosition(anchorY); } // Get chart area ChartArea chartArea = null; if (horizAxis != null) { chartArea = horizAxis.ChartArea; } if (chartArea == null && vertAxis != null) { chartArea = vertAxis.ChartArea; } // Apply 3D transforamtion if required if (chartArea != null && chartArea.Area3DStyle.Enable3D == true) { if (!chartArea.chartAreaIsCurcular && chartArea.requireAxes && chartArea.matrix3D.IsInitialized()) { // Get anotation Z coordinate (use scene depth or anchored point Z position) float positionZ = chartArea.areaSceneDepth; if (this.AnchorDataPoint != null && this.AnchorDataPoint.series != null) { float depth = 0f; chartArea.GetSeriesZPositionAndDepth( this.AnchorDataPoint.series, out depth, out positionZ); positionZ += depth / 2f; } // Define 3D points of annotation object Point3D[] annot3DPoints = new Point3D[1]; annot3DPoints[0] = new Point3D((float)anchorX, (float)anchorY, positionZ); // Tranform cube coordinates chartArea.matrix3D.TransformPoints(annot3DPoints); // Get transformed coordinates anchorX = annot3DPoints[0].X; anchorY = annot3DPoints[0].Y; } } // Get plot rectangle position and inflate it slightly // to solve any float rounding issues. RectangleF rect = chartArea.PlotAreaPosition.ToRectangleF(); rect.Inflate(0.00001f, 0.00001f); // Check if anchor point is in the plotting area if (!rect.Contains((float)anchorX, (float)anchorY)) { return false; } } } return true; } /// /// Returns chart graphics objects. /// /// Chart graphics object. internal ChartGraphics GetGraphics() { if (this.Common != null) { return this.Common.graph; } return null; } /// /// Checks if provided pixel coordinate is contained in one of the /// selection handles rectangle. /// /// Coordinate in pixels. /// Resizing mode. internal ResizingMode GetSelectionHandle(PointF point) { ResizingMode resizingMode = ResizingMode.None; if (this.Common != null && this.Common.graph != null) { // Convert point to relative coordinates point = this.Common.graph.GetRelativePoint(point); // Check if point is in one of the selection handles if (this.selectionRects != null) { for (int index = 0; index < this.selectionRects.Length; index++) { if (!this.selectionRects[index].IsEmpty && this.selectionRects[index].Contains(point)) { if (index > (int)ResizingMode.AnchorHandle) { resizingMode = ResizingMode.MovingPathPoints; this.currentPathPointIndex = index - 9; } else { resizingMode = (ResizingMode)index; } } } } } return resizingMode; } /// /// Gets data point X or Y axis. /// /// Data point to get the axis for. /// X or Y axis to get. /// Data point axis. private Axis GetDataPointAxis(DataPoint dataPoint, AxisName axisName) { if (dataPoint != null && dataPoint.series != null && Chart != null) { // Get data point chart area ChartArea chartArea = Chart.ChartAreas[dataPoint.series.ChartArea]; // Get point X axis if ((axisName == AxisName.X || axisName == AxisName.X2) && !chartArea.switchValueAxes) { return chartArea.GetAxis(axisName, dataPoint.series.XAxisType, dataPoint.series.XSubAxisName); } else { return chartArea.GetAxis(axisName, dataPoint.series.YAxisType, dataPoint.series.YSubAxisName); } } return null; } /// /// Gets annotation vertical and horizontal axes. /// /// Returns annotation vertical axis or null. /// Returns annotation horizontal axis or null. internal void GetAxes(ref Axis vertAxis, ref Axis horizAxis) { vertAxis = null; horizAxis = null; if (this.AxisX != null && this.AxisX.ChartArea != null) { if (this.AxisX.ChartArea.switchValueAxes) { vertAxis = this.AxisX; } else { horizAxis = this.AxisX; } } if (this.AxisY != null && this.AxisY.ChartArea != null) { if (this.AxisY.ChartArea.switchValueAxes) { horizAxis = this.AxisY; } else { vertAxis = this.AxisY; } } // Get axes from attached data point if (this.AnchorDataPoint != null) { if (horizAxis == null) { horizAxis = GetDataPointAxis(this.AnchorDataPoint, AxisName.X); // For chart types like Bar, RangeBar and others, position of X and Y axes are flipped if (horizAxis != null && horizAxis.ChartArea != null && horizAxis.ChartArea.switchValueAxes) { horizAxis = GetDataPointAxis(this.AnchorDataPoint, AxisName.Y); } } if (vertAxis == null) { vertAxis = GetDataPointAxis(this.AnchorDataPoint, AxisName.Y); // For chart types like Bar, RangeBar and others, position of X and Y axes are flipped if (vertAxis != null && vertAxis.ChartArea != null && vertAxis.ChartArea.switchValueAxes) { vertAxis = GetDataPointAxis(this.AnchorDataPoint, AxisName.X); } } } // No axes coordinate system for grouped annotations if (vertAxis != null || horizAxis != null) { if (this.AnnotationGroup != null) { throw (new InvalidOperationException(SR.ExceptionAnnotationGroupedAxisMustBeEmpty)); } } } #endregion #endregion #region IDisposable Members /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { //Free managed resources if (_fontCache != null) { _fontCache.Dispose(); _fontCache = null; } } base.Dispose(disposing); } #endregion } /// /// This class is used to stores position changing event data for an annotation. /// /// /// Provides additional data like the new annotation and anchor position when an end user /// is moving the annotation with the mouse. /// /// Can be used to restrict annotation movement, or snap the annotation position to /// specific points. /// /// [ SRDescription("DescriptionAttributeAnnotationPositionChangingEventArgs_AnnotationPositionChangingEventArgs"), ] public class AnnotationPositionChangingEventArgs : EventArgs { #region Fields private Annotation _Annotation = null; /// /// Gets or sets the annotation the event is fired for. /// public Annotation Annotation { get { return _Annotation; } set { _Annotation = value; } } private double _NewLocationX = 0.0; /// /// Gets or sets the new X location of the annotation. /// public double NewLocationX { get { return _NewLocationX; } set { _NewLocationX = value; } } private double _NewLocationY = 0.0; /// /// Gets or sets the new Y location of the annotation. /// public double NewLocationY { get { return _NewLocationY; } set { _NewLocationY = value; } } private double _NewSizeWidth = 0.0; /// /// Gets or sets the new width of the annotation. /// public double NewSizeWidth { get { return _NewSizeWidth; } set { _NewSizeWidth = value; } } private double _NewSizeHeight = 0.0; /// /// Gets or sets the new height of the annotation. /// public double NewSizeHeight { get { return _NewSizeHeight; } set { _NewSizeHeight = value; } } private double _NewAnchorLocationX = 0.0; /// /// Gets or sets the new annotation anchor point X location. /// public double NewAnchorLocationX { get { return _NewAnchorLocationX; } set { _NewAnchorLocationX = value; } } private double _NewAnchorLocationY = 0.0; /// /// Gets or sets the new annotation anchor point Y location. /// public double NewAnchorLocationY { get { return _NewAnchorLocationY; } set { _NewAnchorLocationY = value; } } #endregion // Fields #region Properties /// /// Gets or sets the new location and size of the annotation. /// [ Browsable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), ] public RectangleF NewPosition { get { return new RectangleF( (float)this.NewLocationX, (float)this.NewLocationY, (float)this.NewSizeWidth, (float)this.NewSizeHeight); } set { this.NewLocationX = value.X; this.NewLocationY = value.Y; this.NewSizeWidth = value.Width; this.NewSizeHeight = value.Height; } } /// /// Gets or sets the new anchor location of the annotation. /// [ Browsable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), ] public PointF NewAnchorLocation { get { return new PointF( (float)this.NewAnchorLocationX, (float)this.NewAnchorLocationY); } set { this.NewAnchorLocationX = value.X; this.NewAnchorLocationY = value.Y; } } #endregion // Properties } }