// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; namespace System.Windows.Controls.DataVisualization.Charting { /// /// Control that displays values as a stacked area chart visualization. /// /// Preview public class StackedAreaSeries : StackedAreaLineSeries, IAnchoredToOrigin { /// /// Initializes a new instance of the StackedAreaSeries class. /// public StackedAreaSeries() { } /// /// Creates a DataPoint for the series. /// /// Series-appropriate DataPoint instance. protected override DataPoint CreateDataPoint() { return new AreaDataPoint(); } /// /// Creates a series-appropriate Shape for connecting the points of the series. /// /// Shape instance. protected override Shape CreateDataShape() { return new Polygon(); } /// /// Updates the Shape for the series. /// /// Locations of the points of each SeriesDefinition in the series. protected override void UpdateShape(IList> definitionPoints) { for (int i = SeriesDefinitions.Count - 1; 0 < i; i--) { PointCollection pointCollection = new PointCollection(); IEnumerable topPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i].OrderBy(p => p.X) : definitionPoints[i]; foreach (Point p in topPoints) { pointCollection.Add(p); } IEnumerable bottomPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i - 1].OrderByDescending(p => p.X) : definitionPoints[i - 1].Reverse(); foreach (Point p in bottomPoints) { pointCollection.Add(p); } SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[i]], pointCollection); } if (1 <= SeriesDefinitions.Count) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; IComparable zeroValue = ActualDependentRangeAxis.Origin ?? 0.0; if (zeroValue.CompareTo(ActualDependentRangeAxis.Range.Minimum) < 0) { zeroValue = ActualDependentRangeAxis.Range.Minimum; } if (0 < zeroValue.CompareTo(ActualDependentRangeAxis.Range.Maximum)) { zeroValue = ActualDependentRangeAxis.Range.Maximum; } double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(zeroValue).Value; PointCollection pointCollection = new PointCollection(); Point[] topPoints = ((ActualIndependentAxis is ICategoryAxis) ? definitionPoints[0].OrderBy(p => p.X) : definitionPoints[0]).ToArray(); foreach (Point p in topPoints) { pointCollection.Add(p); } if (0 < topPoints.Length) { Point firstPoint = topPoints[0]; Point lastPoint = topPoints[topPoints.Length - 1]; pointCollection.Add(new Point(lastPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); pointCollection.Add(new Point(firstPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); } SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[0]], pointCollection); } } /// /// Sets the Points property of a Polygon to the specified PointCollection. /// /// Polygon to set the Points property of. /// Specified PointCollection. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Silverlight implementation is not static.")] protected void SetPolygonPointsProperty(Polygon polygon, PointCollection pointCollection) { #if SILVERLIGHT // Changing .Points during an Arrange pass can create a layout cycle on Silverlight if (!polygon.Points.SequenceEqual(pointCollection)) { #endif polygon.Points = pointCollection; #if SILVERLIGHT // In rare cases, Silverlight doesn't update the line visual to match the new points; // calling InvalidateArrange works around that problem. polygon.InvalidateArrange(); } #endif } /// /// Returns the value margins for the data points of the series. /// /// Consumer of the value margins. /// Sequence of value margins. protected override IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) { if (valueMarginConsumer == ActualIndependentAxis) { return Enumerable.Empty(); } else { return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); } } /// /// Gets the anchored axis for the series. /// IRangeAxis IAnchoredToOrigin.AnchoredAxis { get { return ActualDependentRangeAxis; } } } }