//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant //================================================================= // File: DataManager.cs // // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting.Data // // Classes: DataManager // // Purpose: Series storage and manipulation class. // // Reviewed: AG - Aug 1, 2002; GS - Aug 7, 2002 // //=================================================================== #region Used namespaces using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Data; using System.Drawing; using System.Drawing.Design; #if Microsoft_CONTROL using System.Windows.Forms.DataVisualization.Charting; using System.Windows.Forms.DataVisualization.Charting.Data; using System.Windows.Forms.DataVisualization.Charting.ChartTypes; using System.Windows.Forms.DataVisualization.Charting.Utilities; using System.Windows.Forms.DataVisualization.Charting.Borders3D; #else using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.DataVisualization.Charting; using System.Web.UI.DataVisualization.Charting.Data; using System.Web.UI.DataVisualization.Charting.Utilities; using System.Web.UI.DataVisualization.Charting.ChartTypes; #endif #endregion #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting.Data #else namespace System.Web.UI.DataVisualization.Charting.Data #endif { /// /// Data Manager. /// internal class DataManager : ChartElement, IServiceProvider { #region Fields // Series collection private SeriesCollection _series = null; // Servise container reference internal IServiceContainer serviceContainer = null; // Chart color palette private ChartColorPalette _colorPalette = ChartColorPalette.BrightPastel; #endregion #region Constructors and initialization /// /// Data manager public constructor /// /// Service container object. public DataManager(IServiceContainer container) { if(container == null) { throw(new ArgumentNullException(SR.ExceptionInvalidServiceContainer)); } serviceContainer = container; Common = new CommonElements(container); _series = new SeriesCollection(this); } /// /// Returns Data Manager service object. /// /// Service type requested. /// Data Manager service object. [EditorBrowsableAttribute(EditorBrowsableState.Never)] object IServiceProvider.GetService(Type serviceType) { if(serviceType == typeof(DataManager)) { return this; } throw (new ArgumentException( SR.ExceptionDataManagerUnsupportedType(serviceType.ToString()))); } /// /// Initialize data manger object /// internal void Initialize() { // Attach to the Chart Picture painting events ChartImage chartPicture = (ChartImage)serviceContainer.GetService(typeof(ChartImage)); chartPicture.BeforePaint += new EventHandler(this.ChartPicture_BeforePaint); chartPicture.AfterPaint += new EventHandler(this.ChartPicture_AfterPaint); } #endregion #region Chart picture painting events hanlers internal override void Invalidate() { base.Invalidate(); #if Microsoft_CONTROL if (Chart!=null) Chart.Invalidate(); #endif } /// /// Event fired when chart picture is going to be painted. /// /// Sender object. /// Event arguments. private void ChartPicture_BeforePaint(object sender, ChartPaintEventArgs e) { // Prepare series for drawing int markerIndex = 1; for(int index = 0; index < this.Series.Count; index++) { Series series = this.Series[index]; // Reset series "X values are zeros" flag series.xValuesZerosChecked = false; series.xValuesZeros = false; // Set series colors from palette IChartType chartType = e.CommonElements.ChartTypeRegistry.GetChartType(series.ChartTypeName); bool paletteColorsInPoints = chartType.ApplyPaletteColorsToPoints; // if the series palette is set the we can color all data points, even on column chart. if (series.Palette != ChartColorPalette.None) { paletteColorsInPoints = true; } this.PrepareData( paletteColorsInPoints, series.Name); // Clear temp. marker style if(series.tempMarkerStyleIsSet) { series.MarkerStyle = MarkerStyle.None; series.tempMarkerStyleIsSet = false; } // Set marker style for chart types based on markes if(chartType.GetLegendImageStyle(series) == LegendImageStyle.Marker && series.MarkerStyle == MarkerStyle.None) { series.MarkerStyle = (MarkerStyle)markerIndex++; series.tempMarkerStyleIsSet = true; if(markerIndex > 9) { markerIndex = 1; } } } } /// /// Event fired after chart picture was painted. /// /// Sender object. /// Event arguments. private void ChartPicture_AfterPaint(object sender, ChartPaintEventArgs e) { Chart control = (Chart)serviceContainer.GetService(typeof(Chart)); if(control != null) { // Clean up series after drawing for(int index = 0; index < this.Series.Count; index++) { Series series = this.Series[index]; if(series.UnPrepareData(control.Site)) { --index; } } } } #endregion #region Series data preparation methods /// /// Apply palette colors to the data series if UsePaletteColors property is set. /// internal void ApplyPaletteColors() { ChartColorPalette palette = this.Palette; // switch to default pallette if is none and custom collors array is empty. if (palette == ChartColorPalette.None && this.PaletteCustomColors.Length == 0) { palette = ChartColorPalette.BrightPastel; } // Get palette colors int colorIndex = 0; Color[] paletteColors = (palette == ChartColorPalette.None) ? this.PaletteCustomColors : ChartPaletteColors.GetPaletteColors(palette); foreach (Series dataSeries in _series) { // Check if chart area name is valid bool validAreaName = false; if (Chart!=null) { validAreaName = Chart.ChartAreas.IsNameReferenceValid(dataSeries.ChartArea); } // Change color of the series only if valid chart area name is specified if(validAreaName) { // Change color of the series only if default color is set if(dataSeries.Color == Color.Empty || dataSeries.tempColorIsSet) { dataSeries.color = paletteColors[colorIndex++]; dataSeries.tempColorIsSet = true; if(colorIndex >= paletteColors.Length) { colorIndex = 0; } } } } } /// /// Called just before the data from the series to be used to perform these operations: /// - apply palette colors to the data series /// - prepare data in series /// /// If true each data point will be assigned a color from the palette (if it's set) /// List of series indexes, which requires data preparation internal void PrepareData(bool pointsApplyPaletteColors, params string[] series) { this.ApplyPaletteColors(); // Prepare data in series Chart control = (Chart)serviceContainer.GetService(typeof(Chart)); if(control != null) { foreach(string seriesName in series) { this.Series[seriesName].PrepareData(pointsApplyPaletteColors); } } } #endregion #region Series Min/Max values methods /// /// This method checks if data point should be skipped. This /// method will return true if data point is empty. /// /// Data point /// This method returns true if data point is empty. private bool IsPointSkipped( DataPoint point ) { if( point.IsEmpty ) { return true; } return false; } /// /// Gets max number of data points in specified series. /// /// Series IDs /// Maximum number of data points internal int GetNumberOfPoints(params string[] series) { int numberOfPoints = 0; foreach(string seriesName in series) { numberOfPoints = Math.Max(numberOfPoints, this._series[seriesName].Points.Count); } return numberOfPoints; } /// /// Gets maximum Y value from many series /// /// Index of Y value to use /// Series IDs /// Maximum Y value internal double GetMaxYValue(int valueIndex, params string[] series) { double returnValue = Double.MinValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.YValues[valueIndex])) { returnValue = Math.Max(returnValue, seriesPoint.YValues[valueIndex]); } } } return returnValue; } /// /// Get Maximum value for Y and and Radius (Y2) ( used for bubble chart ) /// /// Chart Area /// Series IDs /// Maximum Y value internal double GetMaxYWithRadiusValue( ChartArea area, params string[] series ) { double returnValue = Double.MinValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.YValues[0])) { if (seriesPoint.YValues.Length > 1) { returnValue = Math.Max(returnValue, seriesPoint.YValues[0] + BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], true)); } else { returnValue = Math.Max(returnValue, seriesPoint.YValues[0]); } } } } return returnValue; } /// /// Get Maximum value for X and Radius (Y2) ( used for bubble chart ) /// /// Chart Area /// Series IDs /// Maximum X value internal double GetMaxXWithRadiusValue( ChartArea area, params string[] series ) { double returnValue = Double.MinValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.XValue)) { if (seriesPoint.YValues.Length > 1) { returnValue = Math.Max(returnValue, seriesPoint.XValue + BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.XValue, false)); } else { returnValue = Math.Max(returnValue, seriesPoint.XValue); } } } } return returnValue; } /// /// Get Minimum value for X and Radius Y2 ( used for bubble chart ) /// /// Chart Area /// Series IDs /// Minimum X value internal double GetMinXWithRadiusValue( ChartArea area, params string[] series ) { double returnValue = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.XValue)) { if (seriesPoint.YValues.Length > 1) { returnValue = Math.Min(returnValue, seriesPoint.XValue - BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], false)); } else { returnValue = Math.Min(returnValue, seriesPoint.XValue); } } } } return returnValue; } /// /// Gets maximum Y value from many series /// /// Series IDs /// Maximum Y value internal double GetMaxYValue(params string[] series) { double returnValue = Double.MinValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } foreach( double y in seriesPoint.YValues ) { if(!double.IsNaN(y)) { returnValue = Math.Max(returnValue, y); } } } } return returnValue; } /// /// Gets maximum X value from many series /// /// Series IDs /// Maximum X value internal double GetMaxXValue(params string[] series) { double returnValue = Double.MinValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { returnValue = Math.Max(returnValue, seriesPoint.XValue); } } return returnValue; } /// /// Gets minimum and maximum X value from many series. /// /// Returns maximum X value. /// Returns minimum X value. /// Series IDs internal void GetMinMaxXValue(out double min, out double max, params string[] series) { max = Double.MinValue; min = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { max = Math.Max(max, seriesPoint.XValue); min = Math.Min(min, seriesPoint.XValue); } } } /// /// Gets minimum and maximum Y value from many series. /// /// Index of Y value to use. /// Returns maximum Y value. /// Returns minimum Y value. /// Series IDs internal void GetMinMaxYValue(int valueIndex, out double min, out double max, params string[] series) { max = Double.MinValue; min = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // Skip empty point if( IsPointSkipped( seriesPoint ) ) { continue; } double yValue = seriesPoint.YValues[valueIndex]; if(!double.IsNaN(yValue)) { max = Math.Max(max, yValue); min = Math.Min(min, yValue); } } } } /// /// Gets minimum and maximum Y value from many series. /// /// Returns maximum Y value. /// Returns minimum Y value. /// Series IDs internal void GetMinMaxYValue(out double min, out double max, params string[] series) { max = Double.MinValue; min = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // Skip empty point if( IsPointSkipped( seriesPoint ) ) { continue; } // Iterate through all Y values foreach( double y in seriesPoint.YValues ) { if(!double.IsNaN(y)) { max = Math.Max(max, y); min = Math.Min(min, y); } } } } } /// /// Gets minimum and maximum Y value from many series. /// /// Series objects list. /// Returns maximum Y value. /// Returns minimum Y value. internal void GetMinMaxYValue(System.Collections.ArrayList seriesList, out double min, out double max) { max = Double.MinValue; min = Double.MaxValue; foreach(Series series in seriesList) { foreach(DataPoint seriesPoint in series.Points) { // Skip empty point if( IsPointSkipped( seriesPoint ) ) { continue; } // Iterate through all Y values foreach( double y in seriesPoint.YValues ) { if(!double.IsNaN(y)) { max = Math.Max(max, y); min = Math.Min(min, y); } } } } } /// /// Gets maximum stacked Y value from many series /// /// Index of Y value to use /// Series IDs /// Maximum stacked Y value internal double GetMaxStackedYValue(int valueIndex, params string[] series) { double returnValue = 0; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double stackedMax = 0; double noStackedMax = 0; foreach(string seriesName in series) { if(this._series[seriesName].Points.Count > pointIndex) { // Take chart type from the series ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry)); IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName); // If stacked area if( !chartType.StackSign ) continue; if( chartType.Stacked ) { if(this._series[seriesName].Points[pointIndex].YValues[valueIndex] > 0) { stackedMax += this._series[seriesName].Points[pointIndex].YValues[valueIndex]; } } else { noStackedMax = Math.Max(noStackedMax,this._series[seriesName].Points[pointIndex].YValues[valueIndex]); } } } stackedMax = Math.Max(stackedMax, noStackedMax); returnValue = Math.Max(returnValue, stackedMax); } return returnValue; } /// /// Gets maximum Unsigned stacked Y value from many series ( Stacked Area chart ) /// /// Index of Y value to use /// Series IDs /// Maximum stacked Y value internal double GetMaxUnsignedStackedYValue(int valueIndex, params string[] series) { double returnValue = 0; double maxValue = Double.MinValue; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double stackedMax = 0; double noStackedMax = 0; foreach(string seriesName in series) { if (this._series[seriesName].Points.Count > pointIndex) { // Take chart type from the series ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry)); IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName); // If stacked column and bar if (chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex])) { continue; } if (chartType.Stacked) { maxValue = Double.MinValue; stackedMax += this._series[seriesName].Points[pointIndex].YValues[valueIndex]; if (stackedMax > maxValue) maxValue = stackedMax; } else { noStackedMax = Math.Max(noStackedMax, this._series[seriesName].Points[pointIndex].YValues[valueIndex]); } } } maxValue = Math.Max(maxValue, noStackedMax); returnValue = Math.Max(returnValue, maxValue); } return returnValue; } /// /// Gets maximum stacked X value from many series /// /// Series IDs /// Maximum stacked X value internal double GetMaxStackedXValue(params string[] series) { double returnValue = 0; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double doubleIndexValue = 0; foreach(string seriesName in series) { if (this._series[seriesName].Points.Count > pointIndex) { if (this._series[seriesName].Points[pointIndex].XValue > 0) { doubleIndexValue += this._series[seriesName].Points[pointIndex].XValue; } } } returnValue = Math.Max(returnValue, doubleIndexValue); } return returnValue; } /// /// Gets minimum Y value from many series /// /// Index of Y value to use /// Series IDs /// Minimum Y value internal double GetMinYValue(int valueIndex, params string[] series) { double returnValue = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.YValues[valueIndex])) { returnValue = Math.Min(returnValue, seriesPoint.YValues[valueIndex]); } } } return returnValue; } /// /// Get Minimum value for Y and and Radius (Y2) ( used for bubble chart ) /// /// Chart Area /// Series IDs /// Minimum Y value internal double GetMinYWithRadiusValue( ChartArea area, params string[] series ) { double returnValue = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } if(!double.IsNaN(seriesPoint.YValues[0])) { if (seriesPoint.YValues.Length > 1) { returnValue = Math.Min(returnValue, seriesPoint.YValues[0] - BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], true)); } else { returnValue = Math.Min(returnValue, seriesPoint.YValues[0]); } } } } return returnValue; } /// /// Gets minimum Y value from many series /// /// Series IDs /// Minimum Y value internal double GetMinYValue(params string[] series) { double returnValue = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { // The empty point if( IsPointSkipped( seriesPoint ) ) { continue; } foreach(double y in seriesPoint.YValues) { if(!double.IsNaN(y)) { returnValue = Math.Min(returnValue, y); } } } } return returnValue; } /// /// Gets minimum X value from many series /// /// Series IDs /// Minimum X value internal double GetMinXValue(params string[] series) { double returnValue = Double.MaxValue; foreach(string seriesName in series) { foreach(DataPoint seriesPoint in this._series[seriesName].Points) { returnValue = Math.Min(returnValue, seriesPoint.XValue); } } return returnValue; } /// /// Gets minimum stacked Y value from many series /// /// Index of Y value to use /// Series IDs /// Minimum stacked Y value internal double GetMinStackedYValue(int valueIndex, params string[] series) { double returnValue = Double.MaxValue; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double stackedMin = 0; double noStackedMin = 0; foreach(string seriesName in series) { if(this._series[seriesName].Points.Count > pointIndex) { // Take chart type from the series ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry)); IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName); // If stacked area if( !chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex])) continue; if( chartType.Stacked ) { if(this._series[seriesName].Points[pointIndex].YValues[valueIndex] < 0) { stackedMin += this._series[seriesName].Points[pointIndex].YValues[valueIndex]; } } else { noStackedMin = Math.Min(noStackedMin,this._series[seriesName].Points[pointIndex].YValues[valueIndex]); } } } stackedMin = Math.Min(stackedMin, noStackedMin); if( stackedMin == 0 ) { stackedMin = this._series[series[0]].Points[this._series[series[0]].Points.Count - 1].YValues[valueIndex]; } returnValue = Math.Min(returnValue, stackedMin); } return returnValue; } /// /// Gets minimum Unsigned stacked Y value from many series /// /// Index of Y value to use /// Series IDs /// Minimum stacked Y value internal double GetMinUnsignedStackedYValue(int valueIndex, params string[] series) { double returnValue = Double.MaxValue; double minValue = Double.MaxValue; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double stackedMin = 0; double noStackedMin = 0; minValue = Double.MaxValue; foreach(string seriesName in series) { if (this._series[seriesName].Points.Count > pointIndex) { // Take chart type from the series ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry)); IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName); // If stacked column and bar if (chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex])) { continue; } if (chartType.Stacked) { if (this._series[seriesName].Points[pointIndex].YValues[valueIndex] < 0) { stackedMin += this._series[seriesName].Points[pointIndex].YValues[valueIndex]; if (stackedMin < minValue) minValue = stackedMin; } } else { noStackedMin = Math.Min(noStackedMin, this._series[seriesName].Points[pointIndex].YValues[valueIndex]); } } } minValue = Math.Min(noStackedMin, minValue); returnValue = Math.Min(returnValue, minValue); } return returnValue; } /// /// Gets minimum stacked X value from many series /// /// Series IDs /// Minimum stacked X value internal double GetMinStackedXValue(params string[] series) { double returnValue = 0; double numberOfPoints = GetNumberOfPoints(series); for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) { double doubleIndexValue = 0; foreach(string seriesName in series) { if(this._series[seriesName].Points[pointIndex].XValue < 0) { doubleIndexValue += this._series[seriesName].Points[pointIndex].XValue; } } returnValue = Math.Min(returnValue, doubleIndexValue); } return returnValue; } /// /// Gets maximum hundred percent stacked Y value /// /// Indicates that negative values are shown on the other side of the axis. /// Series names /// Maximum 100% stacked Y value internal double GetMaxHundredPercentStackedYValue(bool supportNegative, params string[] series) { double returnValue = 0; // Convert array of series names into array of series Series[] seriesArray = new Series[series.Length]; int seriesIndex = 0; foreach(string seriesName in series) { seriesArray[seriesIndex++] = this._series[seriesName]; } // Loop through all dat points try { for(int pointIndex = 0; pointIndex < this._series[series[0]].Points.Count; pointIndex++) { // Calculate the total for all series double totalPerPoint = 0; double positiveTotalPerPoint = 0; foreach(Series ser in seriesArray) { if(supportNegative) { totalPerPoint += Math.Abs(ser.Points[pointIndex].YValues[0]); } else { totalPerPoint += ser.Points[pointIndex].YValues[0]; } if(ser.Points[pointIndex].YValues[0] > 0 || supportNegative == false) { positiveTotalPerPoint += ser.Points[pointIndex].YValues[0]; } } totalPerPoint = Math.Abs(totalPerPoint); // Calculate percentage of total if(totalPerPoint != 0) { returnValue = Math.Max(returnValue, (positiveTotalPerPoint / totalPerPoint) * 100.0); } } } catch(System.Exception) { throw (new InvalidOperationException(SR.ExceptionDataManager100StackedSeriesPointsNumeberMismatch)); } return returnValue; } /// /// Gets minimum hundred percent stacked Y value /// /// Indicates that negative values are shown on the other side of the axis. /// Series names /// Minimum 100% stacked Y value internal double GetMinHundredPercentStackedYValue(bool supportNegative, params string[] series) { double returnValue = 0.0; // Convert array of series names into array of series Series[] seriesArray = new Series[series.Length]; int seriesIndex = 0; foreach(string seriesName in series) { seriesArray[seriesIndex++] = this._series[seriesName]; } // Loop through all dat points try { for(int pointIndex = 0; pointIndex < this._series[series[0]].Points.Count; pointIndex++) { // Calculate the total for all series double totalPerPoint = 0; double negativeTotalPerPoint = 0; foreach(Series ser in seriesArray) { if(supportNegative) { totalPerPoint += Math.Abs(ser.Points[pointIndex].YValues[0]); } else { totalPerPoint += ser.Points[pointIndex].YValues[0]; } if(ser.Points[pointIndex].YValues[0] < 0 || supportNegative == false) { negativeTotalPerPoint += ser.Points[pointIndex].YValues[0]; } } totalPerPoint = Math.Abs(totalPerPoint); // Calculate percentage of total if(totalPerPoint != 0) { returnValue = Math.Min(returnValue, (negativeTotalPerPoint / totalPerPoint) * 100.0); } } } catch(System.Exception) { throw (new InvalidOperationException(SR.ExceptionDataManager100StackedSeriesPointsNumeberMismatch)); } return returnValue; } #endregion #region DataManager Properties /// /// Chart series collection. /// [ SRCategory("CategoryAttributeData"), #if !Microsoft_CONTROL PersistenceMode(PersistenceMode.InnerProperty), #endif Editor(Editors.SeriesCollectionEditor.Editor, Editors.SeriesCollectionEditor.Base), Bindable(true) ] public SeriesCollection Series { get { return _series; } } /// /// Color palette to use /// [ SRCategory("CategoryAttributeAppearance"), Bindable(true), SRDescription("DescriptionAttributePalette"), #if !Microsoft_CONTROL PersistenceMode(PersistenceMode.InnerProperty), #endif DefaultValue(ChartColorPalette.BrightPastel), Editor(Editors.ColorPaletteEditor.Editor, Editors.ColorPaletteEditor.Base) ] public ChartColorPalette Palette { get { return _colorPalette; } set { _colorPalette = value; } } // Array of custom palette colors. private Color[] _paletteCustomColors = new Color[0]; /// /// Array of custom palette colors. /// /// /// When this custom colors array is non-empty the Palette property is ignored. /// [ SRCategory("CategoryAttributeAppearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), SerializationVisibilityAttribute(SerializationVisibility.Attribute), SRDescription("DescriptionAttributeDataManager_PaletteCustomColors"), TypeConverter(typeof(ColorArrayConverter)) ] public Color[] PaletteCustomColors { set { this._paletteCustomColors = value; } get { return this._paletteCustomColors; } } #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) { if (_series != null) { _series.Dispose(); _series = null; } } } #endregion } }