diff options
Diffstat (limited to 'mcs/class/referencesource/System.Web.DataVisualization/Common/General/Chart.cs')
-rw-r--r-- | mcs/class/referencesource/System.Web.DataVisualization/Common/General/Chart.cs | 4361 |
1 files changed, 4361 insertions, 0 deletions
diff --git a/mcs/class/referencesource/System.Web.DataVisualization/Common/General/Chart.cs b/mcs/class/referencesource/System.Web.DataVisualization/Common/General/Chart.cs new file mode 100644 index 00000000000..a83a2fb7137 --- /dev/null +++ b/mcs/class/referencesource/System.Web.DataVisualization/Common/General/Chart.cs @@ -0,0 +1,4361 @@ +//------------------------------------------------------------- +// <copyright company=’Microsoft Corporation’> +// Copyright © Microsoft Corporation. All Rights Reserved. +// </copyright> +//------------------------------------------------------------- +// @owner=alexgor, deliant +//================================================================= +// File: Chart.cs +// +// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting +// +// Classes: ChartImage, ChartPicture, ChartPaintEventArgs +// +// Purpose: This file contains classes, which are used for Image +// creation and chart painting. This file has also a +// class, which is used for Paint events arguments. +// +// Reviewed: GS - August 2, 2002 +// AG - August 8, 2002 +// AG - Microsoft 16, 2007 +// +//=================================================================== + +#region Used Namespaces + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Design; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Resources; +using System.Reflection; +using System.IO; +using System.Data; +using System.Collections; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.Xml; +using System.Globalization; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; +using System.Security; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +#if Microsoft_CONTROL + + 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.Net; + using System.Web.UI.DataVisualization.Charting; + using System.Web.UI.DataVisualization.Charting.Data; + using System.Web.UI.DataVisualization.Charting.ChartTypes; + 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 Enumerations + +#if !Microsoft_CONTROL + + /// <summary> + /// An enumeration of supported image types + /// </summary> + public enum ChartImageType + { + /// <summary> + /// BMP image format + /// </summary> + Bmp, + /// <summary> + /// Jpeg image format + /// </summary> + Jpeg, + + /// <summary> + /// Png image format + /// </summary> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Png")] + Png, + + /// <summary> + /// Enhanced Meta File (Emf) image format. + /// </summary> + Emf, + + }; +#endif + + + #endregion + + /// <summary> + /// ChartImage class adds image type and data binding functionality to + /// the base ChartPicture class. + /// </summary> + internal class ChartImage : ChartPicture + { + #region Fields + + // Private data members, which store properties values + private int _compression = 0; + + // Chart data source object + private object _dataSource = null; + + // Indicates that control was bound to the data source + internal bool boundToDataSource = false; + +#if !Microsoft_CONTROL + private ChartImageType imageType = ChartImageType.Png; +#endif + + #endregion + + #region Constructor + + /// <summary> + /// Chart internal constructor. + /// </summary> + /// <param name="container">Service container</param> + internal ChartImage(IServiceContainer container) + : base(container) + { + } + + #endregion // Constructor + + #region Properties + + /// <summary> + /// Gets or sets the data source for the Chart object. + /// </summary> + [ + SRCategory("CategoryAttributeData"), + Bindable(true), + SRDescription("DescriptionAttributeDataSource"), + DefaultValue(null), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SerializationVisibilityAttribute(SerializationVisibility.Hidden) + ] + public object DataSource + { + get + { + return _dataSource; + } + set + { + if(_dataSource != value) + { + _dataSource = value; + this.boundToDataSource = false; + } + } + } + +#if !Microsoft_CONTROL + + /// <summary> + /// Image type (Jpeg, BMP, Png) + /// </summary> + [ + SRCategory("CategoryAttributeImage"), + Bindable(true), + DefaultValue(ChartImageType.Png), + SRDescription("DescriptionAttributeImageType"), + PersistenceMode(PersistenceMode.Attribute) + ] + public ChartImageType ImageType + { + get + { + return imageType; + } + set + { + imageType = value; + } + } + +#endif + + /// <summary> + /// Image compression value + /// </summary> + [ + SRCategory("CategoryAttributeImage"), + Bindable(true), + DefaultValue(0), + SRDescription("DescriptionAttributeChartImage_Compression"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public int Compression + { + get + { + return _compression; + } + set + { + if(value < 0 || value > 100) + { + throw (new ArgumentOutOfRangeException("value", SR.ExceptionChartCompressionInvalid)); + } + _compression = value; + } + } + + #endregion + + #region Methods + + #region Image Manipulation + + + /// <summary> + /// Saves image into the metafile stream. + /// </summary> + /// <param name="imageStream">Image stream.</param> + /// <param name="emfType">Image stream.</param> + [SecuritySafeCritical] + public void SaveIntoMetafile(Stream imageStream, EmfType emfType) + { + // Check arguments + if (imageStream == null) + throw new ArgumentNullException("imageStream"); + + // Create temporary Graphics object for metafile + using (Bitmap bitmap = new Bitmap(this.Width, this.Height)) + { + using (Graphics newGraphics = Graphics.FromImage(bitmap)) + { + IntPtr hdc = IntPtr.Zero; + try + { + System.Security.Permissions.SecurityPermission securityPermission = new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode); + securityPermission.Demand(); + + hdc = newGraphics.GetHdc(); + + + // Create metafile object to record. + using (Metafile metaFile = new Metafile( + imageStream, + hdc, + new Rectangle(0, 0, this.Width, this.Height), + MetafileFrameUnit.Pixel, + emfType)) + { + + // Create graphics object to record metaFile. + using (Graphics metaGraphics = Graphics.FromImage(metaFile)) + { + + // Note: Fix for issue #3674. Some 3D borders shadows may be drawn outside + // of image boundaries. This causes issues when generated EMF file + // is placed in IE. Image looks shifted down and hot areas do not align. + if (this.BorderSkin.SkinStyle != BorderSkinStyle.None) + { + metaGraphics.Clip = new Region(new Rectangle(0, 0, this.Width, this.Height)); + } + + // Draw chart in the metafile + this.ChartGraph.IsMetafile = true; + this.Paint(metaGraphics, false); + this.ChartGraph.IsMetafile = false; + + } + } + } + finally + { + if (hdc != IntPtr.Zero) + { + newGraphics.ReleaseHdc(hdc); + } + } + } + } + } + + public Bitmap GetImage() + { + return this.GetImage(96); + } + /// <summary> + /// Create Image and draw chart picture + /// </summary> + public Bitmap GetImage(float resolution) + { + // Create a new bitmap + + Bitmap image = null; + + while (image == null) + { + bool failed = true; + try + { + image = new Bitmap(Math.Max(1,Width), Math.Max(1,Height)); + image.SetResolution(resolution, resolution); + failed = false; + } + catch (ArgumentException) + { + failed = true; + } + catch (OverflowException) + { + failed = true; + } + catch (InvalidOperationException) + { + failed = true; + } + catch (ExternalException) + { + failed = true; + } + + if (failed) + { + // if failed to create the image, decrease the size and the resolution of the chart + image = null; + float newResolution = Math.Max(resolution / 2, 96); + Width = (int)Math.Ceiling(Width * newResolution / resolution); + Height = (int)Math.Ceiling(Height * newResolution / resolution); + resolution = newResolution; + } + } + + // Creates a new Graphics object from the + // specified Image object. + Graphics offScreen = Graphics.FromImage( image ); + + + + Color backGroundColor; + + if (this.BackColor != Color.Empty) + backGroundColor = this.BackColor; + else + backGroundColor = Color.White; + + // Get the page color if border skin is visible. + if (GetBorderSkinVisibility() && + this.BorderSkin.PageColor != Color.Empty) + { + backGroundColor = this.BorderSkin.PageColor; + } + + // draw a rctangle first with the size of the control, this prevent strange behavior when printing in the reporting services, + // without this rectangle, the printed picture is blurry + Pen pen = new Pen(backGroundColor); + offScreen.DrawRectangle(pen, 0, 0, Width, Height); + pen.Dispose(); + + // Paint the chart + Paint( offScreen , false); + + // Dispose Graphic object + offScreen.Dispose(); + + // Return reference to the image + return image; + } + + #endregion // Image Manipulation + + #region Data Binding + + /// <summary> + /// Checks if the type of the data source is valid. + /// </summary> + /// <param name="dataSource">Data source object to test.</param> + /// <returns>True if valid data source object.</returns> + static internal bool IsValidDataSource(object dataSource) + { + if( null != dataSource && + ( + dataSource is IEnumerable || + dataSource is DataSet || + dataSource is DataView || + dataSource is DataTable || + dataSource is System.Data.OleDb.OleDbCommand || + dataSource is System.Data.SqlClient.SqlCommand || + dataSource is System.Data.OleDb.OleDbDataAdapter || + dataSource is System.Data.SqlClient.SqlDataAdapter || + // ADDED: for VS2005 compatibility, DT Nov 25, 2005 + dataSource.GetType().GetInterface("IDataSource") != null + // END ADDED + ) + ) + { + return true; + } + + return false; + } + + + + /// <summary> + /// Gets an list of the data source member names. + /// </summary> + /// <param name="dataSource">Data source object to get the members for.</param> + /// <param name="usedForYValue">Indicates that member will be used for Y values.</param> + /// <returns>List of member names.</returns> + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", + Justification = "Too large of a code change to justify making this change")] + static internal ArrayList GetDataSourceMemberNames(object dataSource, bool usedForYValue) + { + ArrayList names = new ArrayList(); + if (dataSource != null) + { + // ADDED: for VS2005 compatibility, DT Nov 25, 2004 + if (dataSource.GetType().GetInterface("IDataSource") != null) + { + try + { + MethodInfo m = dataSource.GetType().GetMethod("Select"); + if (m != null) + { + if (m.GetParameters().Length == 1) + { + // SQL derived datasource + Type selectArgsType = dataSource.GetType().Assembly.GetType("System.Web.UI.DataSourceSelectArguments", true); + ConstructorInfo ci = selectArgsType.GetConstructor(new Type[] { }); + dataSource = m.Invoke(dataSource, new object[] { ci.Invoke(new object[] { }) }); + } + else + { + // object data source + dataSource = m.Invoke(dataSource, new object[] { }); + } + } + } + catch (TargetException) + { + } + catch (TargetInvocationException) + { + } + } + // END ADDED + + // Check all DataTable based data souces + DataTable dataTable = null; + + if (dataSource is DataTable) + { + dataTable = (DataTable)dataSource; + } + else if (dataSource is DataView) + { + dataTable = ((DataView)dataSource).Table; + } + else if (dataSource is DataSet && ((DataSet)dataSource).Tables.Count > 0) + { + dataTable = ((DataSet)dataSource).Tables[0]; + } + else if (dataSource is System.Data.OleDb.OleDbDataAdapter) + { + dataTable = new DataTable(); + dataTable.Locale = CultureInfo.CurrentCulture; + dataTable = ((System.Data.OleDb.OleDbDataAdapter)dataSource).FillSchema(dataTable, SchemaType.Mapped); + } + else if (dataSource is System.Data.SqlClient.SqlDataAdapter) + { + dataTable = new DataTable(); + dataTable.Locale = CultureInfo.CurrentCulture; + dataTable = ((System.Data.SqlClient.SqlDataAdapter)dataSource).FillSchema(dataTable, SchemaType.Mapped); + } + else if (dataSource is System.Data.OleDb.OleDbDataReader) + { + // Add table columns names + for (int fieldIndex = 0; fieldIndex < ((System.Data.OleDb.OleDbDataReader)dataSource).FieldCount; fieldIndex++) + { + if (!usedForYValue || ((System.Data.OleDb.OleDbDataReader)dataSource).GetFieldType(fieldIndex) != typeof(string)) + { + names.Add(((System.Data.OleDb.OleDbDataReader)dataSource).GetName(fieldIndex)); + } + } + } + else if (dataSource is System.Data.SqlClient.SqlDataReader) + { + // Add table columns names + for (int fieldIndex = 0; fieldIndex < ((System.Data.SqlClient.SqlDataReader)dataSource).FieldCount; fieldIndex++) + { + if (!usedForYValue || ((System.Data.SqlClient.SqlDataReader)dataSource).GetFieldType(fieldIndex) != typeof(string)) + { + names.Add(((System.Data.SqlClient.SqlDataReader)dataSource).GetName(fieldIndex)); + } + } + } + else if (dataSource is System.Data.OleDb.OleDbCommand) + { + System.Data.OleDb.OleDbCommand command = (System.Data.OleDb.OleDbCommand)dataSource; + if (command.Connection != null) + { + command.Connection.Open(); + System.Data.OleDb.OleDbDataReader dataReader = command.ExecuteReader(); + if (dataReader.Read()) + { + for (int fieldIndex = 0; fieldIndex < dataReader.FieldCount; fieldIndex++) + { + if (!usedForYValue || dataReader.GetFieldType(fieldIndex) != typeof(string)) + { + names.Add(dataReader.GetName(fieldIndex)); + } + } + } + + dataReader.Close(); + command.Connection.Close(); + } + } + else if (dataSource is System.Data.SqlClient.SqlCommand) + { + System.Data.SqlClient.SqlCommand command = (System.Data.SqlClient.SqlCommand)dataSource; + if (command.Connection != null) + { + command.Connection.Open(); + System.Data.SqlClient.SqlDataReader dataReader = command.ExecuteReader(); + if (dataReader.Read()) + { + for (int fieldIndex = 0; fieldIndex < dataReader.FieldCount; fieldIndex++) + { + if (!usedForYValue || dataReader.GetFieldType(fieldIndex) != typeof(string)) + { + names.Add(dataReader.GetName(fieldIndex)); + } + } + } + + dataReader.Close(); + command.Connection.Close(); + } + } + + + // Check if DataTable was set + if (dataTable != null) + { + // Add table columns names + foreach (DataColumn column in dataTable.Columns) + { + if (!usedForYValue || column.DataType != typeof(string)) + { + names.Add(column.ColumnName); + } + } + } + + else if (names.Count == 0 && dataSource is ITypedList) + { + foreach (PropertyDescriptor pd in ((ITypedList)dataSource).GetItemProperties(null)) + { + if (!usedForYValue || pd.PropertyType != typeof(string)) + { + names.Add(pd.Name); + } + } + } + else if (names.Count == 0 && dataSource is IEnumerable) + { + // .Net 2.0 ObjectDataSource processing + IEnumerator e = ((IEnumerable)dataSource).GetEnumerator(); + e.Reset(); + e.MoveNext(); + foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(e.Current)) + { + if (!usedForYValue || pd.PropertyType != typeof(string)) + { + names.Add(pd.Name); + } + + } + } + + + + // Check if list still empty + if (names.Count == 0) + { + // Add first column or any data member name + names.Add("0"); + } + + } + + return names; + } + + /// <summary> + /// Data binds control to the data source + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", + Justification="Too large of a code change to justify making this change")] + internal void DataBind() + { + // Set bound flag + this.boundToDataSource = true; + + object dataSource = this.DataSource; + if (dataSource != null) + { + + // Convert data adapters to command object + if (dataSource is System.Data.OleDb.OleDbDataAdapter) + { + dataSource = ((System.Data.OleDb.OleDbDataAdapter)dataSource).SelectCommand; + } + else if (dataSource is System.Data.SqlClient.SqlDataAdapter) + { + dataSource = ((System.Data.SqlClient.SqlDataAdapter)dataSource).SelectCommand; + } + + // Convert data source to recognizable source for the series + if (dataSource is DataSet && ((DataSet)dataSource).Tables.Count > 0) + { + dataSource = ((DataSet)dataSource).DefaultViewManager.CreateDataView(((DataSet)dataSource).Tables[0]); + + } + else if (dataSource is DataTable) + { + dataSource = new DataView((DataTable)dataSource); + } + else if (dataSource is System.Data.OleDb.OleDbCommand) + { + System.Data.OleDb.OleDbCommand command = (System.Data.OleDb.OleDbCommand)dataSource; + command.Connection.Open(); + System.Data.OleDb.OleDbDataReader dataReader = command.ExecuteReader(); + + this.DataBind(dataReader, null); + + dataReader.Close(); + command.Connection.Close(); + return; + } + else if (dataSource is System.Data.SqlClient.SqlCommand) + { + System.Data.SqlClient.SqlCommand command = (System.Data.SqlClient.SqlCommand)dataSource; + command.Connection.Open(); + System.Data.SqlClient.SqlDataReader dataReader = command.ExecuteReader(); + + this.DataBind(dataReader, null); + + dataReader.Close(); + command.Connection.Close(); + return; + } + else if (dataSource is IList) + { + dataSource = dataSource as IList; + } + else if (dataSource is IListSource ) + { + if (((IListSource)dataSource).ContainsListCollection && ((IListSource)dataSource).GetList().Count > 0) + { + dataSource = ((IListSource)dataSource).GetList()[0] as IEnumerable; + } + else + { + dataSource = ((IListSource)dataSource).GetList(); + } + } + else + { + dataSource = dataSource as IEnumerable; + } + + // Data bind + DataBind(dataSource as IEnumerable, null); + } + } + + /// <summary> + /// Data binds control to the data source + /// </summary> + /// <param name="dataSource">Data source to bind to.</param> + /// <param name="seriesList">List of series to bind.</param> + internal void DataBind(IEnumerable dataSource, ArrayList seriesList) + { + // Data bind series + if(dataSource != null && this.Common != null) + { + //************************************************************ + //** If list of series is not provided - bind all of them. + //************************************************************ + if(seriesList == null) + { + seriesList = new ArrayList(); + foreach(Series series in this.Common.Chart.Series) + { + // note: added for design time data binding + if (this.Common.Chart.IsDesignMode()) + { + if (series.YValueMembers.Length > 0) + { + seriesList.Add(series); + } + } + else + { + seriesList.Add(series); + } + } + } + + //************************************************************ + //** Clear all data points in data bound series + //************************************************************ + foreach(Series series in seriesList) + { + if(series.XValueMember.Length > 0 || series.YValueMembers.Length > 0) + { + series.Points.Clear(); + } + } + + //************************************************************ + //** Get and reset data enumerator. + //************************************************************ + IEnumerator enumerator = dataSource.GetEnumerator(); + if(enumerator.GetType() != typeof(System.Data.Common.DbEnumerator) ) + { + try + { + enumerator.Reset(); + } + // Some enumerators may not support Resetting + catch (InvalidOperationException) + { + } + catch (NotImplementedException) + { + } + catch (NotSupportedException) + { + } + } + + + //************************************************************ + //** Loop through the enumerator. + //************************************************************ + bool valueExsists = true; + bool autoDetectType = true; + do + { + // Move to the next item + valueExsists = enumerator.MoveNext(); + + // Loop through all series + foreach(Series series in seriesList) + { + if(series.XValueMember.Length > 0 || series.YValueMembers.Length > 0) + { + //************************************************************ + //** Check and convert fields names. + //************************************************************ + + // Convert comma separated field names string to array of names + string[] yFieldNames = null; + if(series.YValueMembers.Length > 0) + { + yFieldNames = series.YValueMembers.Replace(",,", "\n").Split(','); + for(int index = 0; index < yFieldNames.Length; index++) + { + yFieldNames[index] = yFieldNames[index].Replace("\n", ",").Trim(); + } + } + + // Double check that a string object is not provided for data binding + if(dataSource is string) + { + throw (new ArgumentException(SR.ExceptionDataBindYValuesToString, "dataSource")); + } + + // Check number of fields + if(yFieldNames == null || yFieldNames.GetLength(0) > series.YValuesPerPoint) + { + throw(new ArgumentOutOfRangeException("dataSource", SR.ExceptionDataPointYValuesCountMismatch(series.YValuesPerPoint.ToString(System.Globalization.CultureInfo.InvariantCulture) ) ) ); + } + + //************************************************************ + //** Create new data point. + //************************************************************ + if(valueExsists) + { + // Auto detect values type + if(autoDetectType) + { + autoDetectType = false; + + // Make sure Y field is not empty + string yField = yFieldNames[0]; + int fieldIndex = 1; + while(yField.Length == 0 && fieldIndex < yFieldNames.Length) + { + yField = yFieldNames[fieldIndex++]; + } + + DataPointCollection.AutoDetectValuesType(series, enumerator, series.XValueMember.Trim(), enumerator, yField); + } + + + // Create new point + DataPoint newDataPoint = new DataPoint(series); + bool emptyValues = false; + bool xValueIsNull = false; + + //************************************************************ + //** Get new point X and Y values. + //************************************************************ + object[] yValuesObj = new object[yFieldNames.Length]; + object xValueObj = null; + + // Set X to the value provided or use sequence numbers starting with 1 + if(series.XValueMember.Length > 0) + { + xValueObj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, series.XValueMember.Trim()); + if(xValueObj is System.DBNull || xValueObj == null) + { + xValueIsNull = true; + emptyValues = true; + xValueObj = 0.0; + } + } + + if(yFieldNames.Length == 0) + { + yValuesObj[0] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, null); + if(yValuesObj[0] is System.DBNull || yValuesObj[0] == null) + { + emptyValues = true; + yValuesObj[0] = 0.0; + } + } + else + { + for(int i = 0; i < yFieldNames.Length; i++) + { + if(yFieldNames[i].Length > 0) + { + yValuesObj[i] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, yFieldNames[i]); + if(yValuesObj[i] is System.DBNull || yValuesObj[i] == null) + { + emptyValues = true; + yValuesObj[i] = 0.0; + } + } + else + { + yValuesObj[i] = (((Series)seriesList[0]).IsYValueDateTime()) ? DateTime.Now.Date.ToOADate() : 0.0; + } + } + } + + + // Add data point if X value is not Null + if(!xValueIsNull) + { + if(emptyValues) + { + if(xValueObj != null) + { + newDataPoint.SetValueXY(xValueObj, yValuesObj); + } + else + { + newDataPoint.SetValueXY(0, yValuesObj); + } + series.Points.DataPointInit(ref newDataPoint); + newDataPoint.IsEmpty = true; + series.Points.Add(newDataPoint); + } + else + { + if(xValueObj != null) + { + newDataPoint.SetValueXY(xValueObj, yValuesObj); + } + else + { + newDataPoint.SetValueXY(0, yValuesObj); + } + series.Points.DataPointInit(ref newDataPoint); + series.Points.Add(newDataPoint); + } + } + if (this.Common.Chart.IsDesignMode()) + { + series["TempDesignData"] = "true"; + } + } + } + } + + } while(valueExsists); + + } + } + + + /// <summary> + /// Aligns data points using their axis labels. + /// </summary> + /// <param name="sortAxisLabels">Indicates if points should be sorted by axis labels.</param> + /// <param name="sortingOrder">Sorting pointSortOrder.</param> + internal void AlignDataPointsByAxisLabel(bool sortAxisLabels, PointSortOrder sortingOrder) + { + // Find series which are attached to the same X axis in the same chart area + foreach(ChartArea chartArea in this.ChartAreas) + { + + // Check if chart area is visible + if(chartArea.Visible) + + { + // Create series list for primary and secondary X axis + ArrayList chartAreaSeriesPrimary = new ArrayList(); + ArrayList chartAreaSeriesSecondary = new ArrayList(); + foreach(Series series in this.Common.Chart.Series) + { + // Check if series belongs to the chart area + if (series.ChartArea == chartArea.Name) + { + if(series.XSubAxisName.Length == 0) + { + if(series.XAxisType == AxisType.Primary) + { + chartAreaSeriesPrimary.Add(series); + } + else + { + chartAreaSeriesSecondary.Add(series); + } + } + } + } + + // Align series + AlignDataPointsByAxisLabel(chartAreaSeriesPrimary, sortAxisLabels, sortingOrder); + AlignDataPointsByAxisLabel(chartAreaSeriesSecondary, sortAxisLabels, sortingOrder); + } + } + } + + /// <summary> + /// Aligns data points using their axis labels. + /// </summary> + /// <param name="seriesList">List of series to align.</param> + /// <param name="sortAxisLabels">Indicates if points should be sorted by axis labels.</param> + /// <param name="sortingOrder">Sorting order.</param> + internal void AlignDataPointsByAxisLabel( + ArrayList seriesList, + bool sortAxisLabels, + PointSortOrder sortingOrder) + { + // List is empty + if(seriesList.Count == 0) + { + return; + } + + // Collect information about all points in all series + bool indexedX = true; + bool uniqueAxisLabels = true; + ArrayList axisLabels = new ArrayList(); + foreach(Series series in seriesList) + { + ArrayList seriesAxisLabels = new ArrayList(); + foreach(DataPoint point in series.Points) + { + // Check if series has indexed X values + if(!series.IsXValueIndexed && point.XValue != 0.0) + { + indexedX = false; + break; + } + + // Add axis label to the list and make sure it's non-empty and unique + if(point.AxisLabel.Length == 0) + { + uniqueAxisLabels = false; + break; + } + else if(seriesAxisLabels.Contains(point.AxisLabel)) + { + uniqueAxisLabels = false; + break; + } + else if(!axisLabels.Contains(point.AxisLabel)) + { + axisLabels.Add(point.AxisLabel); + } + + seriesAxisLabels.Add(point.AxisLabel); + } + } + + // Sort axis labels + if(sortAxisLabels) + { + axisLabels.Sort(); + if(sortingOrder == PointSortOrder.Descending) + { + axisLabels.Reverse(); + } + } + + // All series must be indexed + if(!indexedX) + { + throw (new InvalidOperationException(SR.ExceptionChartDataPointsAlignmentFaild)); + } + + // AxisLabel can't be empty or duplicated + if(!uniqueAxisLabels) + { + throw (new InvalidOperationException(SR.ExceptionChartDataPointsAlignmentFaildAxisLabelsInvalid)); + } + + // Assign unique X values for data points in all series with same axis LabelStyle + if(indexedX && uniqueAxisLabels) + { + foreach(Series series in seriesList) + { + foreach(DataPoint point in series.Points) + { + point.XValue = axisLabels.IndexOf(point.AxisLabel) + 1; + } + + // Sort points by X value + series.Sort(PointSortOrder.Ascending, "X"); + } + + // Make sure ther are no missing points + foreach(Series series in seriesList) + { + series.IsXValueIndexed = true; + for(int index = 0; index < axisLabels.Count; index++) + { + if(index >= series.Points.Count || + series.Points[index].XValue != index + 1) + { + DataPoint newPoint = new DataPoint(series); + newPoint.AxisLabel = (string)axisLabels[index]; + newPoint.XValue = index + 1; + newPoint.YValues[0] = 0.0; + newPoint.IsEmpty = true; + series.Points.Insert(index, newPoint); + } + } + } + + } + + } + + /// <summary> + /// Data bind chart to the table. Series will be automatically added to the chart depending on + /// the number of unique values in the seriesGroupByField column of the data source. + /// Data source can be the Ole(SQL)DataReader, DataView, DataSet, DataTable or DataRow. + /// </summary> + /// <param name="dataSource">Data source.</param> + /// <param name="seriesGroupByField">Name of the field used to group data into series.</param> + /// <param name="xField">Name of the field for X values.</param> + /// <param name="yFields">Comma separated name(s) of the field(s) for Y value(s).</param> + /// <param name="otherFields">Other point properties binding rule in format: PointProperty=Field[{Format}] [,PointProperty=Field[{Format}]]. For example: "Tooltip=Price{C1},Url=WebSiteName".</param> + /// <param name="sort">Indicates that series should be sorted by group field.</param> + /// <param name="sortingOrder">Series sorting order by group field.</param> + internal void DataBindCrossTab( + IEnumerable dataSource, + string seriesGroupByField, + string xField, + string yFields, + string otherFields, + bool sort, + PointSortOrder sortingOrder) + { + // Check arguments + if (dataSource == null) + throw (new ArgumentNullException("dataSource", SR.ExceptionDataPointInsertionNoDataSource)); + + if (dataSource is string) + throw (new ArgumentException(SR.ExceptionDataBindSeriesToString, "dataSource")); + + if (String.IsNullOrEmpty(yFields)) + throw (new ArgumentException(SR.ExceptionChartDataPointsInsertionFailedYValuesEmpty, "yFields")); + + if (String.IsNullOrEmpty(seriesGroupByField)) + throw (new ArgumentException(SR.ExceptionDataBindSeriesGroupByParameterIsEmpty, "seriesGroupByField")); + + + // List of series and group by field values + ArrayList seriesList = new ArrayList(); + ArrayList groupByValueList = new ArrayList(); + + // Convert comma separated Y values field names string to array of names + string[] yFieldNames = null; + if(yFields != null) + { + yFieldNames = yFields.Replace(",,", "\n").Split(','); + for(int index = 0; index < yFieldNames.Length; index++) + { + yFieldNames[index] = yFieldNames[index].Replace("\n", ","); + } + } + + // Convert other fields/properties names to two arrays of names + string[] otherAttributeNames = null; + string[] otherFieldNames = null; + string[] otherValueFormat = null; + DataPointCollection.ParsePointFieldsParameter( + otherFields, + ref otherAttributeNames, + ref otherFieldNames, + ref otherValueFormat); + + + // Get and reset enumerator + IEnumerator enumerator = DataPointCollection.GetDataSourceEnumerator(dataSource); + if(enumerator.GetType() != typeof(System.Data.Common.DbEnumerator)) + { + try + { + enumerator.Reset(); + } + // Some enumerators may not support Resetting + catch (NotSupportedException) + { + } + catch (NotImplementedException) + { + } + catch (InvalidOperationException) + { + } + + } + + // Add data points + bool valueExsist = true; + object[] yValuesObj = new object[yFieldNames.Length]; + object xValueObj = null; + bool autoDetectType = true; + + do + { + // Move to the next objects in the enumerations + if(valueExsist) + { + valueExsist = enumerator.MoveNext(); + } + + // Create and initialize data point + if(valueExsist) + { + // Get value of the group by field + object groupObj = DataPointCollection.ConvertEnumerationItem( + enumerator.Current, + seriesGroupByField); + + // Check series group by field and create new series if required + Series series = null; + int seriesIndex = groupByValueList.IndexOf(groupObj); + if(seriesIndex >= 0) + { + // Select existing series from the list + series = (Series)seriesList[seriesIndex]; + } + else + { + // Create new series + series = new Series(); + series.YValuesPerPoint = yFieldNames.GetLength(0); + + // If not the first series in the list copy some properties + if(seriesList.Count > 0) + { + series.XValueType = ((Series)seriesList[0]).XValueType; + series.autoXValueType = ((Series)seriesList[0]).autoXValueType; + series.YValueType = ((Series)seriesList[0]).YValueType; + series.autoYValueType = ((Series)seriesList[0]).autoYValueType; + } + + // Try to set series name based on grouping vlaue + string groupObjStr = groupObj as string; + if(groupObjStr != null) + { + series.Name = groupObjStr; + } + else + { + series.Name = seriesGroupByField + " - " + groupObj.ToString(); + } + + + // Add series and group value into the lists + groupByValueList.Add(groupObj); + seriesList.Add(series); + } + + + // Auto detect valu(s) type + if(autoDetectType) + { + autoDetectType = false; + DataPointCollection.AutoDetectValuesType(series, enumerator, xField, enumerator, yFieldNames[0]); + } + + // Create new data point + DataPoint newDataPoint = new DataPoint(series); + bool emptyValues = false; + + // Set X to the value provided + if(xField.Length > 0) + { + xValueObj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, xField); + if( DataPointCollection.IsEmptyValue(xValueObj) ) + { + emptyValues = true; + xValueObj = 0.0; + } + } + + // Set Y values + if(yFieldNames.Length == 0) + { + yValuesObj[0] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, null); + if( DataPointCollection.IsEmptyValue(yValuesObj[0]) ) + { + emptyValues = true; + yValuesObj[0] = 0.0; + } + } + else + { + for(int i = 0; i < yFieldNames.Length; i++) + { + yValuesObj[i] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, yFieldNames[i]); + if( DataPointCollection.IsEmptyValue(yValuesObj[i] ) ) + { + emptyValues = true; + yValuesObj[i] = 0.0; + } + } + } + + // Set other values + if(otherAttributeNames != null && + otherAttributeNames.Length > 0) + { + for(int i = 0; i < otherFieldNames.Length; i++) + { + // Get object by field name + object obj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, otherFieldNames[i]); + if( !DataPointCollection.IsEmptyValue( obj ) ) + { + newDataPoint.SetPointCustomProperty( + obj, + otherAttributeNames[i], + otherValueFormat[i]); + } + } + } + + // IsEmpty value was detected + if(emptyValues) + { + if(xValueObj != null) + { + newDataPoint.SetValueXY(xValueObj, yValuesObj); + } + else + { + newDataPoint.SetValueXY(0, yValuesObj); + } + DataPointCollection.DataPointInit(series, ref newDataPoint); + newDataPoint.IsEmpty = true; + series.Points.Add(newDataPoint); + } + else + { + if(xValueObj != null) + { + newDataPoint.SetValueXY(xValueObj, yValuesObj); + } + else + { + newDataPoint.SetValueXY(0, yValuesObj); + } + DataPointCollection.DataPointInit(series, ref newDataPoint); + series.Points.Add(newDataPoint); + } + } + + } while(valueExsist); + + // Sort series usig values of group by field + if(sort) + { + // Duplicate current list + ArrayList oldList = (ArrayList)groupByValueList.Clone(); + + // Sort list + groupByValueList.Sort(); + if(sortingOrder == PointSortOrder.Descending) + { + groupByValueList.Reverse(); + } + + // Change order of series in collection + ArrayList sortedSeriesList = new ArrayList(); + foreach(object obj in groupByValueList) + { + sortedSeriesList.Add(seriesList[oldList.IndexOf(obj)]); + } + seriesList = sortedSeriesList; + } + + // Add all series from the list into the series collection + foreach(Series series in seriesList) + { + this.Common.Chart.Series.Add(series); + } + } + + /// <summary> + /// Automatically creates and binds series to specified data table. + /// Each column of the table becomes a Y value in a separate series. + /// Series X value field may also be provided. + /// </summary> + /// <param name="dataSource">Data source.</param> + /// <param name="xField">Name of the field for series X values.</param> + internal void DataBindTable( + IEnumerable dataSource, + string xField) + { + // Check arguments + if (dataSource == null) + throw new ArgumentNullException("dataSource"); + + // Get list of member names from the data source + ArrayList dataSourceFields = GetDataSourceMemberNames(dataSource, true); + + // Remove X value field if it's there + if (xField != null && xField.Length > 0) + { + int index = -1; + for (int i = 0; i < dataSourceFields.Count; i++) + { + if ( String.Equals((string)dataSourceFields[i], xField, StringComparison.OrdinalIgnoreCase ) ) + { + index = i; + break; + } + } + if (index >= 0) + { + dataSourceFields.RemoveAt(index); + } + else + { + // Check if field name passed as index + bool parseSucceed = int.TryParse(xField, NumberStyles.Any, CultureInfo.InvariantCulture, out index); + if (parseSucceed && index >= 0 && index < dataSourceFields.Count) + { + dataSourceFields.RemoveAt(index); + } + } + } + + // Get number of series + int seriesNumber = dataSourceFields.Count; + if (seriesNumber > 0) + { + // Create as many series as fields in the data source + ArrayList seriesList = new ArrayList(); + int index = 0; + foreach (string fieldName in dataSourceFields) + { + Series series = new Series(fieldName); + + // Set binding properties + series.YValueMembers = fieldName; + series.XValueMember = xField; + + // Add to list + seriesList.Add(series); + ++index; + } + + + // Data bind series + this.DataBind(dataSource, seriesList); + + // Add all series from the list into the series collection + foreach (Series series in seriesList) + { + // Clear binding properties + series.YValueMembers = String.Empty; + series.XValueMember = String.Empty; + + // Add series into the list + this.Common.Chart.Series.Add(series); + } + } + } + + #endregion // Data Binding + + #endregion + + } + + /// <summary> + /// ChartPicture class represents chart content like legends, titles, + /// chart areas and series. It provides methods for positioning and + /// drawing all chart elements. + /// </summary> + internal class ChartPicture : ChartElement, IServiceProvider + { + #region Fields + + /// <summary> + /// Indicates that chart exceptions should be suppressed. + /// </summary> + private bool _suppressExceptions = false; + + // Chart Graphic object + internal ChartGraphics ChartGraph { get; set; } + + // Private data members, which store properties values + private GradientStyle _backGradientStyle = GradientStyle.None; + private Color _backSecondaryColor = Color.Empty; + private Color _backColor = Color.White; + private string _backImage = ""; + private ChartImageWrapMode _backImageWrapMode = ChartImageWrapMode.Tile; + private Color _backImageTransparentColor = Color.Empty; + private ChartImageAlignmentStyle _backImageAlign = ChartImageAlignmentStyle.TopLeft; + private Color _borderColor = Color.White; + private int _borderWidth = 1; + private ChartDashStyle _borderDashStyle = ChartDashStyle.NotSet; + private ChartHatchStyle _backHatchStyle = ChartHatchStyle.None; + private AntiAliasingStyles _antiAliasing = AntiAliasingStyles.All; + private TextAntiAliasingQuality _textAntiAliasingQuality = TextAntiAliasingQuality.High; + private bool _isSoftShadows = true; + private int _width = 300; + private int _height = 300; + private DataManipulator _dataManipulator = new DataManipulator(); + internal HotRegionsList hotRegionsList = null; + private BorderSkin _borderSkin = null; +#if !Microsoft_CONTROL + private bool _isMapEnabled = true; + private MapAreasCollection _mapAreas = null; +#endif + // Chart areas collection + private ChartAreaCollection _chartAreas = null; + + // Chart legend collection + private LegendCollection _legends = null; + + // Chart title collection + private TitleCollection _titles = null; + + // Chart annotation collection + private AnnotationCollection _annotations = null; + + // Annotation smart labels class + internal AnnotationSmartLabel annotationSmartLabel = new AnnotationSmartLabel(); + + // Chart picture events + internal event EventHandler<ChartPaintEventArgs> BeforePaint; + internal event EventHandler<ChartPaintEventArgs> AfterPaint; + + // Chart title position rectangle + private RectangleF _titlePosition = RectangleF.Empty; + + // Element spacing size + internal const float elementSpacing = 3F; + + // Maximum size of the font in percentage + internal const float maxTitleSize = 15F; + + // Printing indicator + internal bool isPrinting = false; + + // Indicates chart selection mode + internal bool isSelectionMode = false; + + private FontCache _fontCache = new FontCache(); + + // Position of the chart 3D border + private RectangleF _chartBorderPosition = RectangleF.Empty; + +#if Microsoft_CONTROL + + // Saving As Image indicator + internal bool isSavingAsImage = false; + + // Indicates that chart background is restored from the double buffer + // prior to drawing top level objects like annotations, cursors and selection. + internal bool backgroundRestored = false; + + // Buffered image of non-top level chart elements + internal Bitmap nonTopLevelChartBuffer = null; + +#endif // Microsoft_CONTROL + + #endregion + + #region Constructors + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="container">Service container</param> + public ChartPicture(IServiceContainer container) + { + if(container == null) + { + throw(new ArgumentNullException(SR.ExceptionInvalidServiceContainer)); + } + + // Create and set Common Elements + Common = new CommonElements(container); + ChartGraph= new ChartGraphics(Common); + hotRegionsList = new HotRegionsList(Common); + + // Create border properties class + _borderSkin = new BorderSkin(this); + + // Create a collection of chart areas + _chartAreas = new ChartAreaCollection(this); + + // Create a collection of legends + _legends = new LegendCollection(this); + + // Create a collection of titles + _titles = new TitleCollection(this); + + // Create a collection of annotations + _annotations = new AnnotationCollection(this); + + // Set Common elements for data manipulator + _dataManipulator.Common = Common; + +#if !Microsoft_CONTROL + // Create map areas collection + _mapAreas = new MapAreasCollection(); +#endif + } + + /// <summary> + /// Returns Chart service object + /// </summary> + /// <param name="serviceType">Service AxisName</param> + /// <returns>Chart picture</returns> + [EditorBrowsableAttribute(EditorBrowsableState.Never)] + object IServiceProvider.GetService(Type serviceType) + { + if(serviceType == typeof(ChartPicture)) + { + return this; + } + throw (new ArgumentException( SR.ExceptionChartPictureUnsupportedType( serviceType.ToString() ) ) ); + } + + #endregion + + #region Painting and selection methods + + /// <summary> + /// Performs empty painting. + /// </summary> + internal void PaintOffScreen() + { + // Check chart size + // NOTE: Fixes issue #4733 + if (this.Width <= 0 || this.Height <= 0) + { + return; + } + + // Set process Mode to hot regions + this.Common.HotRegionsList.ProcessChartMode |= ProcessMode.HotRegions; +#if Microsoft_CONTROL + this.Common.HotRegionsList.hitTestCalled = true; +#endif // Microsoft_CONTROL + + // Enable selection mode + this.isSelectionMode = true; + + // Hot Region list does not exist. Create the list. + //this.common.HotRegionsList.List = new ArrayList(); + this.Common.HotRegionsList.Clear(); + + // Create a new bitmap + Bitmap image = new Bitmap(Math.Max(1,Width), Math.Max(1,Height)); + + // Creates a new Graphics object from the + // specified Image object. + Graphics offScreen = Graphics.FromImage(image); + + // Connect Graphics object with Chart Graphics object + ChartGraph.Graphics = offScreen; + + // Remember the previous dirty flag +#if Microsoft_CONTROL + bool oldDirtyFlag = this.Common.Chart.dirtyFlag; +#endif //Microsoft_CONTROL + + + Paint(ChartGraph.Graphics, false); + + image.Dispose(); + + // Restore the previous dirty flag +#if Microsoft_CONTROL + this.Common.Chart.dirtyFlag = oldDirtyFlag; +#endif //Microsoft_CONTROL + + // Disable selection mode + this.isSelectionMode = false; + + // Set process Mode to hot regions + this.Common.HotRegionsList.ProcessChartMode |= ProcessMode.HotRegions; + + } + + /// <summary> + /// Gets text rendering quality. + /// </summary> + /// <returns>Text rendering quality.</returns> + internal TextRenderingHint GetTextRenderingHint() + { + TextRenderingHint result = TextRenderingHint.SingleBitPerPixelGridFit; + if( (this.AntiAliasing & AntiAliasingStyles.Text) == AntiAliasingStyles.Text ) + { + result = TextRenderingHint.ClearTypeGridFit; + if(this.TextAntiAliasingQuality == TextAntiAliasingQuality.Normal) + { + result = TextRenderingHint.AntiAlias; + } + else if(this.TextAntiAliasingQuality == TextAntiAliasingQuality.SystemDefault) + { + result = TextRenderingHint.SystemDefault; + } + } + else + { + result = TextRenderingHint.SingleBitPerPixelGridFit; + } + + return result; + } + + internal bool GetBorderSkinVisibility() + { + return _borderSkin.SkinStyle != BorderSkinStyle.None && this.Width > 20 && this.Height > 20; + } + + /// <summary> + /// This function paints a chart. + /// </summary> + /// <param name="graph">The graph provides drawing object to the display device. A Graphics object is associated with a specific device context.</param> + /// <param name="paintTopLevelElementOnly">Indicates that only chart top level elements like cursors, selection or annotation objects must be redrawn.</param> + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "3#svg")] + internal void Paint( + Graphics graph, + bool paintTopLevelElementOnly ) + { + +#if Microsoft_CONTROL + + // Reset restored and saved backgound flags + this.backgroundRestored = false; + +#endif // Microsoft_CONTROL + + // Reset Annotation Smart Labels + this.annotationSmartLabel.Reset(); + + // Do not draw the control if size is less than 5 pixel + if (this.Width < 5 || this.Height < 5) + { + return; + } + +#if Microsoft_CONTROL + + bool resetHotRegionList = false; + + if( + this.Common.HotRegionsList.hitTestCalled + || IsToolTipsEnabled() + ) + { + Common.HotRegionsList.ProcessChartMode = ProcessMode.HotRegions | ProcessMode.Paint; + + this.Common.HotRegionsList.hitTestCalled = false; + + // Clear list of hot regions + if(paintTopLevelElementOnly) + { + // If repainting only top level elements (annotations) - + // clear top level objects hot regions only + for(int index = 0; index < this.Common.HotRegionsList.List.Count; index++) + { + HotRegion region = (HotRegion)this.Common.HotRegionsList.List[index]; + if(region.Type == ChartElementType.Annotation) + { + this.Common.HotRegionsList.List.RemoveAt(index); + --index; + } + } + } + else + { + // If repainting whole chart - clear all hot regions + resetHotRegionList = true; + } + } + else + { + Common.HotRegionsList.ProcessChartMode = ProcessMode.Paint; + + // If repainting whole chart - clear all hot regions + resetHotRegionList = true; + } + + // Reset hot region list + if(resetHotRegionList) + { + this.Common.HotRegionsList.Clear(); + } + +#else + if( this.IsMapEnabled ) + { + Common.HotRegionsList.ProcessChartMode |= ProcessMode.ImageMaps | ProcessMode.Paint; + + // Clear any existing non-custom image map areas + for(int index = 0; index < this.MapAreas.Count; index++) + { + MapArea mapArea = this.MapAreas[index]; + if(!mapArea.IsCustom) + { + this.MapAreas.RemoveAt(index); + --index; + } + } + } + + +#endif //#if Microsoft_CONTROL + + // Check if control was data bound + ChartImage chartImage = this as ChartImage; + if(chartImage != null && !chartImage.boundToDataSource) + { + if(this.Common != null && this.Common.Chart != null && !this.Common.Chart.IsDesignMode()) + { + this.Common.Chart.DataBind(); + } + } + + // Connect Graphics object with Chart Graphics object + ChartGraph.Graphics = graph; + + Common.graph = ChartGraph; + + // Set anti alias mode + ChartGraph.AntiAliasing = _antiAliasing; + ChartGraph.softShadows = _isSoftShadows; + ChartGraph.TextRenderingHint = GetTextRenderingHint(); + + try + { + // Check if only chart area cursors and annotations must be redrawn + if(!paintTopLevelElementOnly) + { + // Fire Before Paint event + OnBeforePaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100))); + + // Flag indicates that resize method should be called + // after adjusting the intervals in 3D charts + bool resizeAfterIntervalAdjusting = false; + + // RecalculateAxesScale paint chart areas + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.Set3DAnglesAndReverseMode(); + area.SetTempValues(); + area.ReCalcInternal(); + + // Resize should be called the second time + if( area.Area3DStyle.Enable3D && !area.chartAreaIsCurcular) + { + resizeAfterIntervalAdjusting = true; + } + } + } + + // Call Customize event + this.Common.Chart.CallOnCustomize(); + + // Resize picture + Resize(ChartGraph, resizeAfterIntervalAdjusting); + + + // This code is introduce because labels has to + // be changed when scene is rotated. + bool intervalReCalculated = false; + foreach (ChartArea area in _chartAreas ) + { + if( area.Area3DStyle.Enable3D && + !area.chartAreaIsCurcular + + && area.Visible + + ) + + { + // Make correction for interval in 3D space + intervalReCalculated = true; + area.Estimate3DInterval( ChartGraph ); + area.ReCalcInternal(); + } + } + + // Resize chart areas after updating 3D interval + if(resizeAfterIntervalAdjusting) + { + // NOTE: Fixes issue #6808. + // In 3D chart area interval will be changed to compenstae for the axis rotation angle. + // This will cause all standard labels to be changed. We need to call the customize event + // the second time to give user a chance to modify those labels. + if (intervalReCalculated) + { + // Call Customize event + this.Common.Chart.CallOnCustomize(); + } + + // Resize chart elements + Resize(ChartGraph); + } + + + //*********************************************************************** + //** Draw chart 3D border + //*********************************************************************** + if (GetBorderSkinVisibility()) + { + // Fill rectangle with page color + ChartGraph.FillRectangleAbs( new RectangleF( 0, 0, Width-1 , Height-1 ), + _borderSkin.PageColor, + ChartHatchStyle.None, + "", + ChartImageWrapMode.Tile, + Color.Empty, + ChartImageAlignmentStyle.Center, + GradientStyle.None, + Color.Empty, + _borderSkin.PageColor, + 1, + ChartDashStyle.Solid, + PenAlignment.Inset ); + + // Draw 3D border + ChartGraph.Draw3DBorderAbs( + _borderSkin, + this._chartBorderPosition, + BackColor, + BackHatchStyle, + BackImage, + BackImageWrapMode, + BackImageTransparentColor, + BackImageAlignment, + BackGradientStyle, + BackSecondaryColor, + BorderColor, + BorderWidth, + BorderDashStyle); + } + + // Paint Background + else + { + ChartGraph.FillRectangleAbs( new RectangleF( 0, 0, Width-1 , Height-1 ), + BackColor, + BackHatchStyle, + BackImage, + BackImageWrapMode, + BackImageTransparentColor, + BackImageAlignment, + BackGradientStyle, + BackSecondaryColor, + BorderColor, + BorderWidth, + BorderDashStyle, + PenAlignment.Inset ); + } + + // Call BackPaint event + this.Chart.CallOnPrePaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100))); + + // Call paint function for each chart area. + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.Paint(ChartGraph); + } + } + + // This code is introduced because of GetPointsInterval method, + // which is very time consuming. There is no reason to calculate + // interval after painting. + foreach (ChartArea area in _chartAreas ) + { + // Reset interval data + area.intervalData = double.NaN; + } + + // Draw Legends + foreach(Legend legendCurrent in this.Legends) + { + legendCurrent.Paint(ChartGraph); + } + + // Draw chart titles from the collection + foreach(Title titleCurrent in this.Titles) + { + titleCurrent.Paint(ChartGraph); + } + + // Call Paint event + this.Chart.CallOnPostPaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100))); + } + + // Draw annotation objects + this.Annotations.Paint(ChartGraph, paintTopLevelElementOnly); + + // Draw chart areas cursors in all areas. + // Only if not in selection + if(!this.isSelectionMode) + { + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.PaintCursors(ChartGraph, paintTopLevelElementOnly); + } + } + } + + // Return default values + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.Restore3DAnglesAndReverseMode(); + area.GetTempValues(); + } + } + } + catch(System.Exception) + { + throw; + } + finally + { + // Fire After Paint event + OnAfterPaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100))); + + // Restore temp values for each chart area + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.Restore3DAnglesAndReverseMode(); + area.GetTempValues(); + } + } + +#if !Microsoft_CONTROL + if (this.Chart.IsDesignMode()) + { + this.Chart.MapAreas.RemoveNonCustom(); + } +#endif //!Microsoft_CONTROL + } + } + + /// <summary> + /// Invoke before paint delegates. + /// </summary> + /// <param name="e">Event arguments.</param> + protected virtual void OnBeforePaint(ChartPaintEventArgs e) + { + if (BeforePaint != null) + { + //Invokes the delegates. + BeforePaint(this, e); + } + } + + /// <summary> + /// Invoke after paint delegates. + /// </summary> + /// <param name="e">Event arguments.</param> + protected virtual void OnAfterPaint(ChartPaintEventArgs e) + { + if (AfterPaint != null) + { + //Invokes the delegates. + AfterPaint(this, e); + } + } + + internal override void Invalidate() + { + base.Invalidate(); + +#if Microsoft_CONTROL + if (Chart!=null) + Chart.Invalidate(); +#endif + } + #endregion + + #region Resizing methods + + /// <summary> + /// Resize the chart picture. + /// </summary> + /// <param name="chartGraph">Chart graphics.</param> + public void Resize(ChartGraphics chartGraph) + { + Resize(chartGraph, false); + } + + /// <summary> + /// Resize the chart picture. + /// </summary> + /// <param name="chartGraph">Chart graphics.</param> + /// <param name="calcAreaPositionOnly">Indicates that only chart area position is calculated.</param> + public void Resize(ChartGraphics chartGraph, bool calcAreaPositionOnly) + { + // Set the chart size for Common elements + Common.Width = _width; + Common.Height = _height; + + // Set the chart size for Chart graphics + chartGraph.SetPictureSize( _width, _height ); + + // Initialize chart area(s) rectangle + RectangleF chartAreasRectangle = new RectangleF(0, 0, _width - 1, _height - 1); + chartAreasRectangle = chartGraph.GetRelativeRectangle(chartAreasRectangle); + + //****************************************************** + //** Get the 3D border interface + //****************************************************** + _titlePosition = RectangleF.Empty; + IBorderType border3D = null; + bool titleInBorder = false; + + if(_borderSkin.SkinStyle != BorderSkinStyle.None) + { + // Set border size + this._chartBorderPosition = chartGraph.GetAbsoluteRectangle(chartAreasRectangle); + + // Get border interface + border3D = Common.BorderTypeRegistry.GetBorderType(_borderSkin.SkinStyle.ToString()); + if(border3D != null) + { + border3D.Resolution = chartGraph.Graphics.DpiX; + // Check if title should be displayed in the border + titleInBorder = border3D.GetTitlePositionInBorder() != RectangleF.Empty; + _titlePosition = chartGraph.GetRelativeRectangle(border3D.GetTitlePositionInBorder()); + _titlePosition.Width = chartAreasRectangle.Width - _titlePosition.Width; + + // Adjust are position to the border size + border3D.AdjustAreasPosition(chartGraph, ref chartAreasRectangle); + } + } + + //****************************************************** + //** Calculate position of all titles in the collection + //****************************************************** + RectangleF frameTitlePosition = RectangleF.Empty; + if(titleInBorder) + { + frameTitlePosition = new RectangleF(_titlePosition.Location, _titlePosition.Size); + } + foreach(Title title in this.Titles) + { + if (title.DockedToChartArea == Constants.NotSetValue && + title.Position.Auto && + title.Visible) + { + title.CalcTitlePosition(chartGraph, ref chartAreasRectangle, ref frameTitlePosition, elementSpacing); + } + } + + //****************************************************** + //** Calculate position of all legends in the collection + //****************************************************** + this.Legends.CalcLegendPosition(chartGraph, ref chartAreasRectangle, elementSpacing); + + //****************************************************** + //** Calculate position of the chart area(s) + //****************************************************** + chartAreasRectangle.Width -= elementSpacing; + chartAreasRectangle.Height -= elementSpacing; + RectangleF areaPosition = new RectangleF(); + + + // Get number of chart areas that requeres automatic positioning + int areaNumber = 0; + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + if(area.Position.Auto) + { + ++areaNumber; + } + } + } + + // Calculate how many columns & rows of areas we going to have + int areaColumns = (int)Math.Floor(Math.Sqrt(areaNumber)); + if(areaColumns < 1) + { + areaColumns = 1; + } + int areaRows = (int)Math.Ceiling(((float)areaNumber) / ((float)areaColumns)); + + // Set position for all areas + int column = 0; + int row = 0; + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + if(area.Position.Auto) + { + // Calculate area position + areaPosition.Width = chartAreasRectangle.Width / areaColumns - elementSpacing; + areaPosition.Height = chartAreasRectangle.Height / areaRows - elementSpacing; + areaPosition.X = chartAreasRectangle.X + column * (chartAreasRectangle.Width / areaColumns) + elementSpacing; + areaPosition.Y = chartAreasRectangle.Y + row * (chartAreasRectangle.Height / areaRows) + elementSpacing; + + // Calculate position of all titles in the collection docked outside of the chart area + TitleCollection.CalcOutsideTitlePosition(this, chartGraph, area, ref areaPosition, elementSpacing); + + // Calculate position of the legend if it's docked outside of the chart area + this.Legends.CalcOutsideLegendPosition(chartGraph, area, ref areaPosition, elementSpacing); + + // Set area position without changing the Auto flag + area.Position.SetPositionNoAuto(areaPosition.X, areaPosition.Y, areaPosition.Width, areaPosition.Height); + + // Go to next area + ++row; + if(row >= areaRows) + { + row = 0; + ++column; + } + } + else + { + RectangleF rect = area.Position.ToRectangleF(); + + // Calculate position of all titles in the collection docked outside of the chart area + TitleCollection.CalcOutsideTitlePosition(this, chartGraph, area, ref rect, elementSpacing); + + // Calculate position of the legend if it's docked outside of the chart area + this.Legends.CalcOutsideLegendPosition(chartGraph, area, ref rect, elementSpacing); + } + } + } + + //****************************************************** + //** Align chart areas Position if required + //****************************************************** + AlignChartAreasPosition(); + + //******************************************************** + //** Check if only chart area position must be calculated. + //******************************************************** + if(!calcAreaPositionOnly) + { + + //****************************************************** + //** Call Resize function for each chart area. + //****************************************************** + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.Resize(chartGraph); + } + } + + //****************************************************** + //** Align chart areas InnerPlotPosition if required + //****************************************************** + AlignChartAreas(AreaAlignmentStyles.PlotPosition); + + //****************************************************** + //** Calculate position of the legend if it's inside + //** chart plotting area + //****************************************************** + + // Calculate position of all titles in the collection docked outside of the chart area + TitleCollection.CalcInsideTitlePosition(this, chartGraph, elementSpacing); + + this.Legends.CalcInsideLegendPosition(chartGraph, elementSpacing); + } + } + + /// <summary> + /// Minimum and maximum do not have to be calculated + /// from data series every time. It is very time + /// consuming. Minimum and maximum are buffered + /// and only when this flags are set Minimum and + /// Maximum are refreshed from data. + /// </summary> + internal void ResetMinMaxFromData() + { + if (_chartAreas != null) + { + // Call ResetMinMaxFromData function for each chart area. + foreach (ChartArea area in _chartAreas) + { + + // Check if area is visible + if (area.Visible) + { + area.ResetMinMaxFromData(); + } + } + } + } + + /// <summary> + /// RecalculateAxesScale the chart picture. + /// </summary> + public void Recalculate() + { + // Call ReCalc function for each chart area. + foreach (ChartArea area in _chartAreas ) + { + + // Check if area is visible + if(area.Visible) + + { + area.ReCalcInternal(); + } + } + } + + #endregion + + #region Chart picture properties + + // VSTS 96787-Text Direction (RTL/LTR) +#if !Microsoft_CONTROL + private RightToLeft rightToLeft = RightToLeft.No; +#endif //!Microsoft_CONTROL + /// <summary> + /// Gets or sets the RightToLeft type. + /// </summary> + [ + DefaultValue(RightToLeft.No) + ] + public RightToLeft RightToLeft + { + get + { +#if Microsoft_CONTROL + return this.Common.Chart.RightToLeft; +#else // !WIN_CONTROL + return this.rightToLeft; +#endif // WIN_CONTROL + } + set + { +#if Microsoft_CONTROL + this.Common.Chart.RightToLeft = value; +#else // !Microsoft_CONTROL + this.rightToLeft = value; +#endif // Microsoft_CONTROL + } + } + + /// <summary> + /// Indicates that non-critical chart exceptions will be suppressed. + /// </summary> + [ + SRCategory("CategoryAttributeMisc"), + DefaultValue(false), + SRDescription("DescriptionAttributeSuppressExceptions"), + ] + internal bool SuppressExceptions + { + set + { + _suppressExceptions = value; + } + get + { + return _suppressExceptions; + } + } + + /// <summary> + /// Chart border skin style. + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(BorderSkinStyle.None), + SRDescription("DescriptionAttributeBorderSkin"), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.InnerProperty), +#endif + ] + public BorderSkin BorderSkin + { + get + { + return _borderSkin; + } + set + { + _borderSkin = value; + } + } + +#if ! Microsoft_CONTROL + + /// <summary> + /// Indicates that chart image map is enabled. + /// </summary> + [ + SRCategory("CategoryAttributeMap"), + Bindable(true), + SRDescription("DescriptionAttributeMapEnabled"), + PersistenceMode(PersistenceMode.InnerProperty), + DefaultValue(true) + ] + public bool IsMapEnabled + { + get + { + return _isMapEnabled; + } + set + { + _isMapEnabled = value; + } + } + + /// <summary> + /// Chart map areas collection. + /// </summary> + [ + SRCategory("CategoryAttributeMap"), + SRDescription("DescriptionAttributeMapAreas"), + PersistenceMode(PersistenceMode.InnerProperty), + Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base) + ] + public MapAreasCollection MapAreas + { + get + { + return _mapAreas; + } + } +#endif + + /// <summary> + /// Reference to chart area collection + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + SRDescription("DescriptionAttributeChartAreas"), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.InnerProperty), +#endif + Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base) + ] + public ChartAreaCollection ChartAreas + { + get + { + return _chartAreas; + } + } + + /// <summary> + /// Chart legend collection. + /// </summary> + [ + SRCategory("CategoryAttributeChart"), + SRDescription("DescriptionAttributeLegends"), + Editor(Editors.LegendCollectionEditor.Editor, Editors.LegendCollectionEditor.Base), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.InnerProperty), +#endif + ] + public LegendCollection Legends + { + get + { + return _legends; + } + } + + /// <summary> + /// Chart title collection. + /// </summary> + [ + SRCategory("CategoryAttributeCharttitle"), + SRDescription("DescriptionAttributeTitles"), + Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.InnerProperty), +#endif + ] + public TitleCollection Titles + { + get + { + return _titles; + } + } + + + + /// <summary> + /// Chart annotation collection. + /// </summary> + [ + SRCategory("CategoryAttributeChart"), + SRDescription("DescriptionAttributeAnnotations3"), + Editor(Editors.AnnotationCollectionEditor.Editor, Editors.AnnotationCollectionEditor.Base), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.InnerProperty), +#endif + ] + public AnnotationCollection Annotations + { + get + { + return _annotations; + } + } + + + + /// <summary> + /// Background color for the Chart + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(typeof(Color), "White"), + SRDescription("DescriptionAttributeBackColor"), + TypeConverter(typeof(ColorConverter)), + Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public Color BackColor + { + get + { + return _backColor; + } + set + { +#if !Microsoft_CONTROL + if(value == Color.Empty || value.A != 255 || value == Color.Transparent) + { + // NOTE: Transparent colors are valid + } +#endif + _backColor = value; + } + } + + /// <summary> + /// Border color for the Chart + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(typeof(Color), "White"), + SRDescription("DescriptionAttributeBorderColor"), + TypeConverter(typeof(ColorConverter)), + Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public Color BorderColor + { + get + { + return _borderColor; + } + set + { + _borderColor = value; + } + } + + /// <summary> + /// Chart width + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(300), + SRDescription("DescriptionAttributeWidth"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public int Width + { + get + { + return _width; + } + set + { + this.InspectChartDimensions(value, this.Height); + _width = value; + Common.Width = _width; + } + } + + /// <summary> + /// Series Data Manipulator + /// </summary> + [ + SRCategory("CategoryAttributeData"), + SRDescription("DescriptionAttributeDataManipulator"), + Browsable(false), + DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), + SerializationVisibilityAttribute(SerializationVisibility.Hidden) + ] + public DataManipulator DataManipulator + { + get + { + return _dataManipulator; + } + } + + + + + /// <summary> + /// Chart height + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(300), + SRDescription("DescriptionAttributeHeight3"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public int Height + { + get + { + return _height; + } + set + { + this.InspectChartDimensions(this.Width, value); + _height = value; + Common.Height = value; + } + } + + /// <summary> + /// Back Hatch style + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(ChartHatchStyle.None), + SRDescription("DescriptionAttributeBackHatchStyle"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute), + #endif + Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base) + ] + public ChartHatchStyle BackHatchStyle + { + get + { + return _backHatchStyle; + } + set + { + _backHatchStyle = value; + } + } + + /// <summary> + /// Chart area background image + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(""), + SRDescription("DescriptionAttributeBackImage"), + Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute), + #endif + NotifyParentPropertyAttribute(true) + ] + public string BackImage + { + get + { + return _backImage; + } + set + { + _backImage = value; + } + } + + /// <summary> + /// Chart area background image drawing mode. + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(ChartImageWrapMode.Tile), + NotifyParentPropertyAttribute(true), + SRDescription("DescriptionAttributeImageWrapMode"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public ChartImageWrapMode BackImageWrapMode + { + get + { + return _backImageWrapMode; + } + set + { + _backImageWrapMode = value; + } + } + + /// <summary> + /// Background image transparent color. + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(typeof(Color), ""), + NotifyParentPropertyAttribute(true), + SRDescription("DescriptionAttributeImageTransparentColor"), + TypeConverter(typeof(ColorConverter)), + Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public Color BackImageTransparentColor + { + get + { + return _backImageTransparentColor; + } + set + { + _backImageTransparentColor = value; + } + } + + /// <summary> + /// Background image alignment used by ClampUnscale drawing mode. + /// </summary> + [ + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(ChartImageAlignmentStyle.TopLeft), + NotifyParentPropertyAttribute(true), + SRDescription("DescriptionAttributeBackImageAlign"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public ChartImageAlignmentStyle BackImageAlignment + { + get + { + return _backImageAlign; + } + set + { + _backImageAlign = value; + } + } + + /// <summary> + /// Indicates that smoothing is applied while drawing shadows. + /// </summary> + [ + SRCategory("CategoryAttributeImage"), + Bindable(true), + DefaultValue(true), + SRDescription("DescriptionAttributeSoftShadows3"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public bool IsSoftShadows + { + get + { + return _isSoftShadows; + } + set + { + _isSoftShadows = value; + } + } + + /// <summary> + /// Specifies whether smoothing (antialiasing) is applied while drawing chart. + /// </summary> + [ + SRCategory("CategoryAttributeImage"), + Bindable(true), + DefaultValue(typeof(AntiAliasingStyles), "All"), + SRDescription("DescriptionAttributeAntiAlias"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public AntiAliasingStyles AntiAliasing + { + get + { + return _antiAliasing; + } + set + { + _antiAliasing = value; + } + } + + /// <summary> + /// Specifies the quality of text antialiasing. + /// </summary> + [ + SRCategory("CategoryAttributeImage"), + Bindable(true), + DefaultValue(typeof(TextAntiAliasingQuality), "High"), + SRDescription("DescriptionAttributeTextAntiAliasingQuality"), +#if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) +#endif + ] + public TextAntiAliasingQuality TextAntiAliasingQuality + { + get + { + return _textAntiAliasingQuality; + } + set + { + _textAntiAliasingQuality = value; + } + } + + /// <summary> + /// A type for the background gradient + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(GradientStyle.None), + SRDescription("DescriptionAttributeBackGradientStyle"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute), + #endif + Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base) + ] + public GradientStyle BackGradientStyle + { + get + { + return _backGradientStyle; + } + set + { + _backGradientStyle = value; + } + } + + /// <summary> + /// The second color which is used for a gradient + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(typeof(Color), ""), + SRDescription("DescriptionAttributeBackSecondaryColor"), + TypeConverter(typeof(ColorConverter)), + Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public Color BackSecondaryColor + { + get + { + return _backSecondaryColor; + } + set + { +#if !Microsoft_CONTROL + if(value != Color.Empty && (value.A != 255 || value == Color.Transparent)) + { + throw (new ArgumentException( SR.ExceptionBackSecondaryColorIsTransparent)); + } +#endif + _backSecondaryColor = value; + } + } + + /// <summary> + /// The width of the border line + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(1), + SRDescription("DescriptionAttributeChart_BorderlineWidth"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public int BorderWidth + { + get + { + return _borderWidth; + } + set + { + if(value < 0) + { + throw(new ArgumentOutOfRangeException("value", SR.ExceptionChartBorderIsNegative)); + } + _borderWidth = value; + } + } + + /// <summary> + /// The style of the border line + /// </summary> + [ + + SRCategory("CategoryAttributeAppearance"), + Bindable(true), + DefaultValue(ChartDashStyle.NotSet), + SRDescription("DescriptionAttributeBorderDashStyle"), + #if !Microsoft_CONTROL + PersistenceMode(PersistenceMode.Attribute) + #endif + ] + public ChartDashStyle BorderDashStyle + { + get + { + return _borderDashStyle; + } + set + { + _borderDashStyle = value; + } + } + + /// <summary> + /// Gets the font cache. + /// </summary> + /// <value>The font cache.</value> + internal FontCache FontCache + { + get { return _fontCache; } + } + + #endregion + + #region Chart areas alignment methods + + /// <summary> + /// Checks if any of the chart areas are aligned. + /// Also checks if the chart ares name in AlignWithChartArea property is valid. + /// </summary> + /// <returns>True if at least one area requires alignment.</returns> + private bool IsAreasAlignmentRequired() + { + bool alignmentRequired = false; + + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Check if area is aligned + if (area.AlignWithChartArea != Constants.NotSetValue) + { + alignmentRequired = true; + + // Check the chart area used for alignment + if (this._chartAreas.IndexOf(area.AlignWithChartArea)<0) + { + throw (new InvalidOperationException(SR.ExceptionChartAreaNameReferenceInvalid(area.Name, area.AlignWithChartArea))); + } + } + } + } + + return alignmentRequired; + } + + /// <summary> + /// Creates a list of the aligned chart areas. + /// </summary> + /// <param name="masterArea">Master chart area.</param> + /// <param name="type">Alignment type.</param> + /// <param name="orientation">Vertical or Horizontal orientation.</param> + /// <returns>List of areas that area aligned to the master area.</returns> + private ArrayList GetAlignedAreasGroup(ChartArea masterArea, AreaAlignmentStyles type, AreaAlignmentOrientations orientation) + { + ArrayList areaList = new ArrayList(); + + // Loop throught the chart areas and get the ones aligned with specified master area + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + if(area.Name != masterArea.Name && + area.AlignWithChartArea == masterArea.Name && + (area.AlignmentStyle & type) == type && + (area.AlignmentOrientation & orientation) == orientation ) + { + // Add "slave" area into the list + areaList.Add(area); + } + } + } + + // If list is not empty insert "master" area in the beginning + if(areaList.Count > 0) + { + areaList.Insert(0, masterArea); + } + + return areaList; + } + + /// <summary> + /// Performs specified type of alignment for the chart areas. + /// </summary> + /// <param name="type">Alignment type required.</param> + internal void AlignChartAreas(AreaAlignmentStyles type) + { + // Check if alignment required + if(IsAreasAlignmentRequired()) + { + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Get vertical areas alignment group using current area as a master + ArrayList alignGroup = GetAlignedAreasGroup( + area, + type, + AreaAlignmentOrientations.Vertical); + + // Align each area in the group + if(alignGroup.Count > 0) + { + AlignChartAreasPlotPosition(alignGroup, AreaAlignmentOrientations.Vertical); + } + + // Get horizontal areas alignment group using current area as a master + alignGroup = GetAlignedAreasGroup( + area, + type, + AreaAlignmentOrientations.Horizontal); + + // Align each area in the group + if(alignGroup.Count > 0) + { + AlignChartAreasPlotPosition(alignGroup, AreaAlignmentOrientations.Horizontal); + } + } + } + } + } + + /// <summary> + /// Align inner plot position of the chart areas in the group. + /// </summary> + /// <param name="areasGroup">List of areas in the group.</param> + /// <param name="orientation">Group orientation.</param> + private void AlignChartAreasPlotPosition(ArrayList areasGroup, AreaAlignmentOrientations orientation) + { + //**************************************************************** + //** Find the smalles size of the inner plot + //**************************************************************** + RectangleF areaPlotPosition = ((ChartArea)areasGroup[0]).PlotAreaPosition.ToRectangleF(); + foreach(ChartArea area in areasGroup) + { + if(area.PlotAreaPosition.X > areaPlotPosition.X) + { + areaPlotPosition.X += area.PlotAreaPosition.X - areaPlotPosition.X; + areaPlotPosition.Width -= area.PlotAreaPosition.X - areaPlotPosition.X; + } + if(area.PlotAreaPosition.Y > areaPlotPosition.Y) + { + areaPlotPosition.Y += area.PlotAreaPosition.Y - areaPlotPosition.Y; + areaPlotPosition.Height -= area.PlotAreaPosition.Y - areaPlotPosition.Y; + } + if(area.PlotAreaPosition.Right < areaPlotPosition.Right) + { + areaPlotPosition.Width -= areaPlotPosition.Right - area.PlotAreaPosition.Right; + if(areaPlotPosition.Width < 5) + { + areaPlotPosition.Width = 5; + } + } + if(area.PlotAreaPosition.Bottom < areaPlotPosition.Bottom) + { + areaPlotPosition.Height -= areaPlotPosition.Bottom - area.PlotAreaPosition.Bottom; + if(areaPlotPosition.Height < 5) + { + areaPlotPosition.Height = 5; + } + } + } + + //**************************************************************** + //** Align inner plot position for all areas + //**************************************************************** + foreach(ChartArea area in areasGroup) + { + // Get curretn plot position of the area + RectangleF rect = area.PlotAreaPosition.ToRectangleF(); + + // Adjust area position + if( (orientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical) + { + rect.X = areaPlotPosition.X; + rect.Width = areaPlotPosition.Width; + } + if( (orientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal) + { + rect.Y = areaPlotPosition.Y; + rect.Height = areaPlotPosition.Height; + } + + // Set new plot position in coordinates relative to chart picture + area.PlotAreaPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height); + + // Set new plot position in coordinates relative to chart area position + rect.X = (rect.X - area.Position.X) / area.Position.Width * 100f; + rect.Y = (rect.Y - area.Position.Y) / area.Position.Height * 100f; + rect.Width = rect.Width / area.Position.Width * 100f; + rect.Height = rect.Height / area.Position.Height * 100f; + area.InnerPlotPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height); + + if( (orientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical) + { + area.AxisX2.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto); + area.AxisX.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto); + } + if( (orientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal) + { + area.AxisY2.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto); + area.AxisY.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto); + } + } + + } + + /// <summary> + /// Aligns positions of the chart areas. + /// </summary> + private void AlignChartAreasPosition() + { + // Check if alignment required + if(IsAreasAlignmentRequired()) + { + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Check if area is alignd by Position to any other area + if (area.AlignWithChartArea != Constants.NotSetValue && (area.AlignmentStyle & AreaAlignmentStyles.Position) == AreaAlignmentStyles.Position) + { + // Get current area position + RectangleF areaPosition = area.Position.ToRectangleF(); + + // Get master chart area + ChartArea masterArea = this.ChartAreas[area.AlignWithChartArea]; + + // Vertical alignment + if((area.AlignmentOrientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical) + { + // Align area position + areaPosition.X = masterArea.Position.X; + areaPosition.Width = masterArea.Position.Width; + } + + // Horizontal alignment + if((area.AlignmentOrientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal) + { + // Align area position + areaPosition.Y = masterArea.Position.Y; + areaPosition.Height = masterArea.Position.Height; + } + + // Set new position + area.Position.SetPositionNoAuto(areaPosition.X, areaPosition.Y, areaPosition.Width, areaPosition.Height); + } + } + } + } + } + +#if Microsoft_CONTROL + + /// <summary> + /// Align chart areas cursor. + /// </summary> + /// <param name="changedArea">Changed chart area.</param> + /// <param name="orientation">Orientation of the changed cursor.</param> + /// <param name="selectionChanged">AxisName of change cursor or selection.</param> + internal void AlignChartAreasCursor(ChartArea changedArea, AreaAlignmentOrientations orientation, bool selectionChanged) + { + // Check if alignment required + if(IsAreasAlignmentRequired()) + { + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Get vertical areas alignment group using current area as a master + ArrayList alignGroup = GetAlignedAreasGroup( + area, + AreaAlignmentStyles.Cursor, + orientation); + + // Align each area in the group if it contains changed area + if(alignGroup.Contains(changedArea)) + { + // Set cursor position for all areas in the group + foreach(ChartArea groupArea in alignGroup) + { + groupArea.alignmentInProcess = true; + + if(orientation == AreaAlignmentOrientations.Vertical) + { + if(selectionChanged) + { + groupArea.CursorX.SelectionStart = changedArea.CursorX.SelectionStart; + groupArea.CursorX.SelectionEnd = changedArea.CursorX.SelectionEnd; + } + else + { + groupArea.CursorX.Position = changedArea.CursorX.Position; + } + } + if(orientation == AreaAlignmentOrientations.Horizontal) + { + if(selectionChanged) + { + groupArea.CursorY.SelectionStart = changedArea.CursorY.SelectionStart; + groupArea.CursorY.SelectionEnd = changedArea.CursorY.SelectionEnd; + } + else + { + groupArea.CursorY.Position = changedArea.CursorY.Position; + } + } + + groupArea.alignmentInProcess = false; + } + } + } + } + } + } + + /// <summary> + /// One of the chart areas was zoomed by the user. + /// </summary> + /// <param name="changedArea">Changed chart area.</param> + /// <param name="orientation">Orientation of the changed scaleView.</param> + /// <param name="disposeBufferBitmap">Area double fuffer image must be disposed.</param> + internal void AlignChartAreasZoomed(ChartArea changedArea, AreaAlignmentOrientations orientation, bool disposeBufferBitmap) + { + // Check if alignment required + if(IsAreasAlignmentRequired()) + { + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Get vertical areas alignment group using current area as a master + ArrayList alignGroup = GetAlignedAreasGroup( + area, + AreaAlignmentStyles.AxesView, + orientation); + + // Align each area in the group if it contains changed area + if(alignGroup.Contains(changedArea)) + { + // Set cursor position for all areas in the group + foreach(ChartArea groupArea in alignGroup) + { + // Clear image buffer + if(groupArea.areaBufferBitmap != null && disposeBufferBitmap) + { + groupArea.areaBufferBitmap.Dispose(); + groupArea.areaBufferBitmap = null; + } + + if(orientation == AreaAlignmentOrientations.Vertical) + { + groupArea.CursorX.SelectionStart = double.NaN; + groupArea.CursorX.SelectionEnd = double.NaN; + } + if(orientation == AreaAlignmentOrientations.Horizontal) + { + groupArea.CursorY.SelectionStart = double.NaN; + groupArea.CursorY.SelectionEnd = double.NaN; + } + } + } + } + } + } + } + +#endif //Microsoft_CONTROL + + /// <summary> + /// Align chart areas axes views. + /// </summary> + /// <param name="changedArea">Changed chart area.</param> + /// <param name="orientation">Orientation of the changed scaleView.</param> + internal void AlignChartAreasAxesView(ChartArea changedArea, AreaAlignmentOrientations orientation) + { + // Check if alignment required + if(IsAreasAlignmentRequired()) + { + // Loop through all chart areas + foreach(ChartArea area in this.ChartAreas) + { + + // Check if chart area is visible + if(area.Visible) + + { + // Get vertical areas alignment group using current area as a master + ArrayList alignGroup = GetAlignedAreasGroup( + area, + AreaAlignmentStyles.AxesView, + orientation); + + // Align each area in the group if it contains changed area + if(alignGroup.Contains(changedArea)) + { + // Set cursor position for all areas in the group + foreach(ChartArea groupArea in alignGroup) + { + groupArea.alignmentInProcess = true; + + if(orientation == AreaAlignmentOrientations.Vertical) + { + groupArea.AxisX.ScaleView.Position = changedArea.AxisX.ScaleView.Position; + groupArea.AxisX.ScaleView.Size = changedArea.AxisX.ScaleView.Size; + groupArea.AxisX.ScaleView.SizeType = changedArea.AxisX.ScaleView.SizeType; + + groupArea.AxisX2.ScaleView.Position = changedArea.AxisX2.ScaleView.Position; + groupArea.AxisX2.ScaleView.Size = changedArea.AxisX2.ScaleView.Size; + groupArea.AxisX2.ScaleView.SizeType = changedArea.AxisX2.ScaleView.SizeType; + } + if(orientation == AreaAlignmentOrientations.Horizontal) + { + groupArea.AxisY.ScaleView.Position = changedArea.AxisY.ScaleView.Position; + groupArea.AxisY.ScaleView.Size = changedArea.AxisY.ScaleView.Size; + groupArea.AxisY.ScaleView.SizeType = changedArea.AxisY.ScaleView.SizeType; + + groupArea.AxisY2.ScaleView.Position = changedArea.AxisY2.ScaleView.Position; + groupArea.AxisY2.ScaleView.Size = changedArea.AxisY2.ScaleView.Size; + groupArea.AxisY2.ScaleView.SizeType = changedArea.AxisY2.ScaleView.SizeType; + } + + groupArea.alignmentInProcess = false; + } + } + } + } + } + } + + #endregion + + #region Helper methods + + /// <summary> + /// Inspects the chart dimensions. + /// </summary> + /// <param name="width">The width.</param> + /// <param name="height">The height.</param> + internal void InspectChartDimensions(int width, int height) + { + if (this.Chart.IsDesignMode() && ((width * height) > (100 * 1024 *1024))) + { + throw new ArgumentException(SR.ExceptionChartOutOfLimits); + } + if (width < 0) + { + throw new ArgumentException(SR.ExceptionValueMustBeGreaterThan("Width", "0px")); + } + if (height < 0) + { + throw new ArgumentException(SR.ExceptionValueMustBeGreaterThan("Height", "0px")); + } + } + + /// <summary> + /// Loads chart appearance template from file. + /// </summary> + /// <param name="name">Template file name to load from.</param> + public void LoadTemplate(string name) + { + // Check arguments + if (name == null) + throw new ArgumentNullException("name"); + + // Load template data into the stream +#if Microsoft_CONTROL + Stream stream = new FileStream(name, FileMode.Open, FileAccess.Read); +#else // Microsoft_CONTROL + Stream stream = LoadTemplateData(name); +#endif // Microsoft_CONTROL + + // Load template from stream + LoadTemplate(stream); + + // Close tempate stream + stream.Close(); + } + + /// <summary> + /// Loads chart appearance template from stream. + /// </summary> + /// <param name="stream">Template stream to load from.</param> + public void LoadTemplate(Stream stream) + { + // Check arguments + if (stream == null) + throw new ArgumentNullException("stream"); + + ChartSerializer serializer = (ChartSerializer)this.Common.container.GetService(typeof(ChartSerializer)); + if (serializer != null) + { + // Save previous serializer properties + string oldSerializableContent = serializer.SerializableContent; + string oldNonSerializableContent = serializer.NonSerializableContent; + SerializationFormat oldFormat = serializer.Format; + bool oldIgnoreUnknownXmlAttributes = serializer.IsUnknownAttributeIgnored; + bool oldTemplateMode = serializer.IsTemplateMode; + + // Set serializer properties + serializer.Content = SerializationContents.Appearance; + serializer.SerializableContent += ",Chart.Titles,Chart.Annotations," + + "Chart.Legends,Legend.CellColumns,Legend.CustomItems,LegendItem.Cells," + + "Chart.Series,Series.*Style," + + "Chart.ChartAreas,ChartArea.Axis*," + + "Axis.*Grid,Axis.*TickMark, Axis.*Style," + + "Axis.StripLines, Axis.CustomLabels"; + serializer.Format = SerializationFormat.Xml; + serializer.IsUnknownAttributeIgnored = true; + serializer.IsTemplateMode = true; + + try + { + // Load template + serializer.Load(stream); + } + catch (Exception ex) + { + throw (new InvalidOperationException(ex.Message)); + } + finally + { + // Restore previous serializer properties + serializer.SerializableContent = oldSerializableContent; + serializer.NonSerializableContent = oldNonSerializableContent; + serializer.Format = oldFormat; + serializer.IsUnknownAttributeIgnored = oldIgnoreUnknownXmlAttributes; + serializer.IsTemplateMode = oldTemplateMode; + } + } + } + +#if !Microsoft_CONTROL + + /// <summary> + /// Loads template data from the URL. + /// </summary> + /// <param name="url">Template URL.</param> + /// <returns>Stream with template data or null if error.</returns> + private Stream LoadTemplateData(string url) + { + Debug.Assert(url != null, "LoadTemplateData: handed a null url string"); + + Stream dataStream = null; + + // Try to load as relative URL using the Control object + if(dataStream == null) + { + if (this.Common != null && + this.Common.Chart != null && + this.Common.Chart.Page != null) + { + try + { + dataStream = new FileStream( + this.Common.Chart.Page.MapPath(url), + FileMode.Open); + } + catch (NotSupportedException) + { + dataStream = null; + } + catch (SecurityException) + { + dataStream = null; + } + catch (FileNotFoundException) + { + dataStream = null; + } + catch (DirectoryNotFoundException) + { + dataStream = null; + } + catch (PathTooLongException) + { + dataStream = null; + } + } + } + + // Try to load image using the Web Request + if(dataStream == null) + { + Uri templateUri = null; + try + { + // Try to create URI directly from template URL (will work in case of absolute URL) + templateUri = new Uri(url); + } + catch (UriFormatException) + { + templateUri = null; + } + + // Make absolute URL using web form document URL + if(templateUri == null) + { + if (this.Common != null && this.Common.Chart != null) + { + string webFormUrl = this.Common.Chart.webFormDocumentURL; + int slashIndex = webFormUrl.LastIndexOf('/'); + if(slashIndex != -1) + { + webFormUrl = webFormUrl.Substring(0, slashIndex + 1); + } + + try + { + templateUri = new Uri(new Uri(webFormUrl), url); + } + catch (UriFormatException) + { + templateUri = null; + } + } + } + + // Load image from file or web resource + if(templateUri != null) + { + try + { + WebRequest request = WebRequest.Create(templateUri); + dataStream = request.GetResponse().GetResponseStream(); + } + catch (NotSupportedException) + { + dataStream = null; + } + catch (NotImplementedException) + { + dataStream = null; + } + catch (SecurityException) + { + dataStream = null; + } + } + } + + // Try to load as file + if(dataStream == null) + { + dataStream = new FileStream(url, FileMode.Open); + } + + return dataStream; + } + +#endif // Microsoft_CONTROL + + + +#if !Microsoft_CONTROL + + + /// <summary> + /// Writes chart map tag into the stream. + /// </summary> + /// <param name="output">Html writer to output the data to.</param> + /// <param name="mapName">Chart map name.</param> + internal void WriteChartMapTag(HtmlTextWriter output, string mapName) + { + output.WriteLine(); + output.AddAttribute(HtmlTextWriterAttribute.Name, mapName); + output.AddAttribute(HtmlTextWriterAttribute.Id, mapName); + output.RenderBeginTag(HtmlTextWriterTag.Map); + + //**************************************************** + //** Fire map areas customize event + //**************************************************** + + // Make sure only non-custom items are passed into the event handler + MapAreasCollection custCollection = new MapAreasCollection(); + + // Move all non-custom items + for (int index = 0; index < _mapAreas.Count; index++) + { + if (!_mapAreas[index].IsCustom) + { + custCollection.Add(_mapAreas[index]); + _mapAreas.RemoveAt(index); + --index; + } + } + + // Call a notification event, so that area items collection can be modified by user + Common.Chart.CallOnCustomizeMapAreas(custCollection); + + // Add customized items + foreach(MapArea area in custCollection) + { + area.IsCustom = false; + _mapAreas.Add(area); + } + + //**************************************************** + //** Add all map areas + //**************************************************** + foreach (MapArea area in _mapAreas) + { + area.RenderTag(output, this.Common.Chart); + } + // if this procedure is enforced to run the image maps have to have at least one map area. + if (_mapAreas.Count == 0) + { + output.Write("<area shape=\"rect\" coords=\"0,0,0,0\" alt=\"\" />"); + } + + //**************************************************** + //** End of the map + //**************************************************** + output.RenderEndTag(); + + return; + } + +#endif + + /// <summary> + /// Returns the default title from Titles collection. + /// </summary> + /// <param name="create">Create title if it doesn't exists.</param> + /// <returns>Default title.</returns> + internal Title GetDefaultTitle(bool create) + { + // Check if default title exists + Title defaultTitle = null; + foreach(Title title in this.Titles) + { + if(title.Name == "Default Title") + { + defaultTitle = title; + } + } + + // Create new default title + if(defaultTitle == null && create) + { + defaultTitle = new Title(); + defaultTitle.Name = "Default Title"; + this.Titles.Insert(0, defaultTitle); + } + + return defaultTitle; + } + + /// <summary> + /// Checks if tooltips are enabled + /// </summary> + /// <returns>true if tooltips enabled</returns> + private bool IsToolTipsEnabled() + { + + // Data series loop + foreach( Series series in Common.DataManager.Series ) + { + // Check series tooltips + if( series.ToolTip.Length > 0) + { + // ToolTips enabled + return true; + } + + // Check series tooltips + if( series.LegendToolTip.Length > 0 || + series.LabelToolTip.Length > 0) + { + // ToolTips enabled + return true; + } + + // Check point tooltips only for "non-Fast" chart types + if( !series.IsFastChartType() ) + { + // Data point loop + foreach( DataPoint point in series.Points ) + { + // ToolTip empty + if( point.ToolTip.Length > 0) + { + // ToolTips enabled + return true; + } + // ToolTip empty + if( point.LegendToolTip.Length > 0 || + point.LabelToolTip.Length > 0) + { + // ToolTips enabled + return true; + } + } + } + } + + // Legend items loop + foreach( Legend legend in Legends ) + { + foreach( LegendItem legendItem in legend.CustomItems ) + { + // ToolTip empty + if( legendItem.ToolTip.Length > 0 ) + { + return true; + } + } + } + + // Title items loop + foreach( Title title in Titles ) + { + // ToolTip empty + if( title.ToolTip.Length > 0 ) + { + return true; + } + } + + return false; + } + + #endregion + + #region IDisposable Members + /// <summary> + /// Releases unmanaged and - optionally - managed resources + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Dispose managed resources + if (ChartGraph != null) + { + ChartGraph.Dispose(); + ChartGraph = null; + } + if (_legends != null) + { + _legends.Dispose(); + _legends = null; + } + if (_titles != null) + { + _titles.Dispose(); + _titles = null; + } + if (_chartAreas != null) + { + _chartAreas.Dispose(); + _chartAreas = null; + } + if (_annotations != null) + { + _annotations.Dispose(); + _annotations = null; + } + if (hotRegionsList != null) + { + hotRegionsList.Dispose(); + hotRegionsList = null; + } + if (_fontCache != null) + { + _fontCache.Dispose(); + _fontCache = null; + } + if (_borderSkin != null) + { + _borderSkin.Dispose(); + _borderSkin = null; + } +#if ! Microsoft_CONTROL + if (_mapAreas != null) + { + _mapAreas.Dispose(); + _mapAreas = null; + } +#endif + +#if Microsoft_CONTROL + if (nonTopLevelChartBuffer != null) + { + nonTopLevelChartBuffer.Dispose(); + nonTopLevelChartBuffer = null; + } +#endif + } + base.Dispose(disposing); + } + + #endregion + } + + /// <summary> + /// Event arguments of Chart paint event. + /// </summary> +#if ASPPERM_35 + [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] +#endif + public class ChartPaintEventArgs : EventArgs + { + #region Fields + + // Private fields + private object _chartElement = null; + private ChartGraphics _chartGraph = null; + private CommonElements _common = null; + private Chart _chart = null; + private ElementPosition _position = null; + + #endregion + + #region Properties + + + /// <summary> + /// Gets the chart element of the event. + /// </summary> + /// <value>The chart element.</value> + public object ChartElement + { + get + { + return _chartElement; + } + } + + + /// <summary> + /// Gets the ChartGraphics object of the event. + /// </summary> + public ChartGraphics ChartGraphics + { + get + { + return _chartGraph; + } + } + + /// <summary> + /// Chart Common elements. + /// </summary> + internal CommonElements CommonElements + { + get + { + return _common; + } + } + + /// <summary> + /// Chart element position in relative coordinates of the event. + /// </summary> + public ElementPosition Position + { + get + { + return _position; + } + } + + /// <summary> + /// Chart object of the event. + /// </summary> + public Chart Chart + { + get + { + if (_chart == null && _common != null) + { + _chart = _common.Chart; + } + + return _chart; + } + } + + #endregion + + #region Methods + + /// <summary> + /// Default constructor is not accessible + /// </summary> + private ChartPaintEventArgs() + { + } + + /// <summary> + /// Paint event arguments constructor. + /// </summary> + /// <param name="chartElement">Chart element.</param> + /// <param name="chartGraph">Chart graphics.</param> + /// <param name="common">Common elements.</param> + /// <param name="position">Position.</param> + internal ChartPaintEventArgs(object chartElement, ChartGraphics chartGraph, CommonElements common, ElementPosition position) + { + this._chartElement = chartElement; + this._chartGraph = chartGraph; + this._common = common; + this._position = position; + } + + #endregion + } + + /// <summary> + /// Event arguments of localized numbers formatting event. + /// </summary> +#if ASPPERM_35 + [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] + [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] +#endif + public class FormatNumberEventArgs : EventArgs + { + #region Fields + + // Private fields + private double _value; + private string _format; + private string _localizedValue; + private ChartValueType _valueType = ChartValueType.Auto; + private object _senderTag; + private ChartElementType _elementType = ChartElementType.Nothing; + + #endregion + + #region Properties + + /// <summary> + /// Value to be formatted. + /// </summary> + public double Value + { + get { return this._value; } + } + + /// <summary> + /// Localized text. + /// </summary> + public string LocalizedValue + { + get { return _localizedValue; } + set { _localizedValue = value; } + } + + /// <summary> + /// Format string. + /// </summary> + public string Format + { + get { return _format; } + } + + /// <summary> + /// Value type. + /// </summary> + public ChartValueType ValueType + { + get { return _valueType; } + } + + /// <summary> + /// The sender object of the event. + /// </summary> + public object SenderTag + { + get { return _senderTag; } + } + + /// <summary> + /// Chart element type. + /// </summary> + public ChartElementType ElementType + { + get { return _elementType; } + } + + #endregion + + #region Methods + + /// <summary> + /// Default constructor is not accessible + /// </summary> + private FormatNumberEventArgs() + { + } + + /// <summary> + /// Object constructor. + /// </summary> + /// <param name="value">Value to be formatted.</param> + /// <param name="format">Format string.</param> + /// <param name="valueType">Value type..</param> + /// <param name="localizedValue">Localized value.</param> + /// <param name="senderTag">Chart element object tag.</param> + /// <param name="elementType">Chart element type.</param> + internal FormatNumberEventArgs(double value, string format, ChartValueType valueType, string localizedValue, object senderTag, ChartElementType elementType) + { + this._value = value; + this._format = format; + this._valueType = valueType; + this._localizedValue = localizedValue; + this._senderTag = senderTag; + this._elementType = elementType; + } + + #endregion + } + + #region FontCache + /// <summary> + /// Font cache class helps ChartElements to reuse the Font instances + /// </summary> + internal class FontCache : IDisposable + { + #region Static + + // Default font family name + private static string _defaultFamilyName; + + /// <summary> + /// Gets the default font family name. + /// </summary> + /// <value>The default font family name.</value> + public static string DefaultFamilyName + { + get + { + if (_defaultFamilyName == null) + { + // Find the "Microsoft Sans Serif" font + foreach (FontFamily fontFamily in FontFamily.Families) + { + if (fontFamily.Name == "Microsoft Sans Serif") + { + _defaultFamilyName = fontFamily.Name; + break; + } + } + // Not found - use the default Sans Serif font + if (_defaultFamilyName == null) + { + _defaultFamilyName = FontFamily.GenericSansSerif.Name; + } + } + return _defaultFamilyName; + } + } + #endregion + + #region Fields + + // Cached fonts dictionary + private Dictionary<KeyInfo, Font> _fontCache = new Dictionary<KeyInfo, Font>(new KeyInfo.EqualityComparer()); + + #endregion // Fields + + #region Properties + /// <summary> + /// Gets the default font. + /// </summary> + /// <value>The default font.</value> + public Font DefaultFont + { + get { return this.GetFont(DefaultFamilyName, 8); } + } + + /// <summary> + /// Gets the default font. + /// </summary> + /// <value>The default font.</value> + public Font DefaultBoldFont + { + get { return this.GetFont(DefaultFamilyName, 8, FontStyle.Bold); } + } + #endregion + + #region Methods + + /// <summary> + /// Gets the font. + /// </summary> + /// <param name="familyName">Name of the family.</param> + /// <param name="size">The size.</param> + /// <returns>Font instance</returns> + public Font GetFont(string familyName, int size) + { + KeyInfo key = new KeyInfo(familyName, size); + if (!this._fontCache.ContainsKey(key)) + { + this._fontCache.Add(key, new Font(familyName, size)); + } + return this._fontCache[key]; + } + + /// <summary> + /// Gets the font. + /// </summary> + /// <param name="familyName">Name of the family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + /// <returns>Font instance</returns> + public Font GetFont(string familyName, float size, FontStyle style) + { + KeyInfo key = new KeyInfo(familyName, size, style); + if (!this._fontCache.ContainsKey(key)) + { + this._fontCache.Add(key, new Font(familyName, size, style)); + } + return this._fontCache[key]; + } + + /// <summary> + /// Gets the font. + /// </summary> + /// <param name="family">The family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + /// <returns>Font instance</returns> + public Font GetFont(FontFamily family, float size, FontStyle style) + { + KeyInfo key = new KeyInfo(family, size, style); + if (!this._fontCache.ContainsKey(key)) + { + this._fontCache.Add(key, new Font(family, size, style)); + } + return this._fontCache[key]; + } + + /// <summary> + /// Gets the font. + /// </summary> + /// <param name="family">The family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + /// <param name="unit">The unit.</param> + /// <returns>Font instance</returns> + public Font GetFont(FontFamily family, float size, FontStyle style, GraphicsUnit unit) + { + KeyInfo key = new KeyInfo(family, size, style, unit); + if (!this._fontCache.ContainsKey(key)) + { + this._fontCache.Add(key, new Font(family, size, style, unit)); + } + return this._fontCache[key]; + } + + #endregion + + #region IDisposable Members + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + foreach (Font font in _fontCache.Values) + { + font.Dispose(); + } + _fontCache.Clear(); + GC.SuppressFinalize(this); + } + + #endregion + + #region FontKeyInfo struct + /// <summary> + /// Font key info + /// </summary> + private class KeyInfo + { + string _familyName; + float _size = 8; + GraphicsUnit _unit = GraphicsUnit.Point; + FontStyle _style = FontStyle.Regular; + int _gdiCharSet = 1; + + /// <summary> + /// Initializes a new instance of the <see cref="KeyInfo"/> class. + /// </summary> + /// <param name="familyName">Name of the family.</param> + /// <param name="size">The size.</param> + public KeyInfo(string familyName, float size) + { + this._familyName = familyName; + this._size = size; + } + /// <summary> + /// Initializes a new instance of the <see cref="KeyInfo"/> class. + /// </summary> + /// <param name="familyName">Name of the family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + public KeyInfo(string familyName, float size, FontStyle style) + { + this._familyName = familyName; + this._size = size; + this._style = style; + } + /// <summary> + /// Initializes a new instance of the <see cref="KeyInfo"/> class. + /// </summary> + /// <param name="family">The family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + public KeyInfo(FontFamily family, float size, FontStyle style) + { + this._familyName = family.ToString(); + this._size = size; + this._style = style; + } + /// <summary> + /// Initializes a new instance of the <see cref="KeyInfo"/> class. + /// </summary> + /// <param name="family">The family.</param> + /// <param name="size">The size.</param> + /// <param name="style">The style.</param> + /// <param name="unit">The unit.</param> + public KeyInfo(FontFamily family, float size, FontStyle style, GraphicsUnit unit) + { + this._familyName = family.ToString(); + this._size = size; + this._style = style; + this._unit = unit; + } + + #region IEquatable<FontKeyInfo> Members + /// <summary> + /// KeyInfo equality comparer + /// </summary> + internal class EqualityComparer : IEqualityComparer<KeyInfo> + { + /// <summary> + /// Determines whether the specified objects are equal. + /// </summary> + /// <param name="x">The first object of type <paramref name="x"/> to compare.</param> + /// <param name="y">The second object of type <paramref name="y"/> to compare.</param> + /// <returns> + /// true if the specified objects are equal; otherwise, false. + /// </returns> + public bool Equals(KeyInfo x, KeyInfo y) + { + return + x._size == y._size && + x._familyName == y._familyName && + x._unit == y._unit && + x._style == y._style && + x._gdiCharSet == y._gdiCharSet; + } + + /// <summary> + /// Returns a hash code for the specified object. + /// </summary> + /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param> + /// <returns>A hash code for the specified object.</returns> + /// <exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception> + public int GetHashCode(KeyInfo obj) + { + return obj._familyName.GetHashCode() ^ obj._size.GetHashCode(); + } + } + #endregion + } + #endregion + } + #endregion + + +} |