//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant //================================================================= // File: SubAxis.cs // // Namespace: DataVisualization.Charting // // Classes: SubAxis, SubAxisCollection // // Purpose: Each chart area contains four main axes PrimaryX, // PrimaryY, SecondaryX and SecondaryY which are usually // positioned on each side of the plotting area. Most of // the charts use only two axes; X and Y, but for some // charts even 4 axes is not sufficient. Sub-axes were // introduced to provide unlimited number of axes in // the chart. // // Each main axis has a collection of SubAxis which is // empty by default. By adding SubAxis into this collection // user can add unlimited number of sub-axis which will // be positioned next to the main axis. // // Each of the SubAxis have a unique name. To associate // data series with a sub axis YSubAxisName and XSubAxisName // properties of the Series should be used. // // Reviewed: AG - March 13, 2007 // //=================================================================== #if SUBAXES #region Used namespace using System; using System.Globalization; using System.Reflection; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Data; using System.Drawing; using System.Drawing.Design; using System.Drawing.Text; using System.Drawing.Drawing2D; #if WINFORMS_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; using System.Windows.Forms.DataVisualization.Charting; #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.ChartTypes; #endif #endregion #if WINFORMS_CONTROL namespace System.Windows.Forms.DataVisualization.Charting #else namespace System.Web.UI.DataVisualization.Charting #endif { /// /// SubAxis class is derived from the main Axis class and provides /// additional axis associated with one of the main chart axis. /// [ SRDescription("DescriptionAttributeSubAxis_SubAxis"), DefaultProperty("Enabled"), #if WINFORMS_CONTROL TypeConverter(typeof(SubAxis.SubAxisConverter)), #endif ] #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public class SubAxis : Axis { #region Fields /// /// Sub-Axis parent axis object. /// internal Axis parentAxis = null; /// /// Sub axis offset from the parent axis /// internal double offsetFromParent = 0.0; /// /// Margin between prev. axis /// internal double locationOffset = 0.0; #endregion // Fields #region Constructor /// /// Default constructor /// public SubAxis() : base() { base.Name = string.Empty; } /// /// Object constructor. /// /// Unique name of the object. public SubAxis(string name) : base() { base.Name = name; } #endregion #region Properties /// /// Axis automatic scale breaks style. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), SRCategory("CategoryAttributeScale"), SRDescription("DescriptionAttributeScaleBreakStyle"), TypeConverter(typeof(NoNameExpandableObjectConverter)), NotifyParentPropertyAttribute(true), #if WINFORMS_CONTROL DesignerSerializationVisibility(DesignerSerializationVisibility.Content), #else PersistenceMode(PersistenceMode.InnerProperty), #endif ] override public AxisScaleBreakStyle ScaleBreakStyle { get { return base.ScaleBreakStyle; } set { base.ScaleBreakStyle = value; } } /// /// Sub axis parent axis. /// [ SRCategory("CategoryAttributeAxis"), Bindable(true), Browsable(false), DefaultValue(null), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeSubAxis_ParentAxis"), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden) ] public Axis ParentAxis { get { return this.parentAxis; } } /// /// Sub axis location offset relative to the previous axis. /// [ SRCategory("CategoryAttributeLocation"), Bindable(true), DefaultValue(0.0), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeSubAxis_LocationOffset"), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif ] public double LocationOffset { get { return this.locationOffset; } set { this.locationOffset = value; this.Invalidate(); } } /// /// Axis position /// [ Bindable(true), Browsable(false), DefaultValue(AxisPosition.Left), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeReverse"), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden) ] override internal AxisPosition AxisPosition { get { if(this.parentAxis != null) { return this.parentAxis.AxisPosition; } return AxisPosition.Left; } set { } } /// /// SubAxis name. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), Browsable(true), DefaultValue(""), SRDescription("DescriptionAttributeSubAxis_Name"), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible), SerializationVisibilityAttribute(SerializationVisibility.Attribute) ] override public string Name { get { return base.Name; } set { base.Name = value; } } /// /// Tick marks and labels move with axis when /// the crossing value is changed. /// [ SRCategory("CategoryAttributeAppearance"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(true), DefaultValue(true), SRDescription("DescriptionAttributeMarksNextToAxis"), NotifyParentPropertyAttribute(true), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden) ] override public bool IsMarksNextToAxis { get { return base.IsMarksNextToAxis; } set { base.IsMarksNextToAxis = value; } } /// /// Point where axis is crossed by another axis. /// [ SRCategory("CategoryAttributeScale"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(true), DefaultValue(Double.NaN), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeCrossing"), #if !WINFORMS_CONTROL PersistenceMode(PersistenceMode.Attribute), #endif TypeConverter(typeof(AxisCrossingValueConverter)), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden) ] override public double Crossing { get { return base.Crossing; } set { base.Crossing = value; } } /// /// Sub-axes collection. /// [ SRCategory("CategoryAttributeSubAxes"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(true), SRDescription("DescriptionAttributeSubAxes"), Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden), ] override public SubAxisCollection SubAxes { get { return base.SubAxes; } } /// /// Indicates if this axis object present the main or sub axis. /// override internal bool IsSubAxis { get { return true; } } /// /// Returns sub-axis name. /// override internal string SubAxisName { get { return base.Name; } } #endregion // Properties #region Methods /// /// Find axis position using crossing value. /// /// Axis crossing should be ignored. /// Relative position override internal double GetAxisPosition(bool ignoreCrossing) { // Parent axis must be set if(this.parentAxis != null) { // Get position of the parent axis double position = this.parentAxis.GetAxisPosition(ignoreCrossing); // Addjust parent position by the offset if(this.parentAxis.AxisPosition == AxisPosition.Left) { position -= this.offsetFromParent; } else if(this.parentAxis.AxisPosition == AxisPosition.Right) { position += this.offsetFromParent; } else if(this.parentAxis.AxisPosition == AxisPosition.Top) { position -= this.offsetFromParent; } else if(this.parentAxis.AxisPosition == AxisPosition.Bottom) { position += this.offsetFromParent; } return position; } return 0.0; } #endregion // Methods #region Type converter #if WINFORMS_CONTROL internal class SubAxisConverter : TypeConverter { /// /// This method overrides CanConvertTo from TypeConverter. This is called when someone /// wants to convert an instance of object to another type. Here, /// only conversion to an InstanceDescriptor is supported. /// /// Descriptor context. /// Destination type. /// True if object can be converted. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { return true; } // Always call the base to see if it can perform the conversion. return base.CanConvertTo(context, destinationType); } /// /// This code performs the actual conversion from an object to an InstanceDescriptor. /// /// Descriptor context. /// Culture information. /// Object value. /// Destination type. /// Converted object. public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo ci = typeof(SubAxis).GetConstructor(System.Type.EmptyTypes); return new InstanceDescriptor(ci, null, false); } // Always call base, even if you can't convert. return base.ConvertTo(context, culture, value, destinationType); } } #endif //#if WINFORMS_CONTROL #endregion } /// /// SubAxisCollection is a strongly typed collection of chart sub-axes objects. /// Collection indexer can accept sub-axis index or it's unique name as a parameter. /// [ SRDescription("DescriptionAttributeSubAxisCollection_SubAxisCollection"), ] #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public class SubAxisCollection : CollectionBase { #region Fields /// /// Sub-Axis parent axis object. /// internal Axis parentAxis = null; #endregion #region Construction and Initialization /// /// Default public constructor. /// /// /// This constructor is for internal use and should not be part of documentation. /// public SubAxisCollection() { this.parentAxis = null; } /// /// Public constructor. /// /// /// Chart object. /// /// /// This constructor is for the internal use and should not be part of documentation. /// internal SubAxisCollection(Axis parentAxis) { this.parentAxis = parentAxis; } #endregion #region Indexer /// /// SubAxis collection indexer. /// /// /// The SubAxis object's name or index can be provided as a parameter. Returns the object. /// Make sure to cast the SubAxis to it's type (e.g. LineSubAxis) to access type /// specific properties. /// [ SRDescription("DescriptionAttributeSubAxisCollection_Item"), ] public SubAxis this[object parameter] { get { // Get SubAxis by index if(parameter is int) { return (SubAxis)this.List[(int)parameter]; } // Get SubAxis by name else if(parameter is string) { // Find SubAxis with specified name foreach(SubAxis SubAxis in this.List) { if(SubAxis.Name == (string)parameter) { return SubAxis; } } // SubAxis with specified name was not found throw(new ArgumentException( SR.ExceptionSubAxisNameNotFound( (string)parameter ) ) ); } // Invalid type of the indexer argument throw(new ArgumentException(SR.ExceptionInvalidIndexerArgumentType)); } set { // Check new SubAxis name int indexSubAxis = -1; if(value.Name.Length != 0) { indexSubAxis = this.List.IndexOf(value); } else { AssignUniqueName(value); } // Set using index in the collection if(parameter is int) { // Check if SubAxis with this name already exists if( indexSubAxis != -1 && indexSubAxis != (int)parameter) { throw( new ArgumentException( SR.ExceptionSubAxisNameAlreadyExistsInCollection( value.Name ) ) ); } this.List[(int)parameter] = value; } // Set using name in the collection else if(parameter is string) { // Find legend with specified name int index = 0; foreach(SubAxis SubAxis in this.List) { if(SubAxis.Name == (string)parameter) { // Check if SubAxis with this name already exists if( indexSubAxis != -1 && indexSubAxis != index) { throw( new ArgumentException( SR.ExceptionSubAxisNameAlreadyExistsInCollection( value.Name ) ) ); } this.List[index] = value; break; } ++index; } } else { throw(new ArgumentException(SR.ExceptionInvalidIndexerArgumentType)); } this.Invalidate(); } } #endregion #region Collection Add and Insert methods /// /// Removes the SubAxis with the specified name from the collection. /// /// /// Name of the SubAxis to be removed. /// public void Remove(string name) { SubAxis axis = FindByName(name); if(axis != null) { this.List.Remove(axis); } } /// /// Removes the given SubAxis from the collection. /// /// /// object to be removed. /// public void Remove(SubAxis SubAxis) { if(SubAxis != null) { this.List.Remove(SubAxis); } } /// /// Adds a SubAxis to the end of the collection. /// /// /// object to add. /// /// /// Index of the newly added object. /// public int Add(SubAxis SubAxis) { return this.List.Add(SubAxis); } /// /// Inserts a SubAxis into the collection. /// /// /// Index to insert the object at. /// /// /// object to insert. /// public void Insert(int index, SubAxis SubAxis) { this.List.Insert(index, SubAxis); } #endregion #region Items Inserting and Removing Notification methods /// /// Called before the new item is inserted. /// /// Item index. /// Item object. /// /// This is an internal method and should not be part of the documentation. /// protected override void OnInsert(int index, object value) { // Check SubAxis object name if( ((SubAxis)value).Name.Length == 0 ) { AssignUniqueName((SubAxis)value); } else { if(this.FindByName(((SubAxis)value).Name) != null) { throw(new InvalidOperationException(SR.ExceptionSubAxisNameIsNotUnique( ((SubAxis)value).Name ))); } } } /// /// After new item inserted. /// /// Item index. /// Item object. /// /// This is an internal method and should not be part of the documentation. /// protected override void OnInsertComplete(int index, object value) { // Set SubAxis parent axis reference SubAxis subAxis = (SubAxis)value; subAxis.parentAxis = this.parentAxis; if(this.parentAxis != null) { subAxis.chart = this.parentAxis.chart; subAxis.Common = this.parentAxis.Common; subAxis.chartArea = this.parentAxis.chartArea; subAxis.axisType= this.parentAxis.axisType; subAxis.AxisPosition= this.parentAxis.AxisPosition; } this.Invalidate(); } /// /// After item removed. /// /// Item index. /// Item object. /// /// This is an internal method and should not be part of the documentation. /// protected override void OnRemoveComplete(int index, object value) { // Reset SubAxis parent axis reference ((SubAxis)value).parentAxis = null; this.Invalidate(); } /// /// After all items removed. /// /// /// This is an internal method and should not be part of the documentation. /// protected override void OnClearComplete() { this.Invalidate(); } #endregion #region Helper Methods /// /// Invalidates chart the collection belongs to. /// private void Invalidate() { #if WINFORMS_CONTROL if(this.parentAxis != null && this.parentAxis.chart != null) { this.parentAxis.chart.dirtyFlag = true; this.parentAxis.chart.Invalidate(); } #endif } /// /// Assigns a unique name to the SubAxis object based on it's type. /// /// SubAxis object to be named. internal void AssignUniqueName(SubAxis SubAxis) { // Generate name using SubAxis type name and unique index string name = string.Empty; int index = 1; do { name = "SubAxis" + index.ToString(); ++index; } while(this.FindByName(name) != null && index < 10000 ); // Asign unique name; SubAxis.Name = name; } /// /// Finds SubAxis by name. /// /// Name of the chart SubAxis. /// SubAxis or null if it does not exist. internal SubAxis FindByName(string name) { SubAxis result = null; for(int index = 0; index < this.List.Count; index ++) { // Compare SubAxis name if(String.Compare(this[index].Name, name, true, System.Globalization.CultureInfo.CurrentCulture) == 0) { result = this[index]; break; } } return result; } #endregion } } #endif // SUBAXES