// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // Purpose: 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. // using System; using System.Windows.Forms; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Data; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using FastReport.DataVisualization.Charting.Borders3D; using FastReport.DataVisualization.Charting.Utilities; namespace FastReport.DataVisualization.Charting { using FontStyle = System.Drawing.FontStyle; /// /// ChartImage class adds image type and data binding functionality to /// the base ChartPicture class. /// 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; #endregion #region Constructor /// /// Chart internal constructor. /// /// Service container internal ChartImage(IServiceContainer container) : base(container) { } #endregion // Constructor #region Properties /// /// Gets or sets the data source for the Chart object. /// [ 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; } } } /// /// Image compression value /// [ SRCategory("CategoryAttributeImage"), Bindable(true), DefaultValue(0), SRDescription("DescriptionAttributeChartImage_Compression"), ] 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 /// /// Saves image into the metafile stream. /// /// Image stream. /// Image stream. [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 { #if DESIGNER System.Security.Permissions.SecurityPermission securityPermission = new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode); securityPermission.Demand(); #endif 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 (IGraphics metaGraphics = new FastReport.GdiGraphics(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); } /// /// Create Image and draw chart picture /// 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. IGraphics offScreen = new FastReport.GdiGraphics(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 /// /// Checks if the type of the data source is valid. /// /// Data source object to test. /// True if valid data source object. 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.Common.DbCommand || dataSource is System.Data.Common.DbDataAdapter || // ADDED: for VS2005 compatibility, DT Nov 25, 2005 dataSource.GetType().GetInterface("IDataSource") != null // END ADDED ) ) { return true; } return false; } /// /// Gets an list of the data source member names. /// /// Data source object to get the members for. /// Indicates that member will be used for Y values. /// List of member names. [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.Common.DbDataAdapter) { dataTable = new DataTable(); dataTable.Locale = CultureInfo.CurrentCulture; dataTable = ((System.Data.Common.DbDataAdapter)dataSource).FillSchema(dataTable, SchemaType.Mapped); } else if (dataSource is System.Data.Common.DbDataReader) { // Add table columns names for (int fieldIndex = 0; fieldIndex < ((System.Data.Common.DbDataReader)dataSource).FieldCount; fieldIndex++) { if (!usedForYValue || ((System.Data.Common.DbDataReader)dataSource).GetFieldType(fieldIndex) != typeof(string)) { names.Add(((System.Data.Common.DbDataReader)dataSource).GetName(fieldIndex)); } } } else if (dataSource is System.Data.Common.DbCommand) { System.Data.Common.DbCommand command = (System.Data.Common.DbCommand)dataSource; if (command.Connection != null) { command.Connection.Open(); System.Data.Common.DbDataReader 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; } /// /// Data binds control to the data source /// [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.Common.DbDataAdapter) { dataSource = ((System.Data.Common.DbDataAdapter)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.Common.DbCommand) { System.Data.Common.DbCommand command = (System.Data.Common.DbCommand)dataSource; command.Connection.Open(); System.Data.Common.DbDataReader 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); } } /// /// Data binds control to the data source /// /// Data source to bind to. /// List of series to bind. 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); } } /// /// Aligns data points using their axis labels. /// /// Indicates if points should be sorted by axis labels. /// Sorting pointSortOrder. 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); } } } /// /// Aligns data points using their axis labels. /// /// List of series to align. /// Indicates if points should be sorted by axis labels. /// Sorting order. 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); } } } } } /// /// 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. /// /// Data source. /// Name of the field used to group data into series. /// Name of the field for X values. /// Comma separated name(s) of the field(s) for Y value(s). /// Other point properties binding rule in format: PointProperty=Field[{Format}] [,PointProperty=Field[{Format}]]. For example: "Tooltip=Price{C1},Url=WebSiteName". /// Indicates that series should be sorted by group field. /// Series sorting order by group field. 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); } } /// /// 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. /// /// Data source. /// Name of the field for series X values. 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 } /// /// ChartPicture class represents chart content like legends, titles, /// chart areas and series. It provides methods for positioning and /// drawing all chart elements. /// internal class ChartPicture : ChartElement, IServiceProvider { #region Fields /// /// Indicates that chart exceptions should be suppressed. /// 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; // 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 BeforePaint; internal event EventHandler 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; // 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; #endregion #region Constructors /// /// Constructor. /// /// Service container 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; } /// /// Returns Chart service object /// /// Service AxisName /// Chart picture [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 /// /// Performs empty painting. /// 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; this.Common.HotRegionsList.hitTestCalled = true; // 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. IGraphics offScreen = new FastReport.GdiGraphics(image); // Connect Graphics object with Chart Graphics object ChartGraph.Graphics = offScreen; // Remember the previous dirty flag bool oldDirtyFlag = this.Common.Chart.dirtyFlag; Paint(ChartGraph.Graphics, false); image.Dispose(); // Restore the previous dirty flag this.Common.Chart.dirtyFlag = oldDirtyFlag; // Disable selection mode this.isSelectionMode = false; // Set process Mode to hot regions this.Common.HotRegionsList.ProcessChartMode |= ProcessMode.HotRegions; } /// /// Gets text rendering quality. /// /// Text rendering quality. 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; } /// /// This function paints a chart. /// /// The graph provides drawing object to the display device. A Graphics object is associated with a specific device context. /// Indicates that only chart top level elements like cursors, selection or annotation objects must be redrawn. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "3#svg")] internal void Paint( IGraphics graph, bool paintTopLevelElementOnly ) { // Reset restored and saved backgound flags this.backgroundRestored = false; // 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; } 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(); } // 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(); } } } } /// /// Invoke before paint delegates. /// /// Event arguments. protected virtual void OnBeforePaint(ChartPaintEventArgs e) { if (BeforePaint != null) { //Invokes the delegates. BeforePaint(this, e); } } /// /// Invoke after paint delegates. /// /// Event arguments. protected virtual void OnAfterPaint(ChartPaintEventArgs e) { if (AfterPaint != null) { //Invokes the delegates. AfterPaint(this, e); } } internal override void Invalidate() { base.Invalidate(); if (Chart!=null) Chart.Invalidate(); } #endregion #region Resizing methods /// /// Resize the chart picture. /// /// Chart graphics. public void Resize(ChartGraphics chartGraph) { Resize(chartGraph, false); } /// /// Resize the chart picture. /// /// Chart graphics. /// Indicates that only chart area position is calculated. 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 = 96f;// 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); } } /// /// 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. /// 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(); } } } } /// /// RecalculateAxesScale the chart picture. /// 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) /// /// Gets or sets the RightToLeft type. /// [ DefaultValue(RightToLeft.No) ] public RightToLeft RightToLeft { get { return this.Common.Chart.RightToLeft; } set { this.Common.Chart.RightToLeft = value; } } /// /// Indicates that non-critical chart exceptions will be suppressed. /// [ SRCategory("CategoryAttributeMisc"), DefaultValue(false), SRDescription("DescriptionAttributeSuppressExceptions"), ] internal bool SuppressExceptions { set { _suppressExceptions = value; } get { return _suppressExceptions; } } /// /// Chart border skin style. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(BorderSkinStyle.None), SRDescription("DescriptionAttributeBorderSkin"), ] public BorderSkin BorderSkin { get { return _borderSkin; } set { _borderSkin = value; } } /// /// Reference to chart area collection /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), SRDescription("DescriptionAttributeChartAreas"), #if DESIGNER Editor(typeof(ChartCollectionEditor), typeof(UITypeEditor)) #endif ] public ChartAreaCollection ChartAreas { get { return _chartAreas; } } /// /// Chart legend collection. /// [ SRCategory("CategoryAttributeChart"), SRDescription("DescriptionAttributeLegends"), #if DESIGNER Editor(typeof(LegendCellCollectionEditor), typeof(UITypeEditor)) #endif ] public LegendCollection Legends { get { return _legends; } } /// /// Chart title collection. /// [ SRCategory("CategoryAttributeCharttitle"), SRDescription("DescriptionAttributeTitles"), #if DESIGNER Editor(typeof(ChartCollectionEditor), typeof(UITypeEditor)) #endif ] public TitleCollection Titles { get { return _titles; } } /// /// Chart annotation collection. /// [ SRCategory("CategoryAttributeChart"), SRDescription("DescriptionAttributeAnnotations3"), #if DESIGNER Editor(typeof(AnnotationCollectionEditor), (typeof(UITypeEditor))) #endif ] public AnnotationCollection Annotations { get { return _annotations; } } /// /// Background color for the Chart /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(typeof(Color), "White"), SRDescription("DescriptionAttributeBackColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] public Color BackColor { get { return _backColor; } set { _backColor = value; } } /// /// Border color for the Chart /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(typeof(Color), "White"), SRDescription("DescriptionAttributeBorderColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] public Color BorderColor { get { return _borderColor; } set { _borderColor = value; } } /// /// Chart width /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(300), SRDescription("DescriptionAttributeWidth"), ] public int Width { get { return _width; } set { this.InspectChartDimensions(value, this.Height); _width = value; Common.Width = _width; } } /// /// Series Data Manipulator /// [ SRCategory("CategoryAttributeData"), SRDescription("DescriptionAttributeDataManipulator"), Browsable(false), DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), SerializationVisibilityAttribute(SerializationVisibility.Hidden) ] public DataManipulator DataManipulator { get { return _dataManipulator; } } /// /// Chart height /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(300), SRDescription("DescriptionAttributeHeight3"), ] public int Height { get { return _height; } set { this.InspectChartDimensions(this.Width, value); _height = value; Common.Height = value; } } /// /// Back Hatch style /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(ChartHatchStyle.None), SRDescription("DescriptionAttributeBackHatchStyle"), #if DESIGNER Editor(typeof(HatchStyleEditor), typeof(UITypeEditor)) #endif ] public ChartHatchStyle BackHatchStyle { get { return _backHatchStyle; } set { _backHatchStyle = value; } } /// /// Chart area background image /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(""), SRDescription("DescriptionAttributeBackImage"), #if DESIGNER Editor(typeof(ImageValueEditor), (typeof(UITypeEditor))), #endif NotifyParentPropertyAttribute(true) ] public string BackImage { get { return _backImage; } set { _backImage = value; } } /// /// Chart area background image drawing mode. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(ChartImageWrapMode.Tile), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeImageWrapMode"), ] public ChartImageWrapMode BackImageWrapMode { get { return _backImageWrapMode; } set { _backImageWrapMode = value; } } /// /// Background image transparent color. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(typeof(Color), ""), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeImageTransparentColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] public Color BackImageTransparentColor { get { return _backImageTransparentColor; } set { _backImageTransparentColor = value; } } /// /// Background image alignment used by ClampUnscale drawing mode. /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(ChartImageAlignmentStyle.TopLeft), NotifyParentPropertyAttribute(true), SRDescription("DescriptionAttributeBackImageAlign"), ] public ChartImageAlignmentStyle BackImageAlignment { get { return _backImageAlign; } set { _backImageAlign = value; } } /// /// Indicates that smoothing is applied while drawing shadows. /// [ SRCategory("CategoryAttributeImage"), Bindable(true), DefaultValue(true), SRDescription("DescriptionAttributeSoftShadows3"), ] public bool IsSoftShadows { get { return _isSoftShadows; } set { _isSoftShadows = value; } } /// /// Specifies whether smoothing (antialiasing) is applied while drawing chart. /// [ SRCategory("CategoryAttributeImage"), Bindable(true), DefaultValue(typeof(AntiAliasingStyles), "All"), SRDescription("DescriptionAttributeAntiAlias"), ] public AntiAliasingStyles AntiAliasing { get { return _antiAliasing; } set { _antiAliasing = value; } } /// /// Specifies the quality of text antialiasing. /// [ SRCategory("CategoryAttributeImage"), Bindable(true), DefaultValue(typeof(TextAntiAliasingQuality), "High"), SRDescription("DescriptionAttributeTextAntiAliasingQuality"), ] public TextAntiAliasingQuality TextAntiAliasingQuality { get { return _textAntiAliasingQuality; } set { _textAntiAliasingQuality = value; } } /// /// A type for the background gradient /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(GradientStyle.None), SRDescription("DescriptionAttributeBackGradientStyle"), #if DESIGNER Editor(typeof(GradientEditor), typeof(UITypeEditor)) #endif ] public GradientStyle BackGradientStyle { get { return _backGradientStyle; } set { _backGradientStyle = value; } } /// /// The second color which is used for a gradient /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(typeof(Color), ""), SRDescription("DescriptionAttributeBackSecondaryColor"), TypeConverter(typeof(ColorConverter)), #if DESIGNER Editor(typeof(ChartColorEditor), typeof(UITypeEditor)) #endif ] public Color BackSecondaryColor { get { return _backSecondaryColor; } set { _backSecondaryColor = value; } } /// /// The width of the border line /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(1), SRDescription("DescriptionAttributeChart_BorderlineWidth"), ] public int BorderWidth { get { return _borderWidth; } set { if(value < 0) { throw(new ArgumentOutOfRangeException("value", SR.ExceptionChartBorderIsNegative)); } _borderWidth = value; } } /// /// The style of the border line /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), DefaultValue(ChartDashStyle.NotSet), SRDescription("DescriptionAttributeBorderDashStyle"), ] public ChartDashStyle BorderDashStyle { get { return _borderDashStyle; } set { _borderDashStyle = value; } } /// /// Gets the font cache. /// /// The font cache. internal FontCache FontCache { get { return _fontCache; } } #endregion #region Chart areas alignment methods /// /// Checks if any of the chart areas are aligned. /// Also checks if the chart ares name in AlignWithChartArea property is valid. /// /// True if at least one area requires alignment. 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; } /// /// Creates a list of the aligned chart areas. /// /// Master chart area. /// Alignment type. /// Vertical or Horizontal orientation. /// List of areas that area aligned to the master area. 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 client 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; } /// /// Performs specified type of alignment for the chart areas. /// /// Alignment type required. 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); } } } } } /// /// Align inner plot position of the chart areas in the group. /// /// List of areas in the group. /// Group orientation. 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); } } } /// /// Aligns positions of the chart areas. /// 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); } } } } } /// /// Align chart areas cursor. /// /// Changed chart area. /// Orientation of the changed cursor. /// AxisName of change cursor or selection. 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; } } } } } } /// /// One of the chart areas was zoomed by the user. /// /// Changed chart area. /// Orientation of the changed scaleView. /// Area double fuffer image must be disposed. 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; } } } } } } } /// /// Align chart areas axes views. /// /// Changed chart area. /// Orientation of the changed scaleView. 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 /// /// Inspects the chart dimensions. /// /// The width. /// The height. 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")); } } /// /// Loads chart appearance template from file. /// /// Template file name to load from. public void LoadTemplate(string name) { // Check arguments if (name == null) throw new ArgumentNullException("name"); // Load template data into the stream Stream stream = new FileStream(name, FileMode.Open, FileAccess.Read); // Load template from stream LoadTemplate(stream); // Close tempate stream stream.Close(); } /// /// Loads chart appearance template from stream. /// /// Template stream to load from. 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; } } } /// /// Returns the default title from Titles collection. /// /// Create title if it doesn't exists. /// Default title. 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; } /// /// Checks if tooltips are enabled /// /// true if tooltips enabled 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 /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { // 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 (nonTopLevelChartBuffer != null) { nonTopLevelChartBuffer.Dispose(); nonTopLevelChartBuffer = null; } } base.Dispose(disposing); } #endregion } /// /// Event arguments of Chart paint event. /// 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 /// /// Gets the chart element of the event. /// /// The chart element. public object ChartElement { get { return _chartElement; } } /// /// Gets the ChartGraphics object of the event. /// public ChartGraphics ChartGraphics { get { return _chartGraph; } } /// /// Chart Common elements. /// internal CommonElements CommonElements { get { return _common; } } /// /// Chart element position in relative coordinates of the event. /// public ElementPosition Position { get { return _position; } } /// /// Chart object of the event. /// public Chart Chart { get { if (_chart == null && _common != null) { _chart = _common.Chart; } return _chart; } } #endregion #region Methods /// /// Default constructor is not accessible /// private ChartPaintEventArgs() { } /// /// Paint event arguments constructor. /// /// Chart element. /// Chart graphics. /// Common elements. /// Position. internal ChartPaintEventArgs(object chartElement, ChartGraphics chartGraph, CommonElements common, ElementPosition position) { this._chartElement = chartElement; this._chartGraph = chartGraph; this._common = common; this._position = position; } #endregion } /// /// Event arguments of localized numbers formatting event. /// 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 /// /// Value to be formatted. /// public double Value { get { return this._value; } } /// /// Localized text. /// public string LocalizedValue { get { return _localizedValue; } set { _localizedValue = value; } } /// /// Format string. /// public string Format { get { return _format; } } /// /// Value type. /// public ChartValueType ValueType { get { return _valueType; } } /// /// The sender object of the event. /// public object SenderTag { get { return _senderTag; } } /// /// Chart element type. /// public ChartElementType ElementType { get { return _elementType; } } #endregion #region Methods /// /// Default constructor is not accessible /// private FormatNumberEventArgs() { } /// /// Object constructor. /// /// Value to be formatted. /// Format string. /// Value type.. /// Localized value. /// Chart element object tag. /// Chart element type. 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 /// /// Font cache class helps ChartElements to reuse the Font instances /// internal class FontCache : IDisposable { #region Static // Default font family name private static string _defaultFamilyName; /// /// Gets the default font family name. /// /// The default font family name. 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 _fontCache = new Dictionary(new KeyInfo.EqualityComparer()); #endregion // Fields #region Properties /// /// Gets the default font. /// /// The default font. public Font DefaultFont { get { return this.GetFont(DefaultFamilyName, 8); } } /// /// Gets the default font. /// /// The default font. public Font DefaultBoldFont { get { return this.GetFont(DefaultFamilyName, 8, FontStyle.Bold); } } #endregion #region Methods /// /// Gets the font. /// /// Name of the family. /// The size. /// Font instance 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]; } /// /// Gets the font. /// /// Name of the family. /// The size. /// The style. /// Font instance 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]; } /// /// Gets the font. /// /// The family. /// The size. /// The style. /// Font instance 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]; } /// /// Gets the font. /// /// The family. /// The size. /// The style. /// The unit. /// Font instance 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 /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { foreach (Font font in _fontCache.Values) { font.Dispose(); } _fontCache.Clear(); GC.SuppressFinalize(this); } #endregion #region FontKeyInfo struct /// /// Font key info /// private class KeyInfo { string _familyName; float _size = 8; GraphicsUnit _unit = GraphicsUnit.Point; FontStyle _style = FontStyle.Regular; int _gdiCharSet = 1; /// /// Initializes a new instance of the class. /// /// Name of the family. /// The size. public KeyInfo(string familyName, float size) { this._familyName = familyName; this._size = size; } /// /// Initializes a new instance of the class. /// /// Name of the family. /// The size. /// The style. public KeyInfo(string familyName, float size, FontStyle style) { this._familyName = familyName; this._size = size; this._style = style; } /// /// Initializes a new instance of the class. /// /// The family. /// The size. /// The style. public KeyInfo(FontFamily family, float size, FontStyle style) { this._familyName = family.ToString(); this._size = size; this._style = style; } /// /// Initializes a new instance of the class. /// /// The family. /// The size. /// The style. /// The unit. 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 Members /// /// KeyInfo equality comparer /// internal class EqualityComparer : IEqualityComparer { /// /// Determines whether the specified objects are equal. /// /// The first object of type to compare. /// The second object of type to compare. /// /// true if the specified objects are equal; otherwise, false. /// 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; } /// /// Returns a hash code for the specified object. /// /// The for which a hash code is to be returned. /// A hash code for the specified object. /// The type of is a reference type and is null. public int GetHashCode(KeyInfo obj) { return obj._familyName.GetHashCode() ^ obj._size.GetHashCode(); } } #endregion } #endregion } #endregion }