<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Series/DataPointSeriesWithAxes.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization;
using System.Windows.Controls.DataVisualization.Collections;

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// Represents a dynamic series that uses axes to display data points.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    public abstract class DataPointSeriesWithAxes : DataPointSeries, IDataProvider, IRangeProvider, IAxisListener, IValueMarginProvider
    {
        /// <summary>
        /// Gets or sets the data points by dependent value.
        /// </summary>
        private OrderedMultipleDictionary<IComparable, DataPoint> DataPointsByActualDependentValue { get; set; }

        /// <summary>
        /// Creates the correct range axis based on the data.
        /// </summary>
        /// <param name="value">The value to evaluate to determine which type of
        /// axis to create.</param>
        /// <returns>The range axis appropriate that can plot the provided
        /// value.</returns>
        protected static IRangeAxis CreateRangeAxisFromData(object value)
        {
            double doubleValue;
            DateTime dateTime;
            if (ValueHelper.TryConvert(value, out doubleValue))
            {
                return new LinearAxis();
            }
            else if (ValueHelper.TryConvert(value, out dateTime))
            {
                return new DateTimeAxis();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Retrieves the value for a given access from a data point.
        /// </summary>
        /// <param name="dataPoint">The data point to retrieve the value from.</param>
        /// <param name="axis">The axis to retrieve the value for.</param>
        /// <returns>A function that returns a value appropriate for the axis
        /// when provided a DataPoint.</returns>
        protected virtual object GetActualDataPointAxisValue(DataPoint dataPoint, IAxis axis)
        {
            if (axis == InternalActualIndependentAxis)
            {
                return dataPoint.ActualIndependentValue;
            }
            else if (axis == InternalActualDependentAxis)
            {
                return dataPoint.ActualDependentValue;
            }
            return null;
        }

        /// <summary>
        /// Gets or sets the actual dependent axis.
        /// </summary>
        protected IAxis InternalActualDependentAxis { get; set; }

        #region public Axis InternalDependentAxis

        /// <summary>
        /// Stores the internal dependent axis.
        /// </summary>
        private IAxis _internalDependentAxis;

        /// <summary>
        /// Gets or sets the value of the internal dependent axis.
        /// </summary>
        protected IAxis InternalDependentAxis
        {
            get { return _internalDependentAxis; }
            set 
            {
                if (_internalDependentAxis != value)
                {
                    IAxis oldValue = _internalDependentAxis;
                    _internalDependentAxis = value;
                    OnInternalDependentAxisPropertyChanged(oldValue, value);
                }
            }
        }

        /// <summary>
        /// DependentAxisProperty property changed handler.
        /// </summary>
        /// <param name="oldValue">Old value.</param>
        /// <param name="newValue">New value.</param>
        protected virtual void OnInternalDependentAxisPropertyChanged(IAxis oldValue, IAxis newValue)
        {
            if (newValue != null 
                && InternalActualDependentAxis != null 
                && InternalActualDependentAxis != newValue 
                && InternalActualDependentAxis.RegisteredListeners.Contains(this))
            {
                InternalActualDependentAxis.RegisteredListeners.Remove(this);
                InternalActualDependentAxis = null;
                GetAxes();
            }
        }
        #endregion public Axis InternalDependentAxis

        /// <summary>
        /// Gets or sets the actual independent axis value.
        /// </summary>
        protected IAxis InternalActualIndependentAxis { get; set; }

        #region protected Axis InternalIndependentAxis

        /// <summary>
        /// The internal independent axis.
        /// </summary>
        private IAxis _internalIndependentAxis;

        /// <summary>
        /// Gets or sets the value of the internal independent axis.
        /// </summary>
        protected IAxis InternalIndependentAxis
        {
            get { return _internalIndependentAxis; }
            set 
            {
                if (value != _internalIndependentAxis)
                {
                    IAxis oldValue = _internalIndependentAxis;
                    _internalIndependentAxis = value;
                    OnInternalIndependentAxisPropertyChanged(oldValue, value);
                }
            }
        }

        /// <summary>
        /// IndependentAxisProperty property changed handler.
        /// </summary>
        /// <param name="oldValue">Old value.</param>
        /// <param name="newValue">New value.</param>
        protected virtual void OnInternalIndependentAxisPropertyChanged(IAxis oldValue, IAxis newValue)
        {
            if (newValue != null
                && InternalActualIndependentAxis != null
                && InternalActualIndependentAxis != newValue
                && InternalActualIndependentAxis.RegisteredListeners.Contains(this))
            {
                InternalActualIndependentAxis.RegisteredListeners.Remove(this);
                InternalActualIndependentAxis = null;
                GetAxes();
            }
        }
        #endregion protected Axis IndependentAxis

        /// <summary>
        /// Initializes a new instance of the DataPointSeriesWithAxes class.
        /// </summary>
        protected DataPointSeriesWithAxes()
        {
            this.DataPointsByActualDependentValue =
                new OrderedMultipleDictionary<IComparable, DataPoint>(
                    false,
                    (left, right) => 
                        left.CompareTo(right),
                    (leftDataPoint, rightDataPoint) => 
                        RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint)));
        }

        /// <summary>
        /// Update the axes when the specified data point's ActualDependentValue property changes.
        /// </summary>
        /// <param name="dataPoint">The data point.</param>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected override void OnDataPointActualDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue)
        {
            if (oldValue != null)
            {
                bool removed = DataPointsByActualDependentValue.Remove(oldValue, dataPoint);
                if (removed)
                {
                    DataPointsByActualDependentValue.Add(newValue, dataPoint);
                }
            }

            UpdateActualDependentAxis();
            UpdateDataPoint(dataPoint);
            base.OnDataPointActualDependentValueChanged(dataPoint, oldValue, newValue);
        }

        /// <summary>
        /// Update the axes when the specified data point's DependentValue property changes.
        /// </summary>
        /// <param name="dataPoint">The data point.</param>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected override void OnDataPointDependentValueChanged(DataPoint dataPoint, IComparable oldValue, IComparable newValue)
        {
            if ((null != InternalActualDependentAxis))
            {
                dataPoint.BeginAnimation(DataPoint.ActualDependentValueProperty, "ActualDependentValue", newValue, this.TransitionDuration, this.TransitionEasingFunction);
            }
            else
            {
                dataPoint.ActualDependentValue = newValue;
            }
            base.OnDataPointDependentValueChanged(dataPoint, oldValue, newValue);
        }

        /// <summary>
        /// Update axes when the specified data point's effective dependent value changes.
        /// </summary>
        private void UpdateActualDependentAxis()
        {
            if (InternalActualDependentAxis != null)
            {
                IDataConsumer dataConsumer = InternalActualDependentAxis as IDataConsumer;
                if (dataConsumer != null)
                {
                    IDataProvider categoryInformationProvider = (IDataProvider)this;
                    dataConsumer.DataChanged(categoryInformationProvider, categoryInformationProvider.GetData(dataConsumer));
                }

                IRangeConsumer rangeAxis = InternalActualDependentAxis as IRangeConsumer;
                if (rangeAxis != null)
                {
                    IRangeProvider rangeInformationProvider = (IRangeProvider)this;
                    rangeAxis.RangeChanged(rangeInformationProvider, rangeInformationProvider.GetRange(rangeAxis));
                }
            }
        }

        /// <summary>
        /// Update axes when the specified data point's actual independent value changes.
        /// </summary>
        /// <param name="dataPoint">The data point.</param>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected override void OnDataPointActualIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue)
        {
            UpdateActualIndependentAxis();
            UpdateDataPoint(dataPoint);
            base.OnDataPointActualIndependentValueChanged(dataPoint, oldValue, newValue);
        }

        /// <summary>
        /// Update axes when the specified data point's independent value changes.
        /// </summary>
        /// <param name="dataPoint">The data point.</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 ((null != InternalActualIndependentAxis) && (InternalActualIndependentAxis is IRangeAxis))
            {
                dataPoint.BeginAnimation(DataPoint.ActualIndependentValueProperty, "ActualIndependentValue", newValue, this.TransitionDuration, this.TransitionEasingFunction);
            }
            else
            {
                dataPoint.ActualIndependentValue = newValue;
            }
            base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue);
        }

        /// <summary>
        /// Update axes when a data point's effective independent value changes.
        /// </summary>
        private void UpdateActualIndependentAxis()
        {
            if (InternalActualIndependentAxis != null)
            {
                ICategoryAxis categoryAxis = InternalActualIndependentAxis as ICategoryAxis;
                if (categoryAxis != null)
                {
                    IDataProvider categoryInformationProvider = (IDataProvider)this;
                    categoryAxis.DataChanged(categoryInformationProvider, categoryInformationProvider.GetData(categoryAxis));
                }
                IRangeConsumer rangeAxis = InternalActualIndependentAxis as IRangeConsumer;
                if (rangeAxis != null)
                {
                    IRangeProvider rangeInformationProvider = (IRangeProvider)this;
                    rangeAxis.RangeChanged(rangeInformationProvider, rangeInformationProvider.GetRange(rangeAxis));
                }
            }
        }

        /// <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)
        {
            foreach (DataPoint dataPoint in newDataPoints)
            {
                DataPointsByActualDependentValue.Add(dataPoint.ActualDependentValue, dataPoint);
            }

            foreach (DataPoint dataPoint in oldDataPoints)
            {
                DataPointsByActualDependentValue.Remove(dataPoint.ActualDependentValue, dataPoint);
            }

            GetAxes();

            if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null)
            {
                Action action = () =>
                    {
                        AxesInvalidated = false;
                        UpdatingAllAxes = true;
                        try
                        {
                            UpdateActualIndependentAxis();
                            UpdateActualDependentAxis();
                        }
                        finally
                        {
                            UpdatingAllAxes = false;
                        }

                        if (AxesInvalidated)
                        {
                            UpdateDataPoints(ActiveDataPoints);
                        }
                        else
                        {
                            UpdateDataPoints(newDataPoints);
                        }

                        AxesInvalidated = false;
                    };

                InvokeOnLayoutUpdated(action);
            }

            base.OnDataPointsChanged(newDataPoints, oldDataPoints);
        }

        /// <summary>
        /// Gets or sets a value indicating whether to the axes are being 
        /// updated.
        /// </summary>
        private bool UpdatingAllAxes { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the axes have been 
        /// invalidated.
        /// </summary>
        private bool AxesInvalidated { get; set; }

        /// <summary>
        /// Only updates all data points if series has axes.
        /// </summary>
        /// <param name="dataPoints">A sequence of data points to update.
        /// </param>
        protected override void UpdateDataPoints(IEnumerable<DataPoint> dataPoints)
        {
            if (InternalActualIndependentAxis != null && InternalActualDependentAxis != null)
            {
                base.UpdateDataPoints(dataPoints);
            }
        }

        /// <summary>
        /// Method called to get series to acquire the axes it needs.  Acquires
        /// no axes by default.
        /// </summary>
        private void GetAxes()
        {
            if (SeriesHost != null)
            {
                DataPoint firstDataPoint = ActiveDataPoints.FirstOrDefault();
                if (firstDataPoint == null)
                {
                    return;
                }

                GetAxes(firstDataPoint);
            }
        }

        /// <summary>
        /// Method called to get series to acquire the axes it needs.  Acquires
        /// no axes by default.
        /// </summary>
        /// <param name="firstDataPoint">The first data point.</param>
        protected abstract void GetAxes(DataPoint firstDataPoint);

        /// <summary>
        /// Method called to get the axes that the series needs.
        /// </summary>
        /// <param name="firstDataPoint">The first data point.</param>
        /// <param name="independentAxisPredicate">A predicate that returns
        /// a value indicating whether an axis is an acceptable candidate for
        /// the series independent axis.</param>
        /// <param name="independentAxisFactory">A function that creates an
        /// acceptable independent axis.</param>
        /// <param name="dependentAxisPredicate">A predicate that returns
        /// a value indicating whether an axis is an acceptable candidate for
        /// the series dependent axis.</param>
        /// <param name="dependentAxisFactory">A function that creates an
        /// acceptable dependent axis.</param>
        protected virtual void GetAxes(DataPoint firstDataPoint, Func<IAxis, bool> independentAxisPredicate, Func<IAxis> independentAxisFactory, Func<IAxis, bool> dependentAxisPredicate, Func<IAxis> dependentAxisFactory)
        {
            Func<IAxis, bool> actualIndependentAxisPredicate = (axis) => independentAxisPredicate(axis) && axis.CanPlot(firstDataPoint.IndependentValue);
            IAxis workingIndependentAxis = null;
            if (this.InternalActualIndependentAxis == null)
            {
                if (this.InternalIndependentAxis != null)
                {
                    if (actualIndependentAxisPredicate(this.InternalIndependentAxis))
                    {
                        workingIndependentAxis = this.InternalIndependentAxis;
                    }
                    else
                    {
                        throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_GetAxes_AssignedIndependentAxisCannotBeUsed);
                    }
                }

                if (workingIndependentAxis == null)
                {
                    workingIndependentAxis = this.SeriesHost.Axes.FirstOrDefault(actualIndependentAxisPredicate);
                }

                if (workingIndependentAxis == null)
                {
                    workingIndependentAxis = independentAxisFactory();
                }

                this.InternalActualIndependentAxis = workingIndependentAxis;

                if (!workingIndependentAxis.RegisteredListeners.Contains(this))
                {
                    workingIndependentAxis.RegisteredListeners.Add(this);
                }
                if (!this.SeriesHost.Axes.Contains(workingIndependentAxis))
                {
                    this.SeriesHost.Axes.Add(workingIndependentAxis);
                }
            }

            Func<IAxis, bool> actualDependentAxisPredicate = (axis) => dependentAxisPredicate(axis) && axis.CanPlot(firstDataPoint.DependentValue);
            IAxis workingDependentAxis = null;
            if (this.InternalActualDependentAxis == null)
            {
                if (this.InternalDependentAxis != null)
                {
                    if (actualDependentAxisPredicate(this.InternalDependentAxis))
                    {
                        workingDependentAxis = this.InternalDependentAxis;
                    }
                    else
                    {
                        throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_GetAxes_AssignedDependentAxisCannotBeUsed);
                    }
                }

                if (workingDependentAxis == null)
                {
                    workingDependentAxis = InternalActualIndependentAxis.DependentAxes.Concat(this.SeriesHost.Axes).FirstOrDefault(actualDependentAxisPredicate);
                }

                if (workingDependentAxis == null)
                {
                    workingDependentAxis = dependentAxisFactory();
                }

                this.InternalActualDependentAxis = workingDependentAxis;

                if (!workingDependentAxis.RegisteredListeners.Contains(this))
                {
                    workingDependentAxis.RegisteredListeners.Add(this);
                }

                // Only add axis to the axes collection of the series host if 
                // it is not a dependent axis belonging to the acquired 
                // independent axis.
                if (!this.SeriesHost.Axes.Contains(workingDependentAxis) && !InternalActualIndependentAxis.DependentAxes.Contains(workingDependentAxis))
                {
                    this.SeriesHost.Axes.Add(workingDependentAxis);
                }
            }
        }

        /// <summary>
        /// Updates data points when the axis is invalidated.
        /// </summary>
        /// <param name="axis">The axis that was invalidated.</param>
        void IAxisListener.AxisInvalidated(IAxis axis)
        {
            if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null && PlotArea != null)
            {
                if (!UpdatingAllAxes)
                {
                    UpdateDataPoints(ActiveDataPoints);
                }
                else
                {
                    AxesInvalidated = true;
                }
            }
        }

        /// <summary>
        /// Returns the actual range of data for a given axis.
        /// </summary>
        /// <param name="consumer">The axis to retrieve the range for.</param>
        /// <returns>The actual range of data.</returns>
        protected virtual Range<IComparable> GetRange(IRangeConsumer consumer)
        {
            if (consumer == null)
            {
                throw new ArgumentNullException("consumer");
            }

            if (consumer == InternalActualDependentAxis)
            {
                if (this.DataPointsByActualDependentValue.Count > 0)
                {
                    return this.DataPointsByActualDependentValue.GetKeyRange();
                }
            }

            IAxis axis = consumer as IAxis;
            return (axis != null)
                ? ActiveDataPoints.Select(dataPoint => (IComparable)GetActualDataPointAxisValue(dataPoint, axis)).GetRange() 
                : new Range<IComparable>();
        }

        /// <summary>
        /// Returns the value margins for a given axis.
        /// </summary>
        /// <param name="consumer">The axis to retrieve the value margins for.
        /// </param>
        /// <returns>A sequence of value margins.</returns>
        protected virtual IEnumerable<ValueMargin> GetValueMargins(IValueMarginConsumer consumer)
        {
            IAxis axis = consumer as IAxis;
            if (axis != null && ActiveDataPoints.Any())
            {
                Func<DataPoint, IComparable> selector = null;
                DataPoint minimumPoint = null;
                DataPoint maximumPoint = null;
                double margin = 0.0;
                if (axis == InternalActualIndependentAxis)
                {
                    selector = (dataPoint) => (IComparable)dataPoint.ActualIndependentValue;

                    minimumPoint = ActiveDataPoints.MinOrNull(selector);
                    maximumPoint = ActiveDataPoints.MaxOrNull(selector);
                    margin = minimumPoint.GetActualMargin(this.InternalActualIndependentAxis);
                }
                else if (axis == InternalActualDependentAxis)
                {
                    selector = (dataPoint) => (IComparable)dataPoint.ActualDependentValue;

                    Tuple<DataPoint, DataPoint> largestAndSmallestValues = this.DataPointsByActualDependentValue.GetLargestAndSmallestValues();
                    minimumPoint = largestAndSmallestValues.Item1;
                    maximumPoint = largestAndSmallestValues.Item2;
                    margin = minimumPoint.GetActualMargin(this.InternalActualDependentAxis);
                }
                
                yield return new ValueMargin(selector(minimumPoint), margin, margin);
                yield return new ValueMargin(selector(maximumPoint), margin, margin);
            }
            else
            {
                yield break;
            }
        }

        /// <summary>
        /// Returns data to a data consumer.
        /// </summary>
        /// <param name="dataConsumer">The data consumer requesting the data.
        /// </param>
        /// <returns>The data for a given data consumer.</returns>
        IEnumerable<object> IDataProvider.GetData(IDataConsumer dataConsumer)
        {
            IAxis axis = (IAxis)dataConsumer;
            if (axis == null)
            {
                throw new ArgumentNullException("dataConsumer");
            }

            Func<DataPoint, object> selector = null;
            if (axis == InternalActualIndependentAxis)
            {
                if (IndependentValueBinding == null)
                {
                    return Enumerable.Range(1, ActiveDataPointCount).Cast<object>();
                }
                selector = (dataPoint) => dataPoint.ActualIndependentValue ?? dataPoint.ActualDependentValue;
            }
            else if (axis == InternalActualDependentAxis)
            {
                selector = (dataPoint) => dataPoint.ActualDependentValue;
            }

            return ActiveDataPoints.Select(selector).Distinct();
        }

        /// <summary>
        /// Called when the value of the SeriesHost property changes.
        /// </summary>
        /// <param name="oldValue">The value to be replaced.</param>
        /// <param name="newValue">The new series host value.</param>
        protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue)
        {
            if (oldValue != null)
            {
                if (InternalActualIndependentAxis != null)
                {
                    InternalActualIndependentAxis.RegisteredListeners.Remove(this);
                    InternalActualIndependentAxis = null;
                }
                if (InternalActualDependentAxis != null)
                {
                    InternalActualDependentAxis.RegisteredListeners.Remove(this);
                    InternalActualDependentAxis = null;
                }
            }

            base.OnSeriesHostPropertyChanged(oldValue, newValue);
        }

        /// <summary>
        /// Returns the data range.
        /// </summary>
        /// <param name="rangeConsumer">The consumer requesting the range.</param>
        /// <returns>The data range.</returns>
        Range<IComparable> IRangeProvider.GetRange(IRangeConsumer rangeConsumer)
        {
            return GetRange(rangeConsumer);
        }

        /// <summary>
        /// Returns the value margins for a given axis.
        /// </summary>
        /// <param name="axis">The axis to retrieve the value margins for.
        /// </param>
        /// <returns>A sequence of value margins.</returns>
        IEnumerable<ValueMargin> IValueMarginProvider.GetValueMargins(IValueMarginConsumer axis)
        {
            return GetValueMargins(axis);
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/TreeMap/Interpolators/InterpolationMode.cs<|end_filename|>
﻿// (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.

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// Specifies the supported interpolation modes.
    /// </summary>
    /// <QualityBand>Experimental</QualityBand>
    public enum InterpolationMode
    {
        /// <summary>
        /// Interpolation shall be applied to leaf nodes only in the tree.
        /// </summary>
        LeafNodesOnly = 0,

        /// <summary>
        /// Interpolation shall be applied to all nodes in the tree.
        /// </summary>
        AllNodes = 1,
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/DataPoint/DataPointState.cs<|end_filename|>
﻿// (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.

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// Describes the state a data point is in.
    /// </summary>
    public enum DataPointState
    {
        /// <summary>
        /// Data point has been created.
        /// </summary>
        Created,

        /// <summary>
        /// Data point is in the process of being revealed.
        /// </summary>
        Showing,

        /// <summary>
        /// Data point is visible in the plot area.
        /// </summary>
        Normal,

        /// <summary>
        /// Data point is in the process of being removed from the plot area.
        /// </summary>
        PendingRemoval,

        /// <summary>
        /// Data point is in the process of being hidden.
        /// </summary>
        Hiding,

        /// <summary>
        /// Data point is hidden.
        /// </summary>
        Hidden,
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Axis/IRangeAxis.cs<|end_filename|>
﻿// (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;
using System.Diagnostics.CodeAnalysis;
using System.Windows;

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// An axis with a range.
    /// </summary>
    public interface IRangeAxis : IAxis, IRangeConsumer
    {
        /// <summary>
        /// Gets the range of values displayed on the axis.
        /// </summary>
        Range<IComparable> Range { get; }

        /// <summary>
        /// The plot area coordinate of a value.
        /// </summary>
        /// <param name="position">The position at which to retrieve the plot 
        /// area coordinate.</param>
        /// <returns>The plot area coordinate.</returns>
        IComparable GetValueAtPosition(UnitValue position);

        /// <summary>
        /// Gets the origin value on the axis.
        /// </summary>
        IComparable Origin { get; }
    }
}


<|start_filename|>sourceCode/Toolkit-release/DataVisualization/GenericEqualityComparer.cs<|end_filename|>
﻿// (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;

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// A generic equality comparer.
    /// </summary>
    /// <typeparam name="T">The type of the objects being compared.</typeparam>
    internal class GenericEqualityComparer<T> : EqualityComparer<T>
    {
        /// <summary>
        /// Gets or sets a function which determines whether two items are equal.
        /// </summary>
        public Func<T, T, bool> EqualityFunction { get; set; }

        /// <summary>
        /// Gets or sets a function that returns a hash code for an object.
        /// </summary>
        public Func<T, int> HashCodeFunction { get; set; }

        /// <summary>
        /// Initializes a new instance of the GenericEqualityComparer class.
        /// </summary>
        /// <param name="equalityFunction">A function which determines whether 
        /// two items are equal.</param>
        /// <param name="hashCodeFunction">A function that returns a hash code 
        /// for an object.</param>
        public GenericEqualityComparer(Func<T, T, bool> equalityFunction, Func<T, int> hashCodeFunction)
        {
            this.EqualityFunction = equalityFunction;
            this.HashCodeFunction = hashCodeFunction;
        }

        /// <summary>
        /// A function which determines whether two items are equal.
        /// </summary>
        /// <param name="x">The left object.</param>
        /// <param name="y">The right object.</param>
        /// <returns>A value indicating whether the objects. are equal.</returns>
        public override bool Equals(T x, T y)
        {
            return EqualityFunction(x, y);
        }

        /// <summary>
        /// A function that returns a hash code for an object.
        /// </summary>
        /// <param name="obj">The object to returns a hash code for.</param>
        /// <returns>The hash code for the object.</returns>
        public override int GetHashCode(T obj)
        {
            return HashCodeFunction(obj);
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Axis/OrientedAxisGridLines.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// This control draws gridlines with the help of an axis.
    /// </summary>
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")]
    [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected capitalization.")]
    internal class OrientedAxisGridLines : DisplayAxisGridLines
    {
        /// <summary>
        /// A pool of grid lines.
        /// </summary>
        private ObjectPool<Line> _gridLinePool;

        /// <summary>
        /// Initializes a new instance of the OrientedAxisGridLines class.
        /// </summary>
        /// <param name="displayAxis">The axis to draw grid lines for.</param>
        public OrientedAxisGridLines(DisplayAxis displayAxis)
            : base(displayAxis)
        {
            _gridLinePool = new ObjectPool<Line>(() => new Line { Style = Axis.GridLineStyle });
        }

        /// <summary>
        /// Draws the grid lines.
        /// </summary>
        protected override void Invalidate()
        {
            _gridLinePool.Reset();

            try
            {
                IList<UnitValue> intervals = Axis.InternalGetMajorGridLinePositions().ToList();

                this.Children.Clear();

                double maximumHeight = Math.Max(Math.Round(ActualHeight - 1), 0);
                double maximumWidth = Math.Max(Math.Round(ActualWidth - 1), 0);
                for (int index = 0; index < intervals.Count; index++)
                {
                    double currentValue = intervals[index].Value;

                    double position = currentValue;
                    if (!double.IsNaN(position))
                    {
                        Line line = _gridLinePool.Next();
                        if (Axis.Orientation == AxisOrientation.Y)
                        {
                            line.Y1 = line.Y2 = maximumHeight - Math.Round(position - (line.StrokeThickness / 2));
                            line.X1 = 0.0;
                            line.X2 = maximumWidth;
                        }
                        else if (Axis.Orientation == AxisOrientation.X)
                        {
                            line.X1 = line.X2 = Math.Round(position - (line.StrokeThickness / 2));
                            line.Y1 = 0.0;
                            line.Y2 = maximumHeight;
                        }
                        // workaround for '1px line thickness issue'
                        if (line.StrokeThickness % 2 > 0)
                        {
                            line.SetValue(Canvas.LeftProperty, 0.5);
                            line.SetValue(Canvas.TopProperty, 0.5);
                        }
                        this.Children.Add(line);
                    }
                }
            }
            finally
            {
                _gridLinePool.Done();
            }
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Series/IRequireGlobalSeriesIndex.cs<|end_filename|>
﻿// (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.

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// Defines methods on classes that contain a global index.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    public interface IRequireGlobalSeriesIndex
    {
        /// <summary>
        /// Occurs when a global series index changes.
        /// </summary>
        /// <param name="globalIndex">The global index that has changed.
        /// </param>
        void GlobalSeriesIndexChanged(int? globalIndex);
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization.Design/DataPointMetadata.cs<|end_filename|>
﻿// (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.

extern alias Silverlight;
using System.ComponentModel;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design;
using Microsoft.Windows.Design.Metadata;
using Microsoft.Windows.Design.PropertyEditing;
using SSWCDC = Silverlight::System.Windows.Controls.DataVisualization.Charting;

namespace System.Windows.Controls.DataVisualization.Design
{
    /// <summary>
    /// To register design time metadata for SSWCDC.DataPoint.
    /// </summary>
    internal class DataPointMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time metadata for SSWCDC.DataPoint.
        /// </summary>
        public DataPointMetadata()
            : base()
        {
            AddCallback(
                typeof(SSWCDC.DataPoint),
                b =>
                {
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.ActualDependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.ActualIndependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.DependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.DependentValueStringFormat),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.FormattedDependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.FormattedIndependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.IndependentValue),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.IndependentValueStringFormat),
                        new CategoryAttribute(Properties.Resources.DataVisualization));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.IsSelectionEnabled),
                        new BrowsableAttribute(false));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.ActualIndependentValue),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.DependentValue),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DataPoint>(x => x.IndependentValue),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));

#if MWD40
                    b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.DataVisualizationControlParts, false));
#endif
                });
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Collections/MultipleDictionary.cs<|end_filename|>
﻿// (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;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace System.Windows.Controls.DataVisualization.Collections
{
    /// <summary>
    /// Implements a dictionary that can store multiple values for the same key.
    /// </summary>
    /// <typeparam name="TKey">Type for keys.</typeparam>
    /// <typeparam name="TValue">Type for values.</typeparam>
    internal class MultipleDictionary<TKey, TValue>
    {
        /// <summary>
        /// Gets or sets the BinaryTree instance used to store the dictionary values.
        /// </summary>
        protected LeftLeaningRedBlackTree<TKey, TValue> BinaryTree { get; set; }

        /// <summary>
        /// Initializes a new instance of the MultipleDictionary class.
        /// </summary>
        protected MultipleDictionary()
        {
        }

        /// <summary>
        /// Initializes a new instance of the MultipleDictionary class.
        /// </summary>
        /// <param name="allowDuplicateValues">The parameter is not used.</param>
        /// <param name="keyEqualityComparer">The parameter is not used.</param>
        /// <param name="valueEqualityComparer">The parameter is not used.</param>
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "allowDuplicateValues", Justification = "Unused parameter exists for API compatibility.")]
        public MultipleDictionary(bool allowDuplicateValues, IEqualityComparer<TKey> keyEqualityComparer, IEqualityComparer<TValue> valueEqualityComparer)
        {
            Debug.Assert(null != keyEqualityComparer, "keyEqualityComparer must not be null.");
            Debug.Assert(null != valueEqualityComparer, "valueEqualityComparer must not be null.");
            BinaryTree = new LeftLeaningRedBlackTree<TKey, TValue>(
                (left, right) => keyEqualityComparer.GetHashCode(left).CompareTo(keyEqualityComparer.GetHashCode(right)),
                (left, right) => valueEqualityComparer.GetHashCode(left).CompareTo(valueEqualityComparer.GetHashCode(right)));
        }

        /// <summary>
        /// Adds a key/value pair to the dictionary.
        /// </summary>
        /// <param name="key">Key to add.</param>
        /// <param name="value">Value to add.</param>
        public void Add(TKey key, TValue value)
        {
            BinaryTree.Add(key, value);
        }

        /// <summary>
        /// Removes a key/value pair from the dictionary.
        /// </summary>
        /// <param name="key">Key to remove.</param>
        /// <param name="value">Value to remove.</param>
        /// <returns>True if the value was present and removed.</returns>
        public bool Remove(TKey key, TValue value)
        {
            return BinaryTree.Remove(key, value);
        }

        /// <summary>
        /// Gets the count of values in the dictionary.
        /// </summary>
        public int Count
        {
            get
            {
                return BinaryTree.Count;
            }
        }

        /// <summary>
        /// Returns the collection of values corresponding to a key.
        /// </summary>
        /// <param name="key">Specified key.</param>
        /// <returns>Collection of values.</returns>
        public ICollection<TValue> this[TKey key]
        {
            get
            {
                return BinaryTree.GetValuesForKey(key).ToList();
            }
        }

        /// <summary>
        /// Clears the items in the dictionary.
        /// </summary>
        public void Clear()
        {
            BinaryTree.Clear();
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization.Design/DisplayAxisMetadata.cs<|end_filename|>
﻿// (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.

extern alias Silverlight;
using System.ComponentModel;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design;
using Microsoft.Windows.Design.Metadata;
using Microsoft.Windows.Design.PropertyEditing;
using SSWCDC = Silverlight::System.Windows.Controls.DataVisualization.Charting;

namespace System.Windows.Controls.DataVisualization.Design
{
    /// <summary>
    /// To register design time metadata for SSWCDC.DisplayAxis.
    /// </summary>
    internal class DisplayAxisMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time metadata for SSWCDC.DisplayAxis.
        /// </summary>
        public DisplayAxisMetadata()
            : base()
        {
            AddCallback(
                typeof(SSWCDC.DisplayAxis),
                b =>
                {
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.ShowGridLines),
                        new CategoryAttribute(Properties.Resources.CommonProperties));
                    b.AddCustomAttributes(
                       Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.Title),
                       new CategoryAttribute(Properties.Resources.CommonProperties));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.SeriesHost),
                        new CategoryAttribute(Properties.Resources.DataVisualization));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.AxisLabelStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.GridLineStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.MajorTickMarkStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.TitleStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.Title),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));

#if MWD40
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.TitleStyle),
                        new DataContextValueSourceAttribute(
                            Extensions.GetMemberName<SSWCDC.DisplayAxis>(x => x.Title),
                            false));
#endif 
                });
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization.Design/PieDataPointMetadata.cs<|end_filename|>
﻿// (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.

extern alias Silverlight;
using System.ComponentModel;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design.Metadata;
using SSWCDC = Silverlight::System.Windows.Controls.DataVisualization.Charting;

namespace System.Windows.Controls.DataVisualization.Design
{
    /// <summary>
    /// To register design time metadata for SSWCDC.PieDataPoint.
    /// </summary>
    internal class PieDataPointMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time metadata for SSWCDC.PieDataPoint.
        /// </summary>
        public PieDataPointMetadata()
            : base()
        {
            AddCallback(
                typeof(SSWCDC.PieDataPoint),
                b =>
                {
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.ActualOffsetRatio),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.ActualRatio),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.FormattedRatio),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.Geometry),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.GeometryHighlight),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.GeometrySelection),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.OffsetRatio),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.Ratio),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.PieDataPoint>(x => x.RatioStringFormat),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                });
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/TreeMap/TreeMap.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
using System.Windows.Markup;

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// Represents a control which can display hierarchical data as a set of nested rectangles. 
    /// Each item in the hierarchy is laid out in a rectangular area of a size proportional to 
    /// the value associated with the item.
    /// </summary>
    /// <remarks>
    /// You populate a TreeMap by setting its ItemsSource property to the root of the hierarchy 
    /// you would like to display. The ItemDefinition property must be set to an instance of a 
    /// TreeMapItemDefinition with appropriate bindings for Value (identifying the value to be used
    /// when calculating relative item sizes) and ItemsSource (identifying the collection of 
    /// children for each item).
    /// </remarks>
    /// <QualityBand>Experimental</QualityBand>
    [TemplatePart(Name = ContainerName, Type = typeof(Canvas))]
    [ContentProperty("ItemsSource")]
    public class TreeMap : Control
    {
        /// <summary>
        /// The name of the Container template part.
        /// </summary>
        private const string ContainerName = "Container";

        #region private object InterpolatorValue
        /// <summary>
        /// Identifies the InterpolatorValue dependency property.
        /// </summary>
        private static readonly DependencyProperty InterpolatorValueProperty =
            DependencyProperty.Register(
                "InterpolatorValue",
                typeof(object),
                typeof(TreeMap),
                null);

        /// <summary>
        /// Gets or sets a generic value used as a temporary storage used as a source for TargetName/TargetProperty binding.
        /// </summary>
        private object InterpolatorValue
        {
            get { return (object)GetValue(InterpolatorValueProperty); }
            set { SetValue(InterpolatorValueProperty, value); }
        }
        #endregion

        /// <summary>
        /// Holds a helper object used to extract values using a property path.
        /// </summary>
        private BindingExtractor _helper;

        /// <summary>
        /// The roots of the pre-calculated parallel tree of TreeMapNodes.
        /// </summary>
        private IEnumerable<TreeMapNode> _nodeRoots;

        /// <summary>
        /// Cached sequence of all TreeMapNodes used by GetTreeMapNodes.
        /// </summary>
        private IEnumerable<TreeMapNode> _getTreeMapNodesCache;

        #region public TreeMapItemDefinitionSelector TreeMapItemDefinitionSelector
        /// <summary>
        /// Gets or sets the selector used to choose the item template dynamically.
        /// </summary>
        public TreeMapItemDefinitionSelector ItemDefinitionSelector
        {
            get { return (TreeMapItemDefinitionSelector)GetValue(ItemDefinitionSelectorProperty); }
            set { SetValue(ItemDefinitionSelectorProperty, value); }
        }

        /// <summary>
        /// Identifies the ItemDefinitionSelector dependency property.
        /// </summary>
        public static readonly DependencyProperty ItemDefinitionSelectorProperty = 
            DependencyProperty.Register(
                "ItemDefinitionSelector",
                typeof(TreeMapItemDefinitionSelector),
                typeof(TreeMap),
                new PropertyMetadata(OnItemDefinitionSelectorPropertyChanged));

        /// <summary>
        /// Called when the value of the TreeMapItemDefinitionSelectorProperty property changes.
        /// </summary>
        /// <param name="d">Reference to the TreeMap object.</param>
        /// <param name="e">Event handler arguments.</param>
        private static void OnItemDefinitionSelectorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TreeMap treeMap = d as TreeMap;
            if (treeMap != null)
            {
                TreeMapItemDefinitionSelector oldValue = e.OldValue as TreeMapItemDefinitionSelector;
                TreeMapItemDefinitionSelector newValue = e.NewValue as TreeMapItemDefinitionSelector;
                treeMap.OnItemDefinitionSelectorPropertyChanged(oldValue, newValue);
            }
        }

        /// <summary>
        /// Called when the value of the ItemDefinitionSelectorProperty property changes.
        /// Triggers a recalculation of the layout.
        /// </summary>
        /// <param name="oldValue">The old selector.</param>
        /// <param name="newValue">The new selector.</param>
        protected virtual void OnItemDefinitionSelectorPropertyChanged(TreeMapItemDefinitionSelector oldValue, TreeMapItemDefinitionSelector newValue)
        {
            RebuildTree();
        }

        #endregion

        #region public TreeMapItemDefinition ItemDefinition
        /// <summary>
        /// Gets or sets a value representing the template used to display each item.
        /// </summary>
        public TreeMapItemDefinition ItemDefinition
        {
            get { return (TreeMapItemDefinition)GetValue(ItemDefinitionProperty); }
            set { SetValue(ItemDefinitionProperty, value); }
        }

        /// <summary>
        /// Identifies the ItemDefinition dependency property.
        /// </summary>
        public static readonly DependencyProperty ItemDefinitionProperty = 
            DependencyProperty.Register(
                "ItemDefinition",
                typeof(TreeMapItemDefinition),
                typeof(TreeMap),
                new PropertyMetadata(OnItemDefinitionPropertyChanged));

        /// <summary>
        /// Called when the value of the ItemDefinitionProperty property changes.
        /// </summary>
        /// <param name="d">Reference to the TreeMap object.</param>
        /// <param name="e">Event handler arguments.</param>
        private static void OnItemDefinitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TreeMap treeMap = d as TreeMap;
            if (treeMap != null)
            {
                TreeMapItemDefinition oldValue = e.OldValue as TreeMapItemDefinition;
                TreeMapItemDefinition newValue = e.NewValue as TreeMapItemDefinition;

                // Unregister old TreeMapItemDefinition
                if (oldValue != null)
                {
                    oldValue.PropertyChanged -= treeMap.OnItemDefinitionPropertyChanged;
                }

                // Register new TreeMapItemDefinition
                if (newValue != null)
                {
                    newValue.PropertyChanged += treeMap.OnItemDefinitionPropertyChanged;
                }

                treeMap.OnItemDefinitionPropertyChanged(oldValue, newValue);
            }
        }

        /// <summary>
        /// This callback ensures that any change in TreeMapItemDefinition.
        /// </summary>
        /// <param name="sender">Source TreeMapItemDefinition object.</param>
        /// <param name="e">Event handler arguments (parameter name).</param>
        private void OnItemDefinitionPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RebuildTree();
        }

        /// <summary>
        /// Called when the value of the ItemDefinitionProperty property changes.
        /// Triggers a recalculation of the layout.
        /// </summary>
        /// <param name="oldValue">The old item definition.</param>
        /// <param name="newValue">The new item definition.</param>
        protected virtual void OnItemDefinitionPropertyChanged(TreeMapItemDefinition oldValue, TreeMapItemDefinition newValue)
        {
            RebuildTree();
        }
        #endregion 

        #region public IEnumerable ItemsSource
        /// <summary>
        /// Gets or sets a value representing the list of hierarchies used to generate
        /// content for the TreeMap.
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        /// Identifies the ItemsSource dependency property.
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty = 
            DependencyProperty.Register(
                "ItemsSource", 
                typeof(IEnumerable),
                typeof(TreeMap),
                new PropertyMetadata(OnItemsSourcePropertyChanged));

        /// <summary>
        /// Called when the value of the ItemsSourceProperty property changes.
        /// </summary>
        /// <param name="d">Reference to the TreeMap object.</param>
        /// <param name="e">Event handler arguments.</param>
        private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TreeMap treeMap = d as TreeMap;
            if (treeMap != null)
            {
                IEnumerable oldValue = e.OldValue as IEnumerable;
                IEnumerable newValue = e.NewValue as IEnumerable;
                treeMap.OnItemsSourcePropertyChanged(oldValue, newValue);
            }
        }

        /// <summary>
        /// Called when the value of the ItemsSourceProperty property changes.
        /// </summary>
        /// <param name="oldValue">The old ItemsSource collection.</param>
        /// <param name="newValue">The new ItemsSource collection.</param>
        protected virtual void OnItemsSourcePropertyChanged(IEnumerable oldValue, IEnumerable newValue)
        {
            // Remove handler for oldValue.CollectionChanged (if present)
            INotifyCollectionChanged oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged;
            if (null != oldValueINotifyCollectionChanged)
            {
                // Detach the WeakEventListener
                if (null != _weakEventListener)
                {
                    _weakEventListener.Detach();
                    _weakEventListener = null;
                }
            }

            // Add handler for newValue.CollectionChanged (if possible)
            INotifyCollectionChanged newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
            if (null != newValueINotifyCollectionChanged)
            {
                // Use a WeakEventListener so that the backwards reference doesn't keep this object alive
                _weakEventListener = new WeakEventListener<TreeMap, object, NotifyCollectionChangedEventArgs>(this);
                _weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs);
                _weakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent;
                newValueINotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent;
            }

            // Handle property change
            RebuildTree();
        }

        /// <summary>
        /// WeakEventListener used to handle INotifyCollectionChanged events.
        /// </summary>
        private WeakEventListener<TreeMap, object, NotifyCollectionChangedEventArgs> _weakEventListener;

        /// <summary>
        /// Method that handles the ObservableCollection.CollectionChanged event for the ItemsSource property.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">The event data.</param>
        private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            RebuildTree();
        }
        #endregion

        #region private Collection<Interpolator> Interpolators
        /// <summary>
        /// Gets or sets a value representing a collection of interpolators to use in TreeMap.
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Setter is public to work around a limitation with the XAML editing tools.")]
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Setter is public to work around a limitation with the XAML editing tools.")]
        public Collection<Interpolator> Interpolators
        {
            get { return (Collection<Interpolator>)GetValue(InterpolatorsProperty); }
            set { throw new NotSupportedException(Properties.Resources.TreeMap_Interpolators_SetterNotSupported); }
        }
        
        /// <summary>
        /// Identifies the Interpolators dependency property.
        /// </summary>
        public static readonly DependencyProperty InterpolatorsProperty = 
            DependencyProperty.Register(
                "Interpolators",
                typeof(Collection<Interpolator>),
                typeof(TreeMap),
                new PropertyMetadata(OnInterpolatorsPropertyChanged));

        /// <summary>
        /// Called when the value of the InterpolatorsProperty property changes.
        /// </summary>
        /// <param name="d">Reference to the TreeMap object.</param>
        /// <param name="e">Event handler arguments.</param>
        private static void OnInterpolatorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TreeMap treeMap = d as TreeMap;
            if (treeMap != null)
            {
                Collection<Interpolator> oldValue = e.OldValue as Collection<Interpolator>;
                Collection<Interpolator> newValue = e.NewValue as Collection<Interpolator>;
                treeMap.OnInterpolatorsPropertyChanged(oldValue, newValue);
            }
        }

        /// <summary>
        /// Called when the value of the InterpolatorsProperty property changes.
        /// Triggers a recalculation of the layout.
        /// </summary>
        /// <param name="oldValue">The old Interpolators collection.</param>
        /// <param name="newValue">The new Interpolators collection.</param>
        protected virtual void OnInterpolatorsPropertyChanged(Collection<Interpolator> oldValue, Collection<Interpolator> newValue)
        {
            RebuildTree();
        }
        #endregion
        
        #region Template Parts
        /// <summary>
        /// The Container template part is used to hold all the items inside
        /// a TreeMap.
        /// </summary>
        private Canvas _containerElement;

        /// <summary>
        /// Gets the Container template part that is used to hold all the items inside
        /// a TreeMap.
        /// </summary>
        internal Canvas ContainerElement
        {
            get { return _containerElement; }
            private set
            {
                // Detach from the old Container element
                if (_containerElement != null)
                {
                    _containerElement.Children.Clear();
                }

                // Attach to the new Container element
                _containerElement = value;
            }
        }
        #endregion

        /// <summary>
        /// Initializes a new instance of the TreeMap class.
        /// </summary>
        public TreeMap()
        {
            _helper = new BindingExtractor();

            DefaultStyleKey = typeof(TreeMap);

            SetValue(InterpolatorsProperty, new ObservableCollection<Interpolator>());
            (Interpolators as ObservableCollection<Interpolator>).CollectionChanged += 
                new NotifyCollectionChangedEventHandler(OnInterpolatorsCollectionChanged);
        }

        /// <summary>
        /// Invoked whenever application code or internal processes call ApplyTemplate. Gets references
        /// to the template parts required by this control.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            ContainerElement = GetTemplateChild(ContainerName) as Canvas;

            RebuildTree();
        }

        /// <summary>
        /// Constructs a new instance of an element used to display an item in the tree. 
        /// </summary>
        /// <remarks>
        /// By default TreeMap will use the template set in its ItemDefinition property, or the value 
        /// returned from GetTemplateForItemOverride if overridden. Override this method to build a 
        /// custom element.
        /// </remarks>
        /// <param name="data">One of the items in the ItemsSource hierarchy.</param>
        /// <param name="level">The level of the item in the hierarchy.</param>
        /// <returns>A new FrameworkElement which will be added to the TreeMap control. If this
        /// method returns null the TreeMap will create the item using the ItemDefinition property,
        /// or the value returned by TreeMapItemDefinitionSelector if specified.</returns>
        protected virtual FrameworkElement GetContainerForItemOverride(object data, int level)
        {
            return null;
        }

        /// <summary>
        /// Performs the Arrange pass of the layout.
        /// </summary>
        /// <remarks>
        /// We round rectangles to snap to nearest pixels. We do that to avoid 
        /// anti-aliasing which results in better appearance. Moreover to get
        /// correct layout we would need to use UseLayoutRounding=false which
        /// is Silverlight specific. A side effect is that areas for rectangles 
        /// in the visual tree no longer can be used to compare them as dimensions
        /// are not rounded and therefore not precise. 
        /// </remarks>
        /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
        /// <returns>The actual size used.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            // Sets ActualHeight & ActualWidth for the container
            finalSize = base.ArrangeOverride(finalSize);

            if (_nodeRoots != null && ContainerElement != null)
            {
                // Create a temporary pseudo-root node containing all the top-level nodes
                TreeMapNode root = new TreeMapNode()
                {
                    Area = _nodeRoots.Sum(x => x.Area),
                    Children = _nodeRoots,
                    ChildItemPadding = new Thickness(0)
                };

                // Calculate associated rectangles. We use ContainerElement, 
                // not finalSize so all elements that are above it like border 
                // (with padding and border) are taken into account
                IEnumerable<Tuple<Rect, TreeMapNode>> measuredRectangles = ComputeRectangles(
                    root,
                    new Rect(0, 0, ContainerElement.ActualWidth, ContainerElement.ActualHeight));

                // Position everything
                foreach (Tuple<Rect, TreeMapNode> rectangle in measuredRectangles)
                {
                    FrameworkElement element = rectangle.Item2.Element;
                    if (element != null)
                    {
                        double roundedTop = Math.Round(rectangle.Item1.Top);
                        double roundedLeft = Math.Round(rectangle.Item1.Left);
                        double height = Math.Round(rectangle.Item1.Height + rectangle.Item1.Top) - roundedTop;
                        double width = Math.Round(rectangle.Item1.Width + rectangle.Item1.Left) - roundedLeft;

                        // Fully specify element location/size (setting size is required on WPF)
                        Canvas.SetLeft(element, roundedLeft);
                        Canvas.SetTop(element, roundedTop);
                        element.Width = width;
                        element.Height = height;

                        element.Arrange(new Rect(roundedLeft, roundedTop, width, height));
                    }
                }
            }

            return finalSize;
        }

        /// <summary>
        /// Triggers a recalculation of the layout when items are added/removed from the Interpolators collection.
        /// </summary>
        /// <param name="sender">Reference to the Interpolators collection.</param>
        /// <param name="e">Event handler arguments.</param>
        private void OnInterpolatorsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            RebuildTree();
        }

        /// <summary>
        /// Returns a sequence of TreeMapNodes in breadth-first order.
        /// </summary>
        /// <returns>Sequence of TreeMapNodes.</returns>
        private IEnumerable<TreeMapNode> GetTreeMapNodes()
        {
            if (_getTreeMapNodesCache == null)
            {
                // Create a new list
                List<TreeMapNode> allNodes = new List<TreeMapNode>();

                // Seed the queue with the roots
                Queue<TreeMapNode> nodes = new Queue<TreeMapNode>();
                foreach (TreeMapNode node in _nodeRoots ?? Enumerable.Empty<TreeMapNode>())
                {
                    nodes.Enqueue(node);
                }
                // Process the queue in breadth-first order
                while (0 < nodes.Count)
                {
                    TreeMapNode node = nodes.Dequeue();
                    allNodes.Add(node);
                    foreach (TreeMapNode child in node.Children)
                    {
                        nodes.Enqueue(child);
                    }
                }

                // Cache the list
                _getTreeMapNodesCache = allNodes;
            }

            // Return the cached sequence
            return _getTreeMapNodesCache;
        }

        /// <summary>
        /// Recursively computes TreeMap rectangles given the root node and the bounding rectangle as start.
        /// </summary>
        /// <param name="root">Root of the TreeMapNode tree.</param>
        /// <param name="boundingRectangle">Bounding rectangle which will be sub-divided.</param>
        /// <returns>A list of RectangularAreas containing a rectangle for each node in the tree.</returns>
        private IEnumerable<Tuple<Rect, TreeMapNode>> ComputeRectangles(TreeMapNode root, Rect boundingRectangle)
        {
            Queue<Tuple<Rect, TreeMapNode>> treeQueue = new Queue<Tuple<Rect, TreeMapNode>>();
            treeQueue.Enqueue(new Tuple<Rect, TreeMapNode>(boundingRectangle, root));

            // Perform a breadth-first traversal of the tree
            SquaringAlgorithm algorithm = new SquaringAlgorithm();
            while (treeQueue.Count > 0)
            {
                Tuple<Rect, TreeMapNode> currentParent = treeQueue.Dequeue();

                yield return currentParent;

                foreach (Tuple<Rect, TreeMapNode> rectangle in
                    algorithm.Split(currentParent.Item1, currentParent.Item2, currentParent.Item2.ChildItemPadding))
                {
                    treeQueue.Enqueue(rectangle);
                }
            }
        }

        /// <summary>
        /// Builds the parallel trees of TreeMapNodes with references to the original user's trees.
        /// </summary>
        /// <param name="nodes">The list of roots of the user hierarchies (whatever was passed through ItemsSource).</param>
        /// <param name="level">Level being processed at this recursive call (the root node is at level 0).</param>
        /// <returns>The list of roots of the internal trees of TreeMapNodes.</returns>
        private IEnumerable<TreeMapNode> BuildTreeMapTree(IEnumerable nodes, int level)
        {
            List<TreeMapNode> retList = new List<TreeMapNode>();

            if (nodes == null)
            {
                return retList;
            }

            foreach (object root in nodes)
            {
                // Give the template selector a chance to override the template for this item.
                TreeMapItemDefinition template = null;
                if (ItemDefinitionSelector != null)
                {
                    template = ItemDefinitionSelector.SelectItemDefinition(this, root, level);
                }

                // Use the default otherwise
                if (template == null)
                {
                    template = ItemDefinition;
                }

                if (template == null)
                {
                    throw new ArgumentException(
                        Properties.Resources.TreeMap_BuildTreeMapTree_TemplateNotSet);
                }

                // Silently create 0 elements if ValueBinding is set to null 
                // in the template
                if (template.ValueBinding != null)
                {
                    IEnumerable objectChildren = (template.ItemsSource != null) ?
                        _helper.RetrieveProperty(root, template.ItemsSource) as IEnumerable :
                        null;
                    IEnumerable<TreeMapNode> children = (objectChildren != null) ?
                        BuildTreeMapTree(objectChildren, level + 1) :
                        children = Enumerable.Empty<TreeMapNode>();

                    // Subscribe to CollectionChanged for the collection
                    WeakEventListener<TreeMap, object, NotifyCollectionChangedEventArgs> weakEventListener = null;
                    INotifyCollectionChanged objectChildrenINotifyCollectionChanged = objectChildren as INotifyCollectionChanged;
                    if (objectChildrenINotifyCollectionChanged != null)
                    {
                        // Use a WeakEventListener so that the backwards reference doesn't keep this object alive
                        weakEventListener = new WeakEventListener<TreeMap, object, NotifyCollectionChangedEventArgs>(this);
                        weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs);
                        weakEventListener.OnDetachAction = (wel) => objectChildrenINotifyCollectionChanged.CollectionChanged -= wel.OnEvent;
                        objectChildrenINotifyCollectionChanged.CollectionChanged += weakEventListener.OnEvent;
                    }

                    // Auto-aggregate children area values
                    double area;
                    if (children.Any())
                    {
                        area = children.Sum(x => x.Area);
                    }
                    else
                    {
                        IConvertible value = _helper.RetrieveProperty(root, template.ValueBinding) as IConvertible;
                        if (value == null)
                        {
                            // Provide a default value so there's something to display
                            value = 1.0;
                        }

                        area = value.ToDouble(CultureInfo.InvariantCulture);
                    }

                    // Do not include elements with negative or 0 size in the
                    // VisualTransition tree. We skip interpolation for such
                    // elements as well
                    if (area > 0)
                    {
                        // Calculate ranges for all interpolators, only consider leaf 
                        // nodes in the LeafNodesOnly mode, or all nodes in the AllNodes 
                        // mode.
                        foreach (Interpolator interpolator in Interpolators)
                        {
                            if (interpolator.InterpolationMode == InterpolationMode.AllNodes || !children.Any())
                            {
                                interpolator.IncludeInRange(root);
                            }
                        }

                        retList.Add(new TreeMapNode()
                                        {
                                            DataContext = root,
                                            Level = level,
                                            Area = area,
                                            ItemDefinition = template,
                                            ChildItemPadding = template.ChildItemPadding,
                                            Children = children,
                                            WeakEventListener = weakEventListener,
                                        });
                    }
                }
            }
            
            return retList;
        }

        /// <summary>
        /// Extracts all children from the user's trees (ItemsSource) into a flat list, and 
        /// creates UI elements for them.
        /// </summary>
        private void CreateChildren()
        {
            // Breadth-first traversal so elements closer to the root will be added first,
            // so that leaf elements will show on top of them.
            foreach (TreeMapNode current in GetTreeMapNodes())
            {
                // Create the UI element and keep a reference to it in our tree
                FrameworkElement element = GetContainerForItemOverride(current.DataContext, current.Level);
                if (element == null && current.ItemDefinition.ItemTemplate != null)
                {
                    element = current.ItemDefinition.ItemTemplate.LoadContent() as FrameworkElement;
                }

                // If an element was created
                if (element != null)
                {
                    current.Element = element;

                    // Apply interpolators to element
                    foreach (Interpolator interpolator in Interpolators)
                    {
                        // Apply interpolators only for leaf nodes in the 
                        // LeafNodesOnly mode, or for all nodes in the AllNodes 
                        // mode.
                        if (interpolator.InterpolationMode == InterpolationMode.AllNodes || !current.Children.Any())
                        {
                            DependencyObject target = element.FindName(interpolator.TargetName) as DependencyObject;
                            if (target != null)
                            {
                                SetBinding(
                                    InterpolatorValueProperty,
                                    new Binding(interpolator.TargetProperty) { Source = target, Mode = BindingMode.TwoWay });

                                if (interpolator.DataRangeBinding == null)
                                {
                                    throw new ArgumentException(
                                        Properties.Resources.TreeMap_CreateChildren_InterpolatorBindingNotSet);
                                }

                                // Extract the current value to interpolate
                                IConvertible value =
                                    _helper.RetrieveProperty(current.DataContext, interpolator.DataRangeBinding) as
                                    IConvertible;
                                if (value == null)
                                {
                                    throw new ArgumentException(
                                        Properties.Resources.Interpolator_IncludeInRange_DataRangeBindingNotIConvertible);
                                }

                                // This will update the TargetProperty of the TargetName object
                                InterpolatorValue = interpolator.Interpolate(value.ToDouble(CultureInfo.InvariantCulture));
                            }
                        }
                    }

                    // Add new child to the panel
                    element.DataContext = current.DataContext;
                    ContainerElement.Children.Add(element);
                }
            }
        }

        /// <summary>
        /// Called internally whenever a property of TreeMap is changed and the internal 
        /// structures need to be rebuilt in order to recalculate the layout.
        /// </summary>
        private void RebuildTree()
        {
            if (ContainerElement != null)
            {
                // Unhook from CollectionChanged
                foreach (TreeMapNode treeMapNode in GetTreeMapNodes().Where(n => n.WeakEventListener != null))
                {
                    treeMapNode.WeakEventListener.Detach();
                }

                // Reset all interpolators
                foreach (Interpolator interpolator in Interpolators)
                {
                    interpolator.ActualDataMinimum = double.PositiveInfinity;
                    interpolator.ActualDataMaximum = double.NegativeInfinity;
                    interpolator.DataContext = this.DataContext;
                }

                // Build the parallel tree of TreeMapNodes needed by the algorithm
                _nodeRoots = BuildTreeMapTree(ItemsSource, 0);

                // Clear cache
                _getTreeMapNodesCache = null;

                // Populate the TreeMap panel with a flat list of all children
                // in the hierarchy passed in.
                ContainerElement.Children.Clear();
                CreateChildren();

                // Refresh UI
                InvalidateArrange();
            }
        }
    }
}


<|start_filename|>sourceCode/Toolkit-release/DataVisualization/ReadOnlyObservableCollection.cs<|end_filename|>
﻿// (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;
using System.Collections.ObjectModel;

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// An observable collection that can only be written to by internal 
    /// classes.
    /// </summary>
    /// <typeparam name="T">The type of object in the observable collection.
    /// </typeparam>
    internal class ReadOnlyObservableCollection<T> : NoResetObservableCollection<T>
    {
        /// <summary>
        /// Gets or sets a value indicating whether the owner is writing to the 
        /// collection.
        /// </summary>
        private bool IsMutating { get; set; }

        /// <summary>
        /// A method that mutates the collection.
        /// </summary>
        /// <param name="action">The action to mutate the collection.</param>
        public void Mutate(Action<ReadOnlyObservableCollection<T>> action)
        {
            IsMutating = true;
            try
            {
                action(this);
            }
            finally
            {
                IsMutating = false;
            }
        }

        /// <summary>
        /// Removes an item from the collection at an index.
        /// </summary>
        /// <param name="index">The index to remove.</param>
        protected override void RemoveItem(int index)
        {
            if (!IsMutating)
            {
                throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly);
            }
            else
            {
                base.RemoveItem(index);
            }
        }

        /// <summary>
        /// Sets an item at a particular location in the collection.
        /// </summary>
        /// <param name="index">The location to set an item.</param>
        /// <param name="item">The item to set.</param>
        protected override void SetItem(int index, T item)
        {
            if (!IsMutating)
            {
                throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly);
            }
            else
            {
                base.SetItem(index, item);
            }
        }

        /// <summary>
        /// Inserts an item in the collection.
        /// </summary>
        /// <param name="index">The index at which to insert the item.</param>
        /// <param name="item">The item to insert.</param>
        protected override void InsertItem(int index, T item)
        {
            if (!IsMutating)
            {
                throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly);
            }
            else
            {
                base.InsertItem(index, item);
            }
        }

        /// <summary>
        /// Clears the items from the collection.
        /// </summary>
        protected override void ClearItems()
        {
            if (!IsMutating)
            {
                throw new NotSupportedException(Properties.Resources.ReadOnlyObservableCollection_CollectionIsReadOnly);
            }
            else
            {
                base.ClearItems();
            }
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/DataPoint/ScatterDataPoint.cs<|end_filename|>
﻿// (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.

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// Represents a data point used for a scatter series.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    [TemplateVisualState(Name = DataPoint.StateCommonNormal, GroupName = DataPoint.GroupCommonStates)]
    [TemplateVisualState(Name = DataPoint.StateCommonMouseOver, GroupName = DataPoint.GroupCommonStates)]
    [TemplateVisualState(Name = DataPoint.StateSelectionUnselected, GroupName = DataPoint.GroupSelectionStates)]
    [TemplateVisualState(Name = DataPoint.StateSelectionSelected, GroupName = DataPoint.GroupSelectionStates)]
    [TemplateVisualState(Name = DataPoint.StateRevealShown, GroupName = DataPoint.GroupRevealStates)]
    [TemplateVisualState(Name = DataPoint.StateRevealHidden, GroupName = DataPoint.GroupRevealStates)]
    public partial class ScatterDataPoint : DataPoint
    {
#if !SILVERLIGHT
        /// <summary>
        /// Initializes the static members of the ScatterDataPoint class.
        /// </summary>
        static ScatterDataPoint()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScatterDataPoint), new FrameworkPropertyMetadata(typeof(ScatterDataPoint)));
        }

#endif
        /// <summary>
        /// Initializes a new instance of the ScatterDataPoint class.
        /// </summary>
        public ScatterDataPoint()
        {
#if SILVERLIGHT
            this.DefaultStyleKey = typeof(ScatterDataPoint);
#endif
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/StringFormatConverter.cs<|end_filename|>
﻿// (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;
using System.Globalization;
using System.Windows.Data;

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// Converts a value to a string using a format string.
    /// </summary>
    public class StringFormatConverter : IValueConverter
    {
        /// <summary>
        /// Converts a value to a string by formatting it.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <param name="targetType">The target type of the conversion.</param>
        /// <param name="parameter">The format string.</param>
        /// <param name="culture">The culture to use for conversion.</param>
        /// <returns>The formatted string.</returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
            {
                return string.Empty;
            }

            return string.Format(CultureInfo.CurrentCulture, (parameter as string) ?? "{0}", value);
        }

        /// <summary>
        /// Converts a value from a string to a target type.
        /// </summary>
        /// <param name="value">The value to convert to a string.</param>
        /// <param name="targetType">The target type of the conversion.</param>
        /// <param name="parameter">A parameter used during the conversion
        /// process.</param>
        /// <param name="culture">The culture to use for the conversion.</param>
        /// <returns>The converted object.</returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/AssemblyInfoShared.cs<|end_filename|>
﻿// (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;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Windows.Markup;

// Silverlight/WPF shared settings
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCopyright("© Microsoft Corporation.  All rights reserved.")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyTitle("System.Windows.Controls.DataVisualization.Toolkit")]
[assembly: AssemblyTrademark("")]
[assembly: CLSCompliant(true)]
[assembly: ComVisible(false)]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: XmlnsDefinitionAttribute("clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit", "System.Windows.Controls.DataVisualization")]
[assembly: XmlnsDefinitionAttribute("clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit", "System.Windows.Controls.DataVisualization.Charting")]
[assembly: XmlnsPrefix("clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit", "visualizationToolkit")]
[assembly: XmlnsPrefix("clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit", "chartingToolkit")]

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Axis/ValueMargin.cs<|end_filename|>
﻿// (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.

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// A margin specified for a given value.
    /// </summary>
    public struct ValueMargin
    {
        /// <summary>
        /// Gets the value that the margin is associated with.
        /// </summary>
        public object Value { get; private set; }

        /// <summary>
        /// Gets the low margin for a value.
        /// </summary>
        public double LowMargin { get; private set; }

        /// <summary>
        /// Gets the high margin for a value.
        /// </summary>
        public double HighMargin { get; private set; }

        /// <summary>
        /// Initializes a new instance of the ValueMargin class.
        /// </summary>
        /// <param name="value">The value the margin is associated with.</param>
        /// <param name="lowMargin">The lower margin.</param>
        /// <param name="highMargin">The higher margin.</param> 
        public ValueMargin(object value, double lowMargin, double highMargin) : this()
        {
            Value = value;
            LowMargin = lowMargin;
            HighMargin = highMargin;
        }

        /// <summary>
        /// Determines whether two value margins are equal.
        /// </summary>
        /// <param name="obj">The value margin to compare with this one.</param>
        /// <returns>A value indicating whether the two value margins are equal.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj is ValueMargin)
            {
                ValueMargin valueMargin = (ValueMargin)obj;
                return this.Value.Equals(valueMargin.Value) && this.LowMargin.Equals(valueMargin.LowMargin) && this.HighMargin.Equals(valueMargin.HighMargin);
            }
            return false;
        }

        /// <summary>
        /// Determines whether two unit value objects are equal.
        /// </summary>
        /// <param name="left">The left value margin.</param>
        /// <param name="right">The right value margin.</param>
        /// <returns>A value indicating  whether two value margins objects are 
        /// equal.</returns>
        public static bool operator ==(ValueMargin left, ValueMargin right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Determines whether two value margin objects are not equal.
        /// </summary>
        /// <param name="left">The left value margin.</param>
        /// <param name="right">The right value margin.</param>
        /// <returns>A value indicating whether two value margin objects are not
        /// equal.</returns>
        public static bool operator !=(ValueMargin left, ValueMargin right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Returns the hash code of the value margin object.
        /// </summary>
        /// <returns>The hash code.</returns>
        public override int GetHashCode()
        {
            return this.Value.GetHashCode() ^ this.LowMargin.GetHashCode() ^ this.HighMargin.GetHashCode();
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization.Design/TreeMapMetadata.cs<|end_filename|>
﻿// (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.

extern alias Silverlight;

using System.ComponentModel;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design.Metadata;
using SSWCDTM = Silverlight::System.Windows.Controls.DataVisualization.TreeMap;

namespace System.Windows.Controls.DataVisualization.Design.Controls.Design.Common
{
    /// <summary>
    /// To register design time data for TreeMap.
    /// </summary>
    internal class TreeMapMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time data for TreeMap.
        /// </summary>
        public TreeMapMetadata()
            : base()
        {
            AddCallback(
                typeof(SSWCDTM),
                b =>
                {
                    // move TreeMap items to the DataVisualization tab.
                    // a workaround with direct string names for 
                    // INotifyPropertyChanged
                    b.AddCustomAttributes(
                        "ItemDefinition",
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDTM>(x => x.ItemDefinitionSelector),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDTM>(x => x.Interpolators),
                        new CategoryAttribute(Properties.Resources.DataVisualization));

                    // move the ItemsSource to the Common Properties tab.
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDTM>(x => x.ItemsSource),
                        new CategoryAttribute(Properties.Resources.CommonProperties));
                });
        }
    }
}


<|start_filename|>sourceCode/Toolkit-release/DataVisualization/DependencyPropertyAnimationHelper.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows;
using System.Windows.Media.Animation;

namespace System.Windows.Controls.DataVisualization
{
    /// <summary>
    /// Represents a control that can animate the transitions between its specified
    /// dependency property.
    /// </summary>
    internal static class DependencyPropertyAnimationHelper
    {
        /// <summary>
        /// Number of key frames per second to generate the date time animations.
        /// </summary>
        public const int KeyFramesPerSecond = 20;

        /// <summary>
        /// The pattern used to ensure unique keys for the storyboards stored in
        /// a framework element's resource dictionary.
        /// </summary>
        private const string StoryboardKeyPattern = "__{0}__";

        /// <summary>
        /// Returns a unique key for a storyboard.
        /// </summary>
        /// <param name="propertyPath">The property path of the property that 
        /// the storyboard animates.</param>
        /// <returns>A unique key for a storyboard.</returns>
        private static string GetStoryboardKey(string propertyPath)
        {
            return string.Format(CultureInfo.InvariantCulture, StoryboardKeyPattern, propertyPath);
        }

        /// <summary>
        /// Starts animating a dependency property of a framework element to a 
        /// target value.
        /// </summary>
        /// <param name="target">The element to animate.</param>
        /// <param name="animatingDependencyProperty">The dependency property to
        /// animate.</param>
        /// <param name="propertyPath">The path of the dependency property to 
        /// animate.</param>
        /// <param name="targetValue">The value to animate the dependency
        /// property to.</param>
        /// <param name="timeSpan">The duration of the animation.</param>
        /// <param name="easingFunction">The easing function to uses to
        /// transition the data points.</param>
        public static void BeginAnimation(
            this FrameworkElement target,
            DependencyProperty animatingDependencyProperty,
            string propertyPath,
            object targetValue,
            TimeSpan timeSpan,
            IEasingFunction easingFunction)
        {
            Storyboard storyBoard = target.Resources[GetStoryboardKey(propertyPath)] as Storyboard;

            if (storyBoard != null)
            {
#if SILVERLIGHT
                // Save current value
                object currentValue = target.GetValue(animatingDependencyProperty);
#endif
                storyBoard.Stop();
#if SILVERLIGHT
                // Restore that value so it doesn't snap back to its starting value
                target.SetValue(animatingDependencyProperty, currentValue);
#endif
                target.Resources.Remove(GetStoryboardKey(propertyPath));
            }

            storyBoard = CreateStoryboard(target, animatingDependencyProperty, propertyPath, ref targetValue, timeSpan, easingFunction);

            storyBoard.Completed += 
                (source, args) =>
                    {
                        storyBoard.Stop();
                        target.SetValue(animatingDependencyProperty, targetValue);
                        target.Resources.Remove(GetStoryboardKey(propertyPath));
                    };

            target.Resources.Add(GetStoryboardKey(propertyPath), storyBoard);
            storyBoard.Begin();
        }

        /// <summary>
        /// Creates a story board that animates a dependency property to a 
        /// value.
        /// </summary>
        /// <param name="target">The element that is the target of the 
        /// storyboard.</param>
        /// <param name="animatingDependencyProperty">The dependency property
        /// to animate.</param>
        /// <param name="propertyPath">The property path of the dependency
        /// property to animate.</param>
        /// <param name="toValue">The value to animate the dependency property
        /// to.</param>
        /// <param name="durationTimeSpan">The duration of the animation.
        /// </param>
        /// <param name="easingFunction">The easing function to use to
        /// transition the data points.</param>
        /// <returns>The story board that animates the property.</returns>
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "easingFunction", Justification = "This parameter is used in Silverlight.")]
        private static Storyboard CreateStoryboard(
            FrameworkElement target,
            DependencyProperty animatingDependencyProperty,
            string propertyPath,
            ref object toValue,
            TimeSpan durationTimeSpan,
            IEasingFunction easingFunction)
        {
            object fromValue = target.GetValue(animatingDependencyProperty);

            double fromDoubleValue;
            double toDoubleValue;

            DateTime fromDateTime;
            DateTime toDateTime;

            Storyboard storyBoard = new Storyboard();
            Storyboard.SetTarget(storyBoard, target);
            Storyboard.SetTargetProperty(storyBoard, new PropertyPath(propertyPath));

            if ((fromValue != null && toValue != null))
            {
                if (ValueHelper.TryConvert(fromValue, out fromDoubleValue) && ValueHelper.TryConvert(toValue, out toDoubleValue))
                {
                    DoubleAnimation doubleAnimation = new DoubleAnimation();
#if !NO_EASING_FUNCTIONS
                    doubleAnimation.EasingFunction = easingFunction;
#endif
                    doubleAnimation.Duration = durationTimeSpan;
                    doubleAnimation.To = ValueHelper.ToDouble(toValue);
                    toValue = doubleAnimation.To;

                    storyBoard.Children.Add(doubleAnimation);
                }
                else if (ValueHelper.TryConvert(fromValue, out fromDateTime) && ValueHelper.TryConvert(toValue, out toDateTime))
                {
                    ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames();
                    keyFrameAnimation.Duration = durationTimeSpan;

                    long intervals = (long)(durationTimeSpan.TotalSeconds * KeyFramesPerSecond);
                    if (intervals < 2L)
                    {
                        intervals = 2L;
                    }

                    IEnumerable<TimeSpan> timeSpanIntervals =
                        ValueHelper.GetTimeSpanIntervalsInclusive(durationTimeSpan, intervals);

                    IEnumerable<DateTime> dateTimeIntervals =
                        ValueHelper.GetDateTimesBetweenInclusive(fromDateTime, toDateTime, intervals);

                    IEnumerable<DiscreteObjectKeyFrame> keyFrames =
                        EnumerableFunctions.Zip(
                            dateTimeIntervals,
                            timeSpanIntervals,
                            (dateTime, timeSpan) => new DiscreteObjectKeyFrame() { Value = dateTime, KeyTime = timeSpan });

                    foreach (DiscreteObjectKeyFrame keyFrame in keyFrames)
                    {
                        keyFrameAnimation.KeyFrames.Add(keyFrame);
                        toValue = keyFrame.Value;
                    }

                    storyBoard.Children.Add(keyFrameAnimation);
                }
            }

            if (storyBoard.Children.Count == 0)
            {
                ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames();
                DiscreteObjectKeyFrame endFrame = new DiscreteObjectKeyFrame() { Value = toValue, KeyTime = new TimeSpan(0, 0, 0) };
                keyFrameAnimation.KeyFrames.Add(endFrame);

                storyBoard.Children.Add(keyFrameAnimation);
            }

           return storyBoard;
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization.Design/ChartMetadata.cs<|end_filename|>
﻿// (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.

extern alias Silverlight;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Controls.Design.Common;
using Microsoft.Windows.Design;
using Microsoft.Windows.Design.Features;
using Microsoft.Windows.Design.Metadata;
using Microsoft.Windows.Design.PropertyEditing;
using SSWCDC = Silverlight::System.Windows.Controls.DataVisualization.Charting;

namespace System.Windows.Controls.DataVisualization.Design
{
    /// <summary>
    /// To register design time metadata for SSWCDC.Chart.
    /// </summary>
    internal class ChartMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time metadata for SSWCDC.Chart.
        /// </summary>
        [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "By design.")]
        public ChartMetadata()
            : base()
        {
            AddCallback(
                typeof(SSWCDC.Chart),
                b =>
                {
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.ActualAxes),
                        new BrowsableAttribute(false));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendItems),
                        new BrowsableAttribute(false));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Title),
                        new CategoryAttribute(Properties.Resources.CommonProperties));

                    // Note: Not using GetMemberName here because that pulls in INotifyPropertyChanged 
                    // from Silverlight's System which conflicts with the desktop's System
                    b.AddCustomAttributes(
                        "Series",
                        new CategoryAttribute(Properties.Resources.CommonProperties));
                    b.AddCustomAttributes(
                        "Axes",
                        new CategoryAttribute(Properties.Resources.CommonProperties));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendItems),
                        new CategoryAttribute(Properties.Resources.DataVisualization));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendTitle),
                        new CategoryAttribute(Properties.Resources.DataVisualization));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.ChartAreaStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.PlotAreaStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Palette),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.TitleStyle),
                        new CategoryAttribute(Properties.Resources.DataVisualizationStyling));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendTitle),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Title),
                        PropertyValueEditor.CreateEditorAttribute(typeof(TextBoxEditor)));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Axes),
                        new NewItemTypesAttribute(typeof(SSWCDC.CategoryAxis)),
                        new NewItemTypesAttribute(typeof(SSWCDC.LinearAxis)),
                        new NewItemTypesAttribute(typeof(SSWCDC.DateTimeAxis)));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Series),
                        new NewItemTypesAttribute(typeof(SSWCDC.AreaSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.BubbleSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.BarSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.LineSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.PieSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.ScatterSeries)),
                        new NewItemTypesAttribute(typeof(SSWCDC.ColumnSeries)));

#if MWD40
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.LegendTitle),
                        new AlternateContentPropertyAttribute());
                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.Title),
                        new AlternateContentPropertyAttribute());

                    b.AddCustomAttributes(
                        "Series",
                        new AlternateContentPropertyAttribute());
                    b.AddCustomAttributes(
                        "Axes",
                        new AlternateContentPropertyAttribute());

                    b.AddCustomAttributes(new FeatureAttribute(typeof(ChartDefaultInitializer)));

                    b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.DataVisualization, true));

                    b.AddCustomAttributes(
                        Extensions.GetMemberName<SSWCDC.Chart>(x => x.TitleStyle),
                        new DataContextValueSourceAttribute(
                            Extensions.GetMemberName<SSWCDC.Chart>(x => x.Title),
                            false));
#endif
                });
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Charting/Axis/DateTimeAxis.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using EF = System.Windows.Controls.DataVisualization.EnumerableFunctions;

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// An axis that displays numeric values.
    /// </summary>
    [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(DateTimeAxisLabel))]
    [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))]
    [TemplatePart(Name = AxisGridName, Type = typeof(Grid))]
    [TemplatePart(Name = AxisTitleName, Type = typeof(Title))]
    public class DateTimeAxis : RangeAxis
    {
        #region public DateTime? ActualMaximum
        /// <summary>
        /// Gets the actual maximum value plotted on the chart.
        /// </summary>
        public DateTime? ActualMaximum
        {
            get { return (DateTime?)GetValue(ActualMaximumProperty); }
            private set { SetValue(ActualMaximumProperty, value); }
        }

        /// <summary>
        /// Identifies the ActualMaximum dependency property.
        /// </summary>
        public static readonly DependencyProperty ActualMaximumProperty =
            DependencyProperty.Register(
                "ActualMaximum",
                typeof(DateTime?),
                typeof(DateTimeAxis),
                null);
        #endregion public DateTime? ActualMaximum

        #region public DateTime? ActualMinimum
        /// <summary>
        /// Gets the actual maximum value plotted on the chart.
        /// </summary>
        public DateTime? ActualMinimum
        {
            get { return (DateTime?)GetValue(ActualMinimumProperty); }
            private set { SetValue(ActualMinimumProperty, value); }
        }

        /// <summary>
        /// Identifies the ActualMinimum dependency property.
        /// </summary>
        public static readonly DependencyProperty ActualMinimumProperty =
            DependencyProperty.Register(
                "ActualMinimum",
                typeof(DateTime?),
                typeof(DateTimeAxis),
                null);
        #endregion public DateTime? ActualMinimum

        #region public DateTime? Maximum
        /// <summary>
        /// Gets or sets the maximum value plotted on the axis.
        /// </summary>
        [TypeConverter(typeof(NullableConverter<DateTime>))]
        public DateTime? Maximum
        {
            get { return (DateTime?)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }

        /// <summary>
        /// Identifies the Maximum dependency property.
        /// </summary>
        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register(
                "Maximum",
                typeof(DateTime?),
                typeof(DateTimeAxis),
                new PropertyMetadata(null, OnMaximumPropertyChanged));

        /// <summary>
        /// MaximumProperty property changed handler.
        /// </summary>
        /// <param name="d">DateTimeAxis2 that changed its Maximum.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimeAxis source = (DateTimeAxis)d;
            DateTime? newValue = (DateTime?)e.NewValue;
            source.OnMaximumPropertyChanged(newValue);
        }

        /// <summary>
        /// MaximumProperty property changed handler.
        /// </summary>
        /// <param name="newValue">New value.</param>
        private void OnMaximumPropertyChanged(DateTime? newValue)
        {
            this.ProtectedMaximum = newValue;
        }
        #endregion public DateTime? Maximum

        #region public DateTime? Minimum
        /// <summary>
        /// Gets or sets the minimum value to plot on the axis.
        /// </summary>
        [TypeConverter(typeof(NullableConverter<DateTime>))]
        public DateTime? Minimum
        {
            get { return (DateTime?)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }

        /// <summary>
        /// Identifies the Minimum dependency property.
        /// </summary>
        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register(
                "Minimum",
                typeof(DateTime?),
                typeof(DateTimeAxis),
                new PropertyMetadata(null, OnMinimumPropertyChanged));

        /// <summary>
        /// MinimumProperty property changed handler.
        /// </summary>
        /// <param name="d">DateTimeAxis2 that changed its Minimum.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimeAxis source = (DateTimeAxis)d;
            DateTime? newValue = (DateTime?)e.NewValue;
            source.OnMinimumPropertyChanged(newValue);
        }

        /// <summary>
        /// MinimumProperty property changed handler.
        /// </summary>
        /// <param name="newValue">New value.</param>
        private void OnMinimumPropertyChanged(DateTime? newValue)
        {
            this.ProtectedMinimum = newValue;
        }
        #endregion public DateTime? Minimum

        #region public double? Interval
        /// <summary>
        /// Gets or sets the axis interval.
        /// </summary>
        [TypeConverter(typeof(NullableConverter<double>))]
        public double? Interval
        {
            get { return (double?)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        /// <summary>
        /// Identifies the Interval dependency property.
        /// </summary>
        public static readonly DependencyProperty IntervalProperty =
            DependencyProperty.Register(
                "Interval",
                typeof(double?),
                typeof(DateTimeAxis),
                new PropertyMetadata(null, OnIntervalPropertyChanged));

        /// <summary>
        /// IntervalProperty property changed handler.
        /// </summary>
        /// <param name="d">DateTimeAxis2 that changed its Interval.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimeAxis source = (DateTimeAxis)d;
            source.OnIntervalPropertyChanged();
        }

        /// <summary>
        /// IntervalProperty property changed handler.
        /// </summary>
        private void OnIntervalPropertyChanged()
        {
            Invalidate();
        }
        #endregion public double? Interval

        #region public double ActualInterval
        /// <summary>
        /// Gets the actual interval.
        /// </summary>
        public double ActualInterval
        {
            get { return (double)GetValue(ActualIntervalProperty); }
            private set { SetValue(ActualIntervalProperty, value); }
        }

        /// <summary>
        /// Identifies the ActualInterval dependency property.
        /// </summary>
        public static readonly DependencyProperty ActualIntervalProperty =
            DependencyProperty.Register(
                "ActualInterval",
                typeof(double),
                typeof(DateTimeAxis),
                new PropertyMetadata(double.NaN));

        #endregion public double ActualInterval

        #region public DateTimeIntervalType IntervalType
        /// <summary>
        /// Gets or sets the interval to use for the axis.
        /// </summary>
        public DateTimeIntervalType IntervalType
        {
            get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); }
            set { SetValue(IntervalTypeProperty, value); }
        }

        /// <summary>
        /// Identifies the InternalIntervalType dependency property.
        /// </summary>
        public static readonly DependencyProperty IntervalTypeProperty =
            DependencyProperty.Register(
                "IntervalType",
                typeof(DateTimeIntervalType),
                typeof(DateTimeAxis),
                new PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged));

        /// <summary>
        /// IntervalTypeProperty property changed handler.
        /// </summary>
        /// <param name="d">DateTimeAxis that changed its InternalIntervalType.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnIntervalTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DateTimeAxis source = (DateTimeAxis)d;
            DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue;
            source.OnIntervalTypePropertyChanged(newValue);
        }

        /// <summary>
        /// IntervalTypeProperty property changed handler.
        /// </summary>
        /// <param name="newValue">New value.</param>
        private void OnIntervalTypePropertyChanged(DateTimeIntervalType newValue)
        {
            this.ActualIntervalType = newValue;
            Invalidate();
        }
        #endregion public DateTimeIntervalType IntervalType

        #region public DateTimeIntervalType ActualIntervalType
        /// <summary>
        /// Gets or sets the actual interval type.
        /// </summary>
        private DateTimeIntervalType ActualIntervalType
        {
            get { return (DateTimeIntervalType)GetValue(ActualIntervalTypeProperty); }
            set { SetValue(ActualIntervalTypeProperty, value); }
        }

        /// <summary>
        /// Identifies the ActualIntervalType dependency property.
        /// </summary>
        private static readonly DependencyProperty ActualIntervalTypeProperty =
            DependencyProperty.Register(
                "ActualIntervalType",
                typeof(DateTimeIntervalType),
                typeof(DateTimeAxis),
                new PropertyMetadata(DateTimeIntervalType.Auto));

        #endregion public DateTimeIntervalType ActualIntervalType

        /// <summary>
        /// Gets the origin value on the axis.
        /// </summary>
        protected override IComparable Origin
        {
            get { return null; }
        }

        /// <summary>
        /// Instantiates a new instance of the DateTimeAxis2 class.
        /// </summary>
        public DateTimeAxis()
        {
            int year = DateTime.Now.Year;
            this.ActualRange = new Range<IComparable>(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
        }

        /// <summary>
        /// Converts a range to a range of type DateTime.
        /// </summary>
        /// <param name="range">A range to be converted.</param>
        /// <returns>A range that has been converted.</returns>
        private static Range<DateTime> ToDateTimeRange(Range<IComparable> range)
        {
            if (!range.HasData)
            {
                return new Range<DateTime>();
            }
            else
            {
                return new Range<DateTime>(ValueHelper.ToDateTime(range.Minimum), ValueHelper.ToDateTime(range.Maximum));
            }
        }

        /// <summary>
        /// Creates a new instance of the DateTimeAxisLabel class.
        /// </summary>
        /// <returns>Returns  a new instance of the DateTimeAxisLabel class.
        /// </returns>
        protected override Control CreateAxisLabel()
        {
            return new DateTimeAxisLabel();
        }

        /// <summary>
        /// Prepares an instance of the DateTimeAxisLabel class by setting its
        /// IntervalType property.
        /// </summary>
        /// <param name="label">An instance of the DateTimeAxisLabel class.
        /// </param>
        /// <param name="dataContext">The data context to assign to the label.
        /// </param>
        protected override void PrepareAxisLabel(Control label, object dataContext)
        {
            DateTimeAxisLabel dateTimeAxisLabel = label as DateTimeAxisLabel;

            if (dateTimeAxisLabel != null)
            {
                dateTimeAxisLabel.IntervalType = ActualIntervalType;
            }
            base.PrepareAxisLabel(label, dataContext);
        }

        /// <summary>
        /// Updates the typed actual maximum and minimum properties when the
        /// actual range changes.
        /// </summary>
        /// <param name="range">The actual range.</param>
        protected override void OnActualRangeChanged(Range<IComparable> range)
        {
            if (range.HasData)
            {
                this.ActualMaximum = (DateTime)range.Maximum;
                this.ActualMinimum = (DateTime)range.Minimum;
            }
            else
            {
                this.ActualMaximum = null;
                this.ActualMinimum = null;
            }

            base.OnActualRangeChanged(range);
        }

        /// <summary>
        /// Returns a value indicating whether a value can plot.
        /// </summary>
        /// <param name="value">The value to plot.</param>
        /// <returns>A value indicating whether a value can plot.</returns>
        public override bool CanPlot(object value)
        {
            DateTime val;
            return ValueHelper.TryConvert(value, out val);
        }

        /// <summary>
        /// Returns the plot area coordinate of a value.
        /// </summary>
        /// <param name="value">The value to plot.</param>
        /// <param name="currentRange">The range to use determine the coordinate.</param>
        /// <param name="length">The length of the axis.</param>
        /// <returns>The plot area coordinate of a value.</returns>
        protected override UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (currentRange.HasData)
            {
                DateTime dateTimeValue = ValueHelper.ToDateTime(value);

                Range<DateTime> actualDateTimeRange = ToDateTimeRange(currentRange);

                double rangelength = actualDateTimeRange.Maximum.ToOADate() - actualDateTimeRange.Minimum.ToOADate();
                double pixelLength = Math.Max(length - 1, 0);

                return new UnitValue((dateTimeValue.ToOADate() - actualDateTimeRange.Minimum.ToOADate()) * (pixelLength / rangelength), Unit.Pixels);
            }

            return UnitValue.NaN();
        }

        /// <summary>
        /// Returns the actual interval to use to determine which values are 
        /// displayed in the axis.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>The actual interval to use to determine which values are 
        /// displayed in the axis.
        /// </returns>
        private double CalculateActualInterval(Size availableSize)
        {
            if (Interval != null)
            {
                return Interval.Value;
            }

            Range<DateTime> actualDateTimeRange = ToDateTimeRange(this.ActualRange);

            DateTimeIntervalType intervalType;
            double interval = CalculateDateTimeInterval(actualDateTimeRange.Minimum, actualDateTimeRange.Maximum, out intervalType, availableSize);
            ActualIntervalType = intervalType;
            return interval;
        }

        /// <summary>
        /// Returns a sequence of major values.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of major values.</returns>
        protected virtual IEnumerable<DateTime> GetMajorAxisValues(Size availableSize)
        {
            if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0)
            {
                yield break;
            }

            this.ActualInterval = CalculateActualInterval(availableSize);
            Range<DateTime> dateTimeRange = ToDateTimeRange(ActualRange);
            DateTime date = dateTimeRange.Minimum;

            DateTime start = AlignIntervalStart(dateTimeRange.Minimum, this.ActualInterval, ActualIntervalType);
            while (start < dateTimeRange.Minimum)
            {
                start = IncrementDateTime(start, this.ActualInterval);
            }

            IEnumerable<DateTime> intermediateDates =
                EnumerableFunctions
                    .Iterate(start, next => IncrementDateTime(next, this.ActualInterval))
                    .TakeWhile(current => dateTimeRange.Contains(current));

            foreach (DateTime current in intermediateDates)
            {
                yield return current;
            }
        }

        /// <summary>
        /// Returns a sequence of values to create major tick marks for.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of values to create major tick marks for.
        /// </returns>
        protected override IEnumerable<IComparable> GetMajorTickMarkValues(Size availableSize)
        {
            return GetMajorAxisValues(availableSize).Cast<IComparable>();
        }

        /// <summary>
        /// Returns a sequence of values to plot on the axis.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of values to plot on the axis.</returns>
        protected override IEnumerable<IComparable> GetLabelValues(Size availableSize)
        {
            return GetMajorAxisValues(availableSize).Cast<IComparable>();
        }

        /// <summary>
        /// This method accepts a date time and increments it.
        /// </summary>
        /// <param name="date">A date time.</param>
        /// <param name="interval">The interval used to increment the date time.
        /// </param>
        /// <returns>The new date time.</returns>
        private DateTime IncrementDateTime(DateTime date, double interval)
        {
            DateTimeIntervalType intervalType = this.ActualIntervalType;
            TimeSpan span = new TimeSpan(0);
            DateTime result;

            if (intervalType == DateTimeIntervalType.Days)
            {
                span = TimeSpan.FromDays(interval);
            }
            else if (intervalType == DateTimeIntervalType.Hours)
            {
                span = TimeSpan.FromHours(interval);
            }
            else if (intervalType == DateTimeIntervalType.Milliseconds)
            {
                span = TimeSpan.FromMilliseconds(interval);
            }
            else if (intervalType == DateTimeIntervalType.Seconds)
            {
                span = TimeSpan.FromSeconds(interval);
            }
            else if (intervalType == DateTimeIntervalType.Minutes)
            {
                span = TimeSpan.FromMinutes(interval);
            }
            else if (intervalType == DateTimeIntervalType.Weeks)
            {
                span = TimeSpan.FromDays(7.0 * interval);
            }
            else if (intervalType == DateTimeIntervalType.Months)
            {
                // Special case handling when current date point
                // to the last day of the month
                bool lastMonthDay = false;
                if (date.Day == DateTime.DaysInMonth(date.Year, date.Month))
                {
                    lastMonthDay = true;
                }

                // Add specified amount of months
                date = date.AddMonths((int)Math.Floor(interval));
                span = TimeSpan.FromDays(30.0 * (interval - Math.Floor(interval)));

                // Check if last month of the day was used
                if (lastMonthDay && span.Ticks == 0)
                {
                    // Make sure the last day of the month is selected
                    int daysInMobth = DateTime.DaysInMonth(date.Year, date.Month);
                    date = date.AddDays(daysInMobth - date.Day);
                }
            }
            else if (intervalType == DateTimeIntervalType.Years)
            {
                date = date.AddYears((int)Math.Floor(interval));
                span = TimeSpan.FromDays(365.0 * (interval - Math.Floor(interval)));
            }

            result = date.Add(span);

            return result;
        }

        /// <summary>
        /// Adjusts the beginning of the first interval depending on the type and size.
        /// </summary>
        /// <param name="start">Original start point.</param>
        /// <param name="intervalSize">Interval size.</param>
        /// <param name="type">Type of the interval (Month, Year, ...).</param>
        /// <returns>
        /// Adjusted interval start position.
        /// </returns>
        private static DateTime AlignIntervalStart(DateTime start, double intervalSize, DateTimeIntervalType type)
        {
            // Do not adjust start position for these interval type
            if (type == DateTimeIntervalType.Auto)
            {
                return start;
            }

            // Get the beginning of the interval depending on type
            DateTime newStartDate = start;

            // Adjust the months interval depending on size
            if (intervalSize > 0.0 && intervalSize != 1.0)
            {
                if (type == DateTimeIntervalType.Months && intervalSize <= 12.0 && intervalSize > 1)
                {
                    // Make sure that the beginning is aligned correctly for cases
                    // like quarters and half years
                    DateTime resultDate = newStartDate;
                    DateTime sizeAdjustedDate = new DateTime(newStartDate.Year, 1, 1, 0, 0, 0);
                    while (sizeAdjustedDate < newStartDate)
                    {
                        resultDate = sizeAdjustedDate;
                        sizeAdjustedDate = sizeAdjustedDate.AddMonths((int)intervalSize);
                    }

                    newStartDate = resultDate;
                    return newStartDate;
                }
            }

            // Check interval type
            switch (type)
            {
                case DateTimeIntervalType.Years:
                    int year = (int)((int)(newStartDate.Year / intervalSize) * intervalSize);
                    if (year <= 0)
                    {
                        year = 1;
                    }
                    newStartDate = new DateTime(year, 1, 1, 0, 0, 0);
                    break;

                case DateTimeIntervalType.Months:
                    int month = (int)((int)(newStartDate.Month / intervalSize) * intervalSize);
                    if (month <= 0)
                    {
                        month = 1;
                    }
                    newStartDate = new DateTime(newStartDate.Year, month, 1, 0, 0, 0);
                    break;

                case DateTimeIntervalType.Days:
                    int day = (int)((int)(newStartDate.Day / intervalSize) * intervalSize);
                    if (day <= 0)
                    {
                        day = 1;
                    }
                    newStartDate = new DateTime(newStartDate.Year, newStartDate.Month, day, 0, 0, 0);
                    break;

                case DateTimeIntervalType.Hours:
                    int hour = (int)((int)(newStartDate.Hour / intervalSize) * intervalSize);
                    newStartDate = new DateTime(
                        newStartDate.Year,
                        newStartDate.Month,
                        newStartDate.Day,
                        hour,
                        0,
                        0);
                    break;

                case DateTimeIntervalType.Minutes:
                    int minute = (int)((int)(newStartDate.Minute / intervalSize) * intervalSize);
                    newStartDate = new DateTime(
                        newStartDate.Year,
                        newStartDate.Month,
                        newStartDate.Day,
                        newStartDate.Hour,
                        minute,
                        0);
                    break;

                case DateTimeIntervalType.Seconds:
                    int second = (int)((int)(newStartDate.Second / intervalSize) * intervalSize);
                    newStartDate = new DateTime(
                        newStartDate.Year,
                        newStartDate.Month,
                        newStartDate.Day,
                        newStartDate.Hour,
                        newStartDate.Minute,
                        second,
                        0);
                    break;

                case DateTimeIntervalType.Milliseconds:
                    int milliseconds = (int)((int)(newStartDate.Millisecond / intervalSize) * intervalSize);
                    newStartDate = new DateTime(
                        newStartDate.Year,
                        newStartDate.Month,
                        newStartDate.Day,
                        newStartDate.Hour,
                        newStartDate.Minute,
                        newStartDate.Second,
                        milliseconds);
                    break;

                case DateTimeIntervalType.Weeks:

                    // Elements that have interval set to weeks should be aligned to the 
                    // nearest start of week no matter how many weeks is the interval.
                    newStartDate = new DateTime(
                        newStartDate.Year,
                        newStartDate.Month,
                        newStartDate.Day,
                        0,
                        0,
                        0);

                    newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek));
                    break;
            }

            return newStartDate;
        }

        /// <summary>
        /// Returns the value range given a plot area coordinate.
        /// </summary>
        /// <param name="value">The position.</param>
        /// <returns>A range of values at that plot area coordinate.</returns>
        protected override IComparable GetValueAtPosition(UnitValue value)
        {
            if (ActualRange.HasData && ActualLength != 0.0)
            {
                double coordinate = value.Value;
                if (value.Unit == Unit.Pixels)
                {
                    Range<DateTime> actualDateTimeRange = ToDateTimeRange(ActualRange);

                    double minimumAsDouble = actualDateTimeRange.Minimum.ToOADate();
                    double rangelength = actualDateTimeRange.Maximum.ToOADate() - minimumAsDouble;
                    DateTime output = DateTime.FromOADate((coordinate * (rangelength / ActualLength)) + minimumAsDouble);

                    return output;
                }
                else
                {
                    throw new NotImplementedException();
                }
            }

            return null;
        }

        /// <summary>
        /// Recalculates a DateTime interval obtained from maximum and minimum.
        /// </summary>
        /// <param name="minimum">The minimum.</param>
        /// <param name="maximum">The maximum.</param>
        /// <param name="type">Date time interval type.</param>
        /// <param name="availableSize">The available size.</param>
        /// <returns>Auto Interval.</returns>
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "The method should inspect all variations of time span (millisec to year) and contains long case. Otherwise is simple and readable.")]
        private double CalculateDateTimeInterval(DateTime minimum, DateTime maximum, out DateTimeIntervalType type, Size availableSize)
        {
            DateTime dateTimeMin = minimum;
            DateTime dateTimeMax = maximum;
            TimeSpan timeSpan = dateTimeMax.Subtract(dateTimeMin);

            // this algorithm is designed to return close to 10 intervals.
            // we need to align the time span for PrefferedNumberOfIntervals
            double maxIntervals = Orientation == AxisOrientation.X ? MaximumAxisIntervalsPer200Pixels * 0.8 : MaximumAxisIntervalsPer200Pixels;
            double rangeMultiplicator = GetLength(availableSize) / (200 * 10 / maxIntervals);
            timeSpan = new TimeSpan((long)((double)timeSpan.Ticks / rangeMultiplicator));

            // Minutes
            double inter = timeSpan.TotalMinutes;

            // For Range less than 60 seconds interval is 5 sec
            if (inter <= 1.0)
            {
                // Milli Seconds
                double milliSeconds = timeSpan.TotalMilliseconds;
                if (milliSeconds <= 10)
                {
                    type = DateTimeIntervalType.Milliseconds;
                    return 1;
                }
                if (milliSeconds <= 50)
                {
                    type = DateTimeIntervalType.Milliseconds;
                    return 4;
                }
                if (milliSeconds <= 200)
                {
                    type = DateTimeIntervalType.Milliseconds;
                    return 20;
                }
                if (milliSeconds <= 500)
                {
                    type = DateTimeIntervalType.Milliseconds;
                    return 50;
                }

                // Seconds
                double seconds = timeSpan.TotalSeconds;

                if (seconds <= 7)
                {
                    type = DateTimeIntervalType.Seconds;
                    return 1;
                }
                else if (seconds <= 15)
                {
                    type = DateTimeIntervalType.Seconds;
                    return 2;
                }
                else if (seconds <= 30)
                {
                    type = DateTimeIntervalType.Seconds;
                    return 5;
                }
                else if (seconds <= 60)
                {
                    type = DateTimeIntervalType.Seconds;
                    return 10;
                }
            }
            else if (inter <= 2.0)
            {
                // For Range less than 120 seconds interval is 10 sec
                type = DateTimeIntervalType.Seconds;
                return 20;
            }
            else if (inter <= 3.0)
            {
                // For Range less than 180 seconds interval is 30 sec
                type = DateTimeIntervalType.Seconds;
                return 30;
            }
            else if (inter <= 10)
            {
                // For Range less than 10 minutes interval is 1 min
                type = DateTimeIntervalType.Minutes;
                return 1;
            }
            else if (inter <= 20)
            {
                // For Range less than 20 minutes interval is 1 min
                type = DateTimeIntervalType.Minutes;
                return 2;
            }
            else if (inter <= 60)
            {
                // For Range less than 60 minutes interval is 5 min
                type = DateTimeIntervalType.Minutes;
                return 5;
            }
            else if (inter <= 120)
            {
                // For Range less than 120 minutes interval is 10 min
                type = DateTimeIntervalType.Minutes;
                return 10;
            }
            else if (inter <= 180)
            {
                // For Range less than 180 minutes interval is 30 min
                type = DateTimeIntervalType.Minutes;
                return 30;
            }
            else if (inter <= 60 * 12)
            {
                // For Range less than 12 hours interval is 1 hour
                type = DateTimeIntervalType.Hours;
                return 1;
            }
            else if (inter <= 60 * 24)
            {
                // For Range less than 24 hours interval is 4 hour
                type = DateTimeIntervalType.Hours;
                return 4;
            }
            else if (inter <= 60 * 24 * 2)
            {
                // For Range less than 2 days interval is 6 hour
                type = DateTimeIntervalType.Hours;
                return 6;
            }
            else if (inter <= 60 * 24 * 3)
            {
                // For Range less than 3 days interval is 12 hour
                type = DateTimeIntervalType.Hours;
                return 12;
            }
            else if (inter <= 60 * 24 * 10)
            {
                // For Range less than 10 days interval is 1 day
                type = DateTimeIntervalType.Days;
                return 1;
            }
            else if (inter <= 60 * 24 * 20)
            {
                // For Range less than 20 days interval is 2 day
                type = DateTimeIntervalType.Days;
                return 2;
            }
            else if (inter <= 60 * 24 * 30)
            {
                // For Range less than 30 days interval is 3 day
                type = DateTimeIntervalType.Days;
                return 3;
            }
            else if (inter <= 60 * 24 * 30.5 * 2)
            {
                // For Range less than 2 months interval is 1 week
                type = DateTimeIntervalType.Weeks;
                return 1;
            }
            else if (inter <= 60 * 24 * 30.5 * 5)
            {
                // For Range less than 5 months interval is 2weeks
                type = DateTimeIntervalType.Weeks;
                return 2;
            }
            else if (inter <= 60 * 24 * 30.5 * 12)
            {
                // For Range less than 12 months interval is 1 month
                type = DateTimeIntervalType.Months;
                return 1;
            }
            else if (inter <= 60 * 24 * 30.5 * 24)
            {
                // For Range less than 24 months interval is 3 month
                type = DateTimeIntervalType.Months;
                return 3;
            }
            else if (inter <= 60 * 24 * 30.5 * 48)
            {
                // For Range less than 48 months interval is 6 months 
                type = DateTimeIntervalType.Months;
                return 6;
            }

            // For Range more than 48 months interval is year 
            type = DateTimeIntervalType.Years;
            double years = inter / 60 / 24 / 365;
            if (years < 5)
            {
                return 1;
            }
            else if (years < 10)
            {
                return 2;
            }

            // Make a correction of the interval
            return Math.Floor(years / 5);
        }

        /// <summary>
        /// Overrides the actual range to ensure that it is never set to an
        /// empty range.
        /// </summary>
        /// <param name="range">The range to override.</param>
        /// <returns>The overridden range.</returns>
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This method is very difficult to break up cleanly.")]
        protected override Range<IComparable> OverrideDataRange(Range<IComparable> range)
        {
            Range<IComparable> overriddenActualRange = range;

            if (!overriddenActualRange.HasData)
            {
                int year = DateTime.Now.Year;
                return new Range<IComparable>(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
            }

            // ActualLength of 1.0 or less maps all points to the same coordinate
            if (range.HasData && this.ActualLength > 1.0)
            {
                IList<ValueMarginCoordinateAndOverlap> valueMargins = new List<ValueMarginCoordinateAndOverlap>();
                foreach (ValueMargin valueMargin in
                    this.RegisteredListeners
                        .OfType<IValueMarginProvider>()
                        .SelectMany(provider => provider.GetValueMargins(this)))
                {
                    valueMargins.Add(
                        new ValueMarginCoordinateAndOverlap
                        {
                            ValueMargin = valueMargin,
                        });
                }

                if (valueMargins.Count > 0)
                {
                    double maximumPixelMarginLength =
                        valueMargins
                        .Select(valueMargin => valueMargin.ValueMargin.LowMargin + valueMargin.ValueMargin.HighMargin)
                        .MaxOrNullable().Value;

                    // Requested margin is larger than the axis so give up
                    // trying to find a range that will fit it.
                    if (maximumPixelMarginLength > this.ActualLength)
                    {
                        return range;
                    }

                    Range<DateTime> currentRange = range.ToDateTimeRange();

                    // Ensure range is not empty.
                    if (currentRange.Minimum == currentRange.Maximum)
                    {
                        int year = DateTime.Now.Year;
                        currentRange = new Range<DateTime>(new DateTime(year, 1, 1), new DateTime(year + 1, 1, 1));
                    }

                    // priming the loop
                    double actualLength = this.ActualLength;
                    ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin;
                    ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin;
                    UpdateValueMargins(valueMargins, currentRange.ToComparableRange());
                    GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin);

                    while (maxLeftOverlapValueMargin.LeftOverlap > 0 || maxRightOverlapValueMargin.RightOverlap > 0)
                    {
                        long unitOverPixels = currentRange.GetLength().Value.Ticks / ((long) actualLength);
                        DateTime newMinimum = new DateTime(currentRange.Minimum.Ticks - (long)((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels));
                        DateTime newMaximum = new DateTime(currentRange.Maximum.Ticks + (long)((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels));

                        currentRange = new Range<DateTime>(newMinimum, newMaximum);
                        UpdateValueMargins(valueMargins, currentRange.ToComparableRange());
                        GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin);
                    }

                    return currentRange.ToComparableRange();
                }
            }

            return range;
        }
    }
}

<|start_filename|>sourceCode/Toolkit-release/DataVisualization/Collections/OrderedMultipleDictionary.cs<|end_filename|>
﻿// (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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace System.Windows.Controls.DataVisualization.Collections
{
    /// <summary>
    /// Implements a dictionary that can store multiple values for the same key and sorts the values.
    /// </summary>
    /// <typeparam name="TKey">Type for keys.</typeparam>
    /// <typeparam name="TValue">Type for values.</typeparam>
    internal class OrderedMultipleDictionary<TKey, TValue> : MultipleDictionary<TKey, TValue>, IEnumerable<TValue>
        where TKey : IComparable
    {
        /// <summary>
        /// Initializes a new instance of the MultipleDictionary class.
        /// </summary>
        /// <param name="allowDuplicateValues">The parameter is not used.</param>
        /// <param name="keyComparison">Key comparison class.</param>
        /// <param name="valueComparison">Value comparison class.</param>
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "allowDuplicateValues", Justification = "Unused parameter exists for API compatibility.")]
        public OrderedMultipleDictionary(bool allowDuplicateValues, Comparison<TKey> keyComparison, Comparison<TValue> valueComparison)
        {
            Debug.Assert(null != keyComparison, "keyComparison must not be null.");
            Debug.Assert(null != valueComparison, "valueComparison must not be null.");
            BinaryTree = new LeftLeaningRedBlackTree<TKey, TValue>(keyComparison, valueComparison);
        }

        /// <summary>
        /// Gets a Range corresponding to the keys in the dictionary.
        /// </summary>
        /// <returns>Range of keys.</returns>
        public Range<TKey> GetKeyRange()
        {
            if (0 < BinaryTree.Count)
            {
                return new Range<TKey>(BinaryTree.MinimumKey, BinaryTree.MaximumKey);
            }
            else
            {
                return new Range<TKey>();
            }
        }

        /// <summary>
        /// Gets the largest and smallest key's extreme values from the dictionary.
        /// </summary>
        /// <returns>Tuple of the largest and smallest values.</returns>
        public Tuple<TValue, TValue> GetLargestAndSmallestValues()
        {
            if (0 < BinaryTree.Count)
            {
                return new Tuple<TValue, TValue>(BinaryTree.MinimumValue, BinaryTree.MaximumValue);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Gets an enumerator for the values in the dictionary.
        /// </summary>
        /// <returns>Enumerator for values.</returns>
        public IEnumerator<TValue> GetEnumerator()
        {
            return BinaryTree.GetValuesForAllKeys().GetEnumerator();
        }

        /// <summary>
        /// Gets an enumerator for the values in the dictionary.
        /// </summary>
        /// <returns>Enumerator for the values.</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return BinaryTree.GetValuesForAllKeys().GetEnumerator();
        }
    }
}