You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
13 KiB
306 lines
13 KiB
// (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
|
|
{
|
|
/// <summary>
|
|
/// A base class that contains methods used by both the line and area series.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of data point used by the series.</typeparam>
|
|
public abstract class LineAreaBaseSeries<T> : DataPointSingleSeriesWithAxes
|
|
where T : DataPoint, new()
|
|
{
|
|
#region public IRangeAxis DependentRangeAxis
|
|
/// <summary>
|
|
/// Gets or sets the dependent range axis.
|
|
/// </summary>
|
|
public IRangeAxis DependentRangeAxis
|
|
{
|
|
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; }
|
|
set { SetValue(DependentRangeAxisProperty, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Identifies the DependentRangeAxis dependency property.
|
|
/// </summary>
|
|
[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<T>),
|
|
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged));
|
|
|
|
/// <summary>
|
|
/// DependentRangeAxisProperty property changed handler.
|
|
/// </summary>
|
|
/// <param name="d">LineAreaBaseSeries that changed its DependentRangeAxis.</param>
|
|
/// <param name="e">Event arguments.</param>
|
|
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
LineAreaBaseSeries<T> source = (LineAreaBaseSeries<T>)d;
|
|
IRangeAxis newValue = (IRangeAxis)e.NewValue;
|
|
source.OnDependentRangeAxisPropertyChanged(newValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// DependentRangeAxisProperty property changed handler.
|
|
/// </summary>
|
|
/// <param name="newValue">New value.</param>
|
|
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue)
|
|
{
|
|
this.InternalDependentAxis = (IAxis)newValue;
|
|
}
|
|
#endregion public IRangeAxis DependentRangeAxis
|
|
|
|
#region public IAxis IndependentAxis
|
|
/// <summary>
|
|
/// Gets or sets the independent range axis.
|
|
/// </summary>
|
|
public IAxis IndependentAxis
|
|
{
|
|
get { return GetValue(IndependentAxisProperty) as IAxis; }
|
|
set { SetValue(IndependentAxisProperty, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Identifies the IndependentAxis dependency property.
|
|
/// </summary>
|
|
[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<T>),
|
|
new PropertyMetadata(null, OnIndependentAxisPropertyChanged));
|
|
|
|
/// <summary>
|
|
/// IndependentAxisProperty property changed handler.
|
|
/// </summary>
|
|
/// <param name="d">LineAreaBaseSeries that changed its IndependentAxis.</param>
|
|
/// <param name="e">Event arguments.</param>
|
|
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
LineAreaBaseSeries<T> source = (LineAreaBaseSeries<T>)d;
|
|
IAxis newValue = (IAxis)e.NewValue;
|
|
source.OnIndependentAxisPropertyChanged(newValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// IndependentAxisProperty property changed handler.
|
|
/// </summary>
|
|
/// <param name="newValue">New value.</param>
|
|
private void OnIndependentAxisPropertyChanged(IAxis newValue)
|
|
{
|
|
this.InternalIndependentAxis = (IAxis)newValue;
|
|
}
|
|
#endregion public IAxis IndependentAxis
|
|
|
|
/// <summary>
|
|
/// Gets data points collection sorted by independent value.
|
|
/// </summary>
|
|
internal OrderedMultipleDictionary<IComparable, DataPoint> DataPointsByIndependentValue { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the independent axis as a range axis.
|
|
/// </summary>
|
|
public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } }
|
|
|
|
/// <summary>
|
|
/// Gets the dependent axis as a range axis.
|
|
/// </summary>
|
|
public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the LineAreaBaseSeries class.
|
|
/// </summary>
|
|
protected LineAreaBaseSeries()
|
|
{
|
|
DataPointsByIndependentValue =
|
|
new OrderedMultipleDictionary<IComparable, DataPoint>(
|
|
false,
|
|
(left, right) =>
|
|
left.CompareTo(right),
|
|
(leftDataPoint, rightDataPoint) =>
|
|
RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a DataPoint for determining the line color.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called after data points have been loaded from the items source.
|
|
/// </summary>
|
|
/// <param name="newDataPoints">New active data points.</param>
|
|
/// <param name="oldDataPoints">Old inactive data points.</param>
|
|
protected override void OnDataPointsChanged(IList<DataPoint> newDataPoints, IList<DataPoint> 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method executes after all data points have been updated.
|
|
/// </summary>
|
|
protected override void OnAfterUpdateDataPoints()
|
|
{
|
|
if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null)
|
|
{
|
|
UpdateShape();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repositions line data point in the sorted collection if the actual
|
|
/// independent axis is a range axis.
|
|
/// </summary>
|
|
/// <param name="dataPoint">The data point that has changed.</param>
|
|
/// <param name="oldValue">The old value.</param>
|
|
/// <param name="newValue">The new value.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new line data point.
|
|
/// </summary>
|
|
/// <returns>A line data point.</returns>
|
|
protected override DataPoint CreateDataPoint()
|
|
{
|
|
return new T();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// ResourceDictionary to use for necessary resources.
|
|
/// </returns>
|
|
protected override IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost()
|
|
{
|
|
return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the visual representation of the data point.
|
|
/// </summary>
|
|
/// <param name="dataPoint">The data point to update.</param>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the Series shape object.
|
|
/// </summary>
|
|
protected virtual void UpdateShape()
|
|
{
|
|
double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value;
|
|
|
|
Func<DataPoint, Point> createPoint =
|
|
dataPoint =>
|
|
new Point(
|
|
ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value,
|
|
maximum - ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value);
|
|
|
|
IEnumerable<Point> points = Enumerable.Empty<Point>();
|
|
if (ValueHelper.CanGraph(maximum))
|
|
{
|
|
if (ActualIndependentAxis is IRangeAxis)
|
|
{
|
|
points = DataPointsByIndependentValue.Select(createPoint);
|
|
}
|
|
else
|
|
{
|
|
points =
|
|
ActiveDataPoints
|
|
.Select(createPoint)
|
|
.OrderBy(point => point.X);
|
|
}
|
|
}
|
|
UpdateShapeFromPoints(points);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the Series shape object from a collection of Points.
|
|
/// </summary>
|
|
/// <param name="points">Collection of Points.</param>
|
|
protected abstract void UpdateShapeFromPoints(IEnumerable<Point> points);
|
|
}
|
|
}
|
|
|