// (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.Runtime.CompilerServices; using System.Windows.Controls.DataVisualization.Collections; namespace System.Windows.Controls.DataVisualization.Charting { /// /// A base class that contains methods used by both the line and area series. /// /// The type of data point used by the series. public abstract class LineAreaBaseSeries : DataPointSingleSeriesWithAxes where T : DataPoint, new() { #region public IRangeAxis DependentRangeAxis /// /// Gets or sets the dependent range axis. /// public IRangeAxis DependentRangeAxis { get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } set { SetValue(DependentRangeAxisProperty, value); } } /// /// Identifies the DependentRangeAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty DependentRangeAxisProperty = DependencyProperty.Register( "DependentRangeAxis", typeof(IRangeAxis), typeof(LineAreaBaseSeries), new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); /// /// DependentRangeAxisProperty property changed handler. /// /// LineAreaBaseSeries that changed its DependentRangeAxis. /// Event arguments. private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LineAreaBaseSeries source = (LineAreaBaseSeries)d; IRangeAxis newValue = (IRangeAxis)e.NewValue; source.OnDependentRangeAxisPropertyChanged(newValue); } /// /// DependentRangeAxisProperty property changed handler. /// /// New value. private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) { this.InternalDependentAxis = (IAxis)newValue; } #endregion public IRangeAxis DependentRangeAxis #region public IAxis IndependentAxis /// /// Gets or sets the independent range axis. /// public IAxis IndependentAxis { get { return GetValue(IndependentAxisProperty) as IAxis; } set { SetValue(IndependentAxisProperty, value); } } /// /// Identifies the IndependentAxis dependency property. /// [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] public static readonly DependencyProperty IndependentAxisProperty = DependencyProperty.Register( "IndependentAxis", typeof(IAxis), typeof(LineAreaBaseSeries), new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); /// /// IndependentAxisProperty property changed handler. /// /// LineAreaBaseSeries that changed its IndependentAxis. /// Event arguments. private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { LineAreaBaseSeries source = (LineAreaBaseSeries)d; IAxis newValue = (IAxis)e.NewValue; source.OnIndependentAxisPropertyChanged(newValue); } /// /// IndependentAxisProperty property changed handler. /// /// New value. private void OnIndependentAxisPropertyChanged(IAxis newValue) { this.InternalIndependentAxis = (IAxis)newValue; } #endregion public IAxis IndependentAxis /// /// Gets data points collection sorted by independent value. /// internal OrderedMultipleDictionary DataPointsByIndependentValue { get; private set; } /// /// Gets the independent axis as a range axis. /// public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } /// /// Gets the dependent axis as a range axis. /// public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } /// /// Initializes a new instance of the LineAreaBaseSeries class. /// protected LineAreaBaseSeries() { DataPointsByIndependentValue = new OrderedMultipleDictionary( false, (left, right) => left.CompareTo(right), (leftDataPoint, rightDataPoint) => RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint))); } /// /// Creates a DataPoint for determining the line color. /// public override void OnApplyTemplate() { base.OnApplyTemplate(); if (null != PlotArea) { Grid grid = new Grid(); DataPoint dataPoint = CreateDataPoint(); dataPoint.Visibility = Visibility.Collapsed; dataPoint.Loaded += delegate { dataPoint.SetStyle(ActualDataPointStyle); Background = dataPoint.Background; if (null != PlotArea) { PlotArea.Children.Remove(grid); } }; grid.Children.Add(dataPoint); PlotArea.Children.Add(grid); } } /// /// Called after data points have been loaded from the items source. /// /// New active data points. /// Old inactive data points. protected override void OnDataPointsChanged(IList newDataPoints, IList oldDataPoints) { base.OnDataPointsChanged(newDataPoints, oldDataPoints); if (ActualIndependentAxis is IRangeAxis) { foreach (DataPoint dataPoint in oldDataPoints) { DataPointsByIndependentValue.Remove((IComparable)dataPoint.IndependentValue, dataPoint); } foreach (DataPoint dataPoint in newDataPoints) { DataPointsByIndependentValue.Add((IComparable)dataPoint.IndependentValue, dataPoint); } } } /// /// This method executes after all data points have been updated. /// protected override void OnAfterUpdateDataPoints() { if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null) { UpdateShape(); } } /// /// Repositions line data point in the sorted collection if the actual /// independent axis is a range axis. /// /// The data point that has changed. /// The old value. /// The new value. protected override void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) { if (ActualIndependentAxis is IRangeAxis && !oldValue.Equals(newValue)) { bool removed = DataPointsByIndependentValue.Remove((IComparable)oldValue, dataPoint); if (removed) { DataPointsByIndependentValue.Add((IComparable)newValue, dataPoint); } } base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); } /// /// Creates a new line data point. /// /// A line data point. protected override DataPoint CreateDataPoint() { return new T(); } /// /// Returns the custom ResourceDictionary to use for necessary resources. /// /// /// ResourceDictionary to use for necessary resources. /// protected override IEnumerator GetResourceDictionaryEnumeratorFromHost() { return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true); } /// /// Updates the visual representation of the data point. /// /// The data point to update. protected override void UpdateDataPoint(DataPoint dataPoint) { double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; if (ValueHelper.CanGraph(maximum)) { double x = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value; if (ValueHelper.CanGraph(x) && ValueHelper.CanGraph(y)) { dataPoint.Visibility = Visibility.Visible; double coordinateY = Math.Round(maximum - (y + (dataPoint.ActualHeight / 2))); Canvas.SetTop(dataPoint, coordinateY); double coordinateX = Math.Round(x - (dataPoint.ActualWidth / 2)); Canvas.SetLeft(dataPoint, coordinateX); } else { dataPoint.Visibility = Visibility.Collapsed; } } if (!UpdatingDataPoints) { UpdateShape(); } } /// /// Updates the Series shape object. /// protected virtual void UpdateShape() { double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; Func createPoint = dataPoint => new Point( ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value, maximum - ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value); IEnumerable points = Enumerable.Empty(); if (ValueHelper.CanGraph(maximum)) { if (ActualIndependentAxis is IRangeAxis) { points = DataPointsByIndependentValue.Select(createPoint); } else { points = ActiveDataPoints .Select(createPoint) .OrderBy(point => point.X); } } UpdateShapeFromPoints(points); } /// /// Updates the Series shape object from a collection of Points. /// /// Collection of Points. protected abstract void UpdateShapeFromPoints(IEnumerable points); } }