//-------------------------------------------------------------
//
// 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