//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant //================================================================= // File: ArrowAnnotation.cs // // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting // // Classes: ArrowAnnotation // // Purpose: Arrow annotation classes. // // Reviewed: // //=================================================================== #region Used namespace using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.Data; using System.Drawing; using System.Drawing.Design; using System.Drawing.Text; using System.Drawing.Drawing2D; #if Microsoft_CONTROL using System.Windows.Forms.DataVisualization.Charting; using System.Windows.Forms.DataVisualization.Charting.Data; using System.Windows.Forms.DataVisualization.Charting.ChartTypes; using System.Windows.Forms.DataVisualization.Charting.Utilities; using System.Windows.Forms.DataVisualization.Charting.Borders3D; #else using System.Web; using System.Web.UI; using System.Web.UI.DataVisualization.Charting; using System.Web.UI.DataVisualization.Charting.Data; using System.Web.UI.DataVisualization.Charting.Utilities; using System.Web.UI.DataVisualization.Charting.Borders3D; #endif #endregion #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting #else namespace System.Web.UI.DataVisualization.Charting #endif { #region Enumeration /// /// Arrow annotation styles. /// /// [ SRDescription("DescriptionAttributeArrowStyle_ArrowStyle") ] public enum ArrowStyle { /// /// Simple arrow. /// Simple, /// /// Arrow pointing in two directions. /// DoubleArrow, /// /// Arrow with a tail. /// Tailed, } #endregion // Enumeration /// /// ArrowAnnotation is a class class that represents an arrow annotation. /// /// /// Arrow annotations can be used to connect to points on the chart or highlight a /// single chart area. Different arrow styles and sizes may be applied. /// [ SRDescription("DescriptionAttributeArrowAnnotation_ArrowAnnotation"), ] #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public class ArrowAnnotation : Annotation { #region Fields // Annotation arrow style private ArrowStyle _arrowStyle = ArrowStyle.Simple; // Annotation arrow size private int _arrowSize = 5; #endregion #region Construction and Initialization /// /// Default public constructor. /// public ArrowAnnotation() : base() { base.AnchorAlignment = ContentAlignment.TopLeft; } #endregion #region Properties #region Arrow properties /// /// Gets or sets the arrow style of an arrow annotation. /// /// /// /// of an annotation. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(ArrowStyle.Simple), SRDescription("DescriptionAttributeArrowAnnotation_ArrowStyle"), ParenthesizePropertyNameAttribute(true), ] virtual public ArrowStyle ArrowStyle { get { return _arrowStyle; } set { _arrowStyle = value; Invalidate(); } } /// /// Gets or sets the arrow size in pixels of an arrow annotation. /// /// /// /// Integer value that represents arrow size (thickness) in pixels. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(5), SRDescription("DescriptionAttributeArrowAnnotation_ArrowSize"), ParenthesizePropertyNameAttribute(true), ] virtual public int ArrowSize { get { return _arrowSize; } set { if(value <= 0) { throw(new ArgumentOutOfRangeException("value", SR.ExceptionAnnotationArrowSizeIsZero)); } if(value > 100) { throw (new ArgumentOutOfRangeException("value", SR.ExceptionAnnotationArrowSizeMustBeLessThen100)); } _arrowSize = value; Invalidate(); } } #endregion // Arrow properties #region Anchor /// /// 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"), Browsable(false), EditorBrowsableAttribute(EditorBrowsableState.Never), DefaultValue(typeof(ContentAlignment), "TopLeft"), SRDescription("DescriptionAttributeAnchorAlignment"), ] override public ContentAlignment AnchorAlignment { get { return base.AnchorAlignment; } set { base.AnchorAlignment = value; } } #endregion // Anchoring #region Other /// /// Gets or sets an annotation's type name. /// /// /// This property is used to get the name of each annotation type /// (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("DescriptionAttributeAnnotationType"), ] public override string AnnotationType { get { return "Arrow"; } } /// /// Gets or sets 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"), ] override internal SelectionPointsStyle SelectionPointsStyle { get { return SelectionPointsStyle.TwoPoints; } } #endregion #endregion #region Methods /// /// Paints annotation object on specified graphics. /// /// /// A used to paint annotation object. /// /// /// Reference to the control. /// override internal void Paint(Chart chart, ChartGraphics graphics) { // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; GetRelativePosition(out firstPoint, out size, out anchorPoint); PointF secondPoint = new PointF(firstPoint.X + size.Width, firstPoint.Y + size.Height); // Create selection rectangle RectangleF selectionRect = new RectangleF(firstPoint, new SizeF(secondPoint.X - firstPoint.X, secondPoint.Y - firstPoint.Y)); // Check if text position is valid if( float.IsNaN(firstPoint.X) || float.IsNaN(firstPoint.Y) || float.IsNaN(secondPoint.X) || float.IsNaN(secondPoint.Y) ) { return; } // Get arrow shape path using (GraphicsPath arrowPathAbs = GetArrowPath(graphics, selectionRect)) { // Draw arrow shape if (this.Common.ProcessModePaint) { graphics.DrawPathAbs( arrowPathAbs, (this.BackColor.IsEmpty) ? Color.White : this.BackColor, this.BackHatchStyle, String.Empty, ChartImageWrapMode.Scaled, Color.Empty, ChartImageAlignmentStyle.Center, this.BackGradientStyle, this.BackSecondaryColor, this.LineColor, this.LineWidth, this.LineDashStyle, PenAlignment.Center, this.ShadowOffset, this.ShadowColor); } // Process hot region if (this.Common.ProcessModeRegions) { // Use callout defined hot region this.Common.HotRegionsList.AddHotRegion( graphics, arrowPathAbs, false, ReplaceKeywords(this.ToolTip), #if Microsoft_CONTROL String.Empty, String.Empty, String.Empty, #else // Microsoft_CONTROL ReplaceKeywords(this.Url), ReplaceKeywords(this.MapAreaAttributes), ReplaceKeywords(this.PostBackValue), #endif // Microsoft_CONTROL this, ChartElementType.Annotation); } // Paint selection handles PaintSelectionHandles(graphics, selectionRect, null); } } /// /// Get arrow path for the specified annotation position /// /// /// /// private GraphicsPath GetArrowPath( ChartGraphics graphics, RectangleF position) { // Get absolute position RectangleF positionAbs = graphics.GetAbsoluteRectangle(position); PointF firstPoint = positionAbs.Location; PointF secondPoint = new PointF(positionAbs.Right, positionAbs.Bottom); // Calculate arrow length float deltaX = secondPoint.X - firstPoint.X; float deltaY = secondPoint.Y - firstPoint.Y; float arrowLength = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); // Create unrotated graphics path for the arrow started at the annotation location // and going to the right for the length of the rotated arrow. GraphicsPath path = new GraphicsPath(); PointF[] points = null; float pointerRatio = 2.1f; if(this.ArrowStyle == ArrowStyle.Simple) { points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize*pointerRatio), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize*pointerRatio) }; } else if(this.ArrowStyle == ArrowStyle.DoubleArrow) { points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize*pointerRatio), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength - this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength - this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize*pointerRatio), new PointF(firstPoint.X + arrowLength, firstPoint.Y), new PointF(firstPoint.X + arrowLength - this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize*pointerRatio), new PointF(firstPoint.X + arrowLength - this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize*pointerRatio) }; } else if(this.ArrowStyle == ArrowStyle.Tailed) { float tailRatio = 2.1f; points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize*pointerRatio), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y - this.ArrowSize*tailRatio), new PointF(firstPoint.X + arrowLength - this.ArrowSize*tailRatio, firstPoint.Y), new PointF(firstPoint.X + arrowLength, firstPoint.Y + this.ArrowSize*tailRatio), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize*pointerRatio, firstPoint.Y + this.ArrowSize*pointerRatio) }; } else { throw (new InvalidOperationException(SR.ExceptionAnnotationArrowStyleUnknown)); } path.AddLines(points); path.CloseAllFigures(); // Calculate arrow angle float angle = (float)(Math.Atan(deltaY / deltaX) * 180f / Math.PI); if(deltaX < 0) { angle += 180f; } // Rotate arrow path around the first point using( Matrix matrix = new Matrix() ) { matrix.RotateAt(angle, firstPoint); path.Transform(matrix); } return path; } #endregion } }