141 changed files with 118 additions and 28739 deletions
@ -1,175 +0,0 @@ |
|||||
// (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.Diagnostics; |
|
||||
using System.Linq; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Aggregated observable collection.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the items in the observable collections.
|
|
||||
/// </typeparam>
|
|
||||
internal class AggregatedObservableCollection<T> : ReadOnlyObservableCollection<T> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of an aggregated observable collection.
|
|
||||
/// </summary>
|
|
||||
public AggregatedObservableCollection() |
|
||||
{ |
|
||||
this.ChildCollections = new NoResetObservableCollection<IList>(); |
|
||||
this.ChildCollections.CollectionChanged += new NotifyCollectionChangedEventHandler(ChildCollectionsCollectionChanged); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rebuilds the list if a collection changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void ChildCollectionsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
Debug.Assert(e.Action != NotifyCollectionChangedAction.Reset, "Reset is not supported."); |
|
||||
|
|
||||
if (e.Action == NotifyCollectionChangedAction.Add) |
|
||||
{ |
|
||||
e.NewItems |
|
||||
.OfType<IList>() |
|
||||
.ForEachWithIndex((newCollection, index) => |
|
||||
{ |
|
||||
int startingIndex = GetStartingIndexOfCollectionAtIndex(e.NewStartingIndex + index); |
|
||||
foreach (T item in newCollection.OfType<T>().Reverse()) |
|
||||
{ |
|
||||
this.Mutate(items => items.Insert(startingIndex, item)); |
|
||||
} |
|
||||
|
|
||||
INotifyCollectionChanged notifyCollectionChanged = newCollection as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged += ChildCollectionCollectionChanged; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove) |
|
||||
{ |
|
||||
foreach (IList oldCollection in e.OldItems) |
|
||||
{ |
|
||||
INotifyCollectionChanged notifyCollectionChanged = oldCollection as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged -= ChildCollectionCollectionChanged; |
|
||||
} |
|
||||
|
|
||||
foreach (T item in oldCollection) |
|
||||
{ |
|
||||
this.Mutate(items => items.Remove(item)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else if (e.Action == NotifyCollectionChangedAction.Replace) |
|
||||
{ |
|
||||
foreach (IList oldCollection in e.OldItems) |
|
||||
{ |
|
||||
INotifyCollectionChanged notifyCollectionChanged = oldCollection as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged -= ChildCollectionCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
foreach (IList newCollection in e.NewItems) |
|
||||
{ |
|
||||
INotifyCollectionChanged notifyCollectionChanged = newCollection as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged += ChildCollectionCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Rebuild(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Synchronizes the collection with changes made in a child collection.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void ChildCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
Debug.Assert(e.Action != NotifyCollectionChangedAction.Reset, "Reset is not supported."); |
|
||||
IList collectionSender = sender as IList; |
|
||||
|
|
||||
if (e.Action == NotifyCollectionChangedAction.Add) |
|
||||
{ |
|
||||
int startingIndex = GetStartingIndexOfCollectionAtIndex(ChildCollections.IndexOf(collectionSender)); |
|
||||
e.NewItems |
|
||||
.OfType<T>() |
|
||||
.ForEachWithIndex((item, index) => |
|
||||
{ |
|
||||
this.Mutate(that => that.Insert(startingIndex + e.NewStartingIndex + index, item)); |
|
||||
}); |
|
||||
} |
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove) |
|
||||
{ |
|
||||
foreach (T item in e.OldItems.OfType<T>()) |
|
||||
{ |
|
||||
this.Mutate(that => that.Remove(item)); |
|
||||
} |
|
||||
} |
|
||||
else if (e.Action == NotifyCollectionChangedAction.Replace) |
|
||||
{ |
|
||||
for (int cnt = 0; cnt < e.NewItems.Count; cnt++) |
|
||||
{ |
|
||||
T oldItem = (T)e.OldItems[cnt]; |
|
||||
T newItem = (T)e.NewItems[cnt]; |
|
||||
int oldItemIndex = this.IndexOf(oldItem); |
|
||||
this.Mutate((that) => |
|
||||
{ |
|
||||
that[oldItemIndex] = newItem; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the starting index of a collection in the aggregate
|
|
||||
/// collection.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The starting index of a collection.</param>
|
|
||||
/// <returns>The starting index of the collection in the aggregate
|
|
||||
/// collection.</returns>
|
|
||||
private int GetStartingIndexOfCollectionAtIndex(int index) |
|
||||
{ |
|
||||
return ChildCollections.OfType<IEnumerable>().Select(collection => collection.CastWrapper<T>()).Take(index).SelectMany(collection => collection).Count(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rebuild the list in the correct order when a child collection
|
|
||||
/// changes.
|
|
||||
/// </summary>
|
|
||||
private void Rebuild() |
|
||||
{ |
|
||||
this.Mutate(that => that.Clear()); |
|
||||
this.Mutate(that => |
|
||||
{ |
|
||||
IList<T> items = ChildCollections.OfType<IEnumerable>().Select(collection => collection.CastWrapper<T>()).SelectMany(collection => collection).ToList(); |
|
||||
foreach (T item in items) |
|
||||
{ |
|
||||
that.Add(item); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets child collections of the aggregated collection.
|
|
||||
/// </summary>
|
|
||||
public ObservableCollection<IList> ChildCollections { get; private set; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Specifies the supported animation sequences.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public enum AnimationSequence |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Animates all of the data points simultaneously.
|
|
||||
/// </summary>
|
|
||||
Simultaneous = 0, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Animates the data points from first to last.
|
|
||||
/// </summary>
|
|
||||
FirstToLast = 1, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Animates the data points from last to first.
|
|
||||
/// </summary>
|
|
||||
LastToFirst = 2 |
|
||||
} |
|
||||
} |
|
||||
@ -1,237 +0,0 @@ |
|||||
// (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; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Collections.ObjectModel; |
|
||||
using System.Collections.Specialized; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis class used to determine the plot area coordinate of values.
|
|
||||
/// </summary>
|
|
||||
public abstract class Axis : Control, IAxis |
|
||||
{ |
|
||||
#region public AxisLocation Location
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the axis location.
|
|
||||
/// </summary>
|
|
||||
public AxisLocation Location |
|
||||
{ |
|
||||
get { return (AxisLocation)GetValue(LocationProperty); } |
|
||||
set { SetValue(LocationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Location dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LocationProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Location", |
|
||||
typeof(AxisLocation), |
|
||||
typeof(Axis), |
|
||||
new PropertyMetadata(AxisLocation.Auto, OnLocationPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// LocationProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Axis that changed its Location.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnLocationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
Axis source = (Axis)d; |
|
||||
AxisLocation oldValue = (AxisLocation)e.OldValue; |
|
||||
AxisLocation newValue = (AxisLocation)e.NewValue; |
|
||||
source.OnLocationPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// LocationProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnLocationPropertyChanged(AxisLocation oldValue, AxisLocation newValue) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<AxisLocation> handler = this.LocationChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<AxisLocation>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the location property is changed.
|
|
||||
/// </summary>
|
|
||||
public event RoutedPropertyChangedEventHandler<AxisLocation> LocationChanged; |
|
||||
|
|
||||
#endregion public AxisLocation Location
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the list of child axes belonging to this axis.
|
|
||||
/// </summary>
|
|
||||
public ObservableCollection<IAxis> DependentAxes { get; private set; } |
|
||||
|
|
||||
#region public AxisOrientation Orientation
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the orientation of the axis.
|
|
||||
/// </summary>
|
|
||||
public AxisOrientation Orientation |
|
||||
{ |
|
||||
get { return (AxisOrientation)GetValue(OrientationProperty); } |
|
||||
set { SetValue(OrientationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Orientation dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty OrientationProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Orientation", |
|
||||
typeof(AxisOrientation), |
|
||||
typeof(Axis), |
|
||||
new PropertyMetadata(AxisOrientation.None, OnOrientationPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// OrientationProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Axis that changed its Orientation.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
Axis source = (Axis)d; |
|
||||
AxisOrientation oldValue = (AxisOrientation)e.OldValue; |
|
||||
AxisOrientation newValue = (AxisOrientation)e.NewValue; |
|
||||
source.OnOrientationPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// OrientationProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnOrientationPropertyChanged(AxisOrientation oldValue, AxisOrientation newValue) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<AxisOrientation> handler = OrientationChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<AxisOrientation>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the Orientation property is changed.
|
|
||||
/// </summary>
|
|
||||
public event RoutedPropertyChangedEventHandler<AxisOrientation> OrientationChanged; |
|
||||
|
|
||||
#endregion public AxisOrientation Orientation
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Raises the invalidated event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
protected virtual void OnInvalidated(RoutedEventArgs args) |
|
||||
{ |
|
||||
foreach (IAxisListener listener in RegisteredListeners) |
|
||||
{ |
|
||||
listener.AxisInvalidated(this); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or the collection of series that are using the Axis.
|
|
||||
/// </summary>
|
|
||||
public ObservableCollection<IAxisListener> RegisteredListeners { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a value indicating whether the axis can plot a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to plot.</param>
|
|
||||
/// <returns>A value indicating whether the axis can plot a value.
|
|
||||
/// </returns>
|
|
||||
public abstract bool CanPlot(object value); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value for which to retrieve the plot area
|
|
||||
/// coordinate.</param>
|
|
||||
/// <returns>The plot area coordinate.</returns>
|
|
||||
public abstract UnitValue GetPlotAreaCoordinate(object value); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the Axis class.
|
|
||||
/// </summary>
|
|
||||
protected Axis() |
|
||||
{ |
|
||||
RegisteredListeners = new UniqueObservableCollection<IAxisListener>(); |
|
||||
this.RegisteredListeners.CollectionChanged += RegisteredListenersCollectionChanged; |
|
||||
this.DependentAxes = new ObservableCollection<IAxis>(); |
|
||||
this.DependentAxes.CollectionChanged += OnChildAxesCollectionChanged; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Child axes collection changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void OnChildAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
this.OnDependentAxesCollectionChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Child axes collection changed.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnDependentAxesCollectionChanged() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the registered listeners collection is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void RegisteredListenersCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (e.OldItems != null) |
|
||||
{ |
|
||||
foreach (IAxisListener obj in e.OldItems) |
|
||||
{ |
|
||||
OnObjectUnregistered(obj); |
|
||||
} |
|
||||
} |
|
||||
if (e.NewItems != null) |
|
||||
{ |
|
||||
foreach (IAxisListener obj in e.NewItems) |
|
||||
{ |
|
||||
OnObjectRegistered(obj); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is invoked when a series is registered.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series that has been registered.</param>
|
|
||||
protected virtual void OnObjectRegistered(IAxisListener series) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is invoked when a series is unregistered.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series that has been unregistered.</param>
|
|
||||
protected virtual void OnObjectUnregistered(IAxisListener series) |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,64 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Specifies an interval type.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
internal enum AxisIntervalType |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Automatically determined by the ISeriesHost control.
|
|
||||
/// </summary>
|
|
||||
Auto = 0, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is numerical.
|
|
||||
/// </summary>
|
|
||||
Number = 1, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is years.
|
|
||||
/// </summary>
|
|
||||
Years = 2, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is months.
|
|
||||
/// </summary>
|
|
||||
Months = 3, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is weeks.
|
|
||||
/// </summary>
|
|
||||
Weeks = 4, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is days.
|
|
||||
/// </summary>
|
|
||||
Days = 5, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is hours.
|
|
||||
/// </summary>
|
|
||||
Hours = 6, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is minutes.
|
|
||||
/// </summary>
|
|
||||
Minutes = 7, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is seconds.
|
|
||||
/// </summary>
|
|
||||
Seconds = 8, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The interval type is milliseconds.
|
|
||||
/// </summary>
|
|
||||
Milliseconds = 9, |
|
||||
} |
|
||||
} |
|
||||
@ -1,112 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
using System.Globalization; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Data; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A label used to display data in an axis.
|
|
||||
/// </summary>
|
|
||||
public class AxisLabel : Control |
|
||||
{ |
|
||||
#region public string StringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the text string format.
|
|
||||
/// </summary>
|
|
||||
public string StringFormat |
|
||||
{ |
|
||||
get { return GetValue(StringFormatProperty) as string; } |
|
||||
set { SetValue(StringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the StringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty StringFormatProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"StringFormat", |
|
||||
typeof(string), |
|
||||
typeof(AxisLabel), |
|
||||
new PropertyMetadata(null, OnStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// StringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">AxisLabel that changed its StringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
AxisLabel source = (AxisLabel)d; |
|
||||
string newValue = (string)e.NewValue; |
|
||||
source.OnStringFormatPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// StringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnStringFormatPropertyChanged(string newValue) |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string StringFormat
|
|
||||
|
|
||||
#region public string FormattedContent
|
|
||||
/// <summary>
|
|
||||
/// Gets the formatted content property.
|
|
||||
/// </summary>
|
|
||||
public string FormattedContent |
|
||||
{ |
|
||||
get { return GetValue(FormattedContentProperty) as string; } |
|
||||
protected set { SetValue(FormattedContentProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the FormattedContent dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty FormattedContentProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"FormattedContent", |
|
||||
typeof(string), |
|
||||
typeof(AxisLabel), |
|
||||
new PropertyMetadata(null)); |
|
||||
#endregion public string FormattedContent
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the AxisLabel class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static AxisLabel() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(AxisLabel), new FrameworkPropertyMetadata(typeof(AxisLabel))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the AxisLabel class.
|
|
||||
/// </summary>
|
|
||||
public AxisLabel() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(AxisLabel); |
|
||||
#endif
|
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = StringFormat ?? "{0}" }); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the formatted text.
|
|
||||
/// </summary>
|
|
||||
protected virtual void UpdateFormattedContent() |
|
||||
{ |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = StringFormat ?? "{0}" }); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Axis position.
|
|
||||
/// </summary>
|
|
||||
public enum AxisLocation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Location is determined automatically.
|
|
||||
/// </summary>
|
|
||||
Auto, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Left in the series host area.
|
|
||||
/// </summary>
|
|
||||
Left, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Top in the series host area.
|
|
||||
/// </summary>
|
|
||||
Top, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Right in the series host area.
|
|
||||
/// </summary>
|
|
||||
Right, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Bottom of the series host area.
|
|
||||
/// </summary>
|
|
||||
Bottom, |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Specifies the orientation of an axis.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public enum AxisOrientation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Orientation is automatically set.
|
|
||||
/// </summary>
|
|
||||
None, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Indicates the axis plots along the X axis.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X", Justification = "X is the expected terminology.")] |
|
||||
X, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Indicates the axis plots along the Y axis.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = "Y is the expected terminology.")] |
|
||||
Y, |
|
||||
} |
|
||||
} |
|
||||
@ -1,363 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis that displays categories.
|
|
||||
/// </summary>
|
|
||||
[StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))] |
|
||||
[StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))] |
|
||||
[StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(AxisLabel))] |
|
||||
[StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] |
|
||||
[TemplatePart(Name = AxisGridName, Type = typeof(Grid))] |
|
||||
[TemplatePart(Name = AxisTitleName, Type = typeof(Title))] |
|
||||
public class CategoryAxis : DisplayAxis, ICategoryAxis |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A pool of major tick marks.
|
|
||||
/// </summary>
|
|
||||
private ObjectPool<Line> _majorTickMarkPool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A pool of labels.
|
|
||||
/// </summary>
|
|
||||
private ObjectPool<Control> _labelPool; |
|
||||
|
|
||||
#region public CategorySortOrder SortOrder
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the sort order used for the categories.
|
|
||||
/// </summary>
|
|
||||
public CategorySortOrder SortOrder |
|
||||
{ |
|
||||
get { return (CategorySortOrder)GetValue(SortOrderProperty); } |
|
||||
set { SetValue(SortOrderProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the SortOrder dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty SortOrderProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"SortOrder", |
|
||||
typeof(CategorySortOrder), |
|
||||
typeof(CategoryAxis), |
|
||||
new PropertyMetadata(CategorySortOrder.None, OnSortOrderPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SortOrderProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">CategoryAxis that changed its SortOrder.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnSortOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
CategoryAxis source = (CategoryAxis)d; |
|
||||
source.OnSortOrderPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SortOrderProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
private void OnSortOrderPropertyChanged() |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
#endregion public CategorySortOrder SortOrder
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a list of categories to display.
|
|
||||
/// </summary>
|
|
||||
private IList<object> Categories { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the grid line coordinates to display.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] |
|
||||
private IList<UnitValue> GridLineCoordinatesToDisplay { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the CategoryAxis class.
|
|
||||
/// </summary>
|
|
||||
public CategoryAxis() |
|
||||
{ |
|
||||
this._labelPool = new ObjectPool<Control>(() => CreateAxisLabel()); |
|
||||
this._majorTickMarkPool = new ObjectPool<Line>(() => CreateMajorTickMark()); |
|
||||
this.Categories = new List<object>(); |
|
||||
this.GridLineCoordinatesToDisplay = new List<UnitValue>(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates categories when a series is registered.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series to be registered.</param>
|
|
||||
protected override void OnObjectRegistered(IAxisListener series) |
|
||||
{ |
|
||||
base.OnObjectRegistered(series); |
|
||||
if (series is IDataProvider) |
|
||||
{ |
|
||||
UpdateCategories(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates categories when a series is unregistered.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series to be unregistered.</param>
|
|
||||
protected override void OnObjectUnregistered(IAxisListener series) |
|
||||
{ |
|
||||
base.OnObjectUnregistered(series); |
|
||||
if (series is IDataProvider) |
|
||||
{ |
|
||||
UpdateCategories(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns range of coordinates for a given category.
|
|
||||
/// </summary>
|
|
||||
/// <param name="category">The category to return the range for.</param>
|
|
||||
/// <returns>The range of coordinates corresponding to the category.
|
|
||||
/// </returns>
|
|
||||
public Range<UnitValue> GetPlotAreaCoordinateRange(object category) |
|
||||
{ |
|
||||
if (category == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException("category"); |
|
||||
} |
|
||||
int index = Categories.IndexOf(category); |
|
||||
if (index == -1) |
|
||||
{ |
|
||||
return new Range<UnitValue>(); |
|
||||
} |
|
||||
|
|
||||
if (Orientation == AxisOrientation.X || Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
double maximumLength = Math.Max(ActualLength - 1, 0); |
|
||||
double lower = (index * maximumLength) / Categories.Count; |
|
||||
double upper = ((index + 1) * maximumLength) / Categories.Count; |
|
||||
|
|
||||
if (Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
return new Range<UnitValue>(new UnitValue(lower, Unit.Pixels), new UnitValue(upper, Unit.Pixels)); |
|
||||
} |
|
||||
else if (Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
return new Range<UnitValue>(new UnitValue(maximumLength - upper, Unit.Pixels), new UnitValue(maximumLength - lower, Unit.Pixels)); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
double startingAngle = 270.0; |
|
||||
double angleOffset = 360 / this.Categories.Count; |
|
||||
double halfAngleOffset = angleOffset / 2.0; |
|
||||
int categoryIndex = this.Categories.IndexOf(category); |
|
||||
double angle = startingAngle + (categoryIndex * angleOffset); |
|
||||
|
|
||||
return new Range<UnitValue>(new UnitValue(angle - halfAngleOffset, Unit.Degrees), new UnitValue(angle + halfAngleOffset, Unit.Degrees)); |
|
||||
} |
|
||||
|
|
||||
return new Range<UnitValue>(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the category at a given coordinate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="position">The plot area position.</param>
|
|
||||
/// <returns>The category at the given plot area position.</returns>
|
|
||||
public object GetCategoryAtPosition(UnitValue position) |
|
||||
{ |
|
||||
if (this.ActualLength == 0.0 || this.Categories.Count == 0) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
if (position.Unit == Unit.Pixels) |
|
||||
{ |
|
||||
double coordinate = position.Value; |
|
||||
int index = (int)Math.Floor(coordinate / (this.ActualLength / this.Categories.Count)); |
|
||||
if (index >= 0 && index < this.Categories.Count) |
|
||||
{ |
|
||||
if (Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
return this.Categories[index]; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return this.Categories[(this.Categories.Count - 1) - index]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the categories in response to an update from a registered
|
|
||||
/// axis data provider.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataProvider">The category axis information
|
|
||||
/// provider.</param>
|
|
||||
/// <param name="data">A sequence of categories.</param>
|
|
||||
public void DataChanged(IDataProvider dataProvider, IEnumerable<object> data) |
|
||||
{ |
|
||||
UpdateCategories(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the list of categories.
|
|
||||
/// </summary>
|
|
||||
private void UpdateCategories() |
|
||||
{ |
|
||||
IEnumerable<object> categories = |
|
||||
this.RegisteredListeners |
|
||||
.OfType<IDataProvider>() |
|
||||
.SelectMany(infoProvider => infoProvider.GetData(this)) |
|
||||
.Distinct(); |
|
||||
|
|
||||
if (SortOrder == CategorySortOrder.Ascending) |
|
||||
{ |
|
||||
categories = categories.OrderBy(category => category); |
|
||||
} |
|
||||
else if (SortOrder == CategorySortOrder.Descending) |
|
||||
{ |
|
||||
categories = categories.OrderByDescending(category => category); |
|
||||
} |
|
||||
|
|
||||
this.Categories = categories.ToList(); |
|
||||
|
|
||||
Invalidate(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the major axis grid line coordinates.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>A sequence of the major grid line coordinates.</returns>
|
|
||||
protected override IEnumerable<UnitValue> GetMajorGridLineCoordinates(Size availableSize) |
|
||||
{ |
|
||||
return GridLineCoordinatesToDisplay; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value for which to retrieve the plot area
|
|
||||
/// coordinate.</param>
|
|
||||
/// <returns>The plot area coordinate.</returns>
|
|
||||
public override UnitValue GetPlotAreaCoordinate(object value) |
|
||||
{ |
|
||||
if (value == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException("value"); |
|
||||
} |
|
||||
|
|
||||
Range<UnitValue> range = GetPlotAreaCoordinateRange(value); |
|
||||
if (range.HasData) |
|
||||
{ |
|
||||
double minimum = range.Minimum.Value; |
|
||||
double maximum = range.Maximum.Value; |
|
||||
return new UnitValue(((maximum - minimum) / 2.0) + minimum, range.Minimum.Unit); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return UnitValue.NaN(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates and prepares a new axis label.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The axis label value.</param>
|
|
||||
/// <returns>The axis label content control.</returns>
|
|
||||
private Control CreateAndPrepareAxisLabel(object value) |
|
||||
{ |
|
||||
Control axisLabel = _labelPool.Next(); |
|
||||
PrepareAxisLabel(axisLabel, value); |
|
||||
return axisLabel; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders as an oriented axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
private void RenderOriented(Size availableSize) |
|
||||
{ |
|
||||
_labelPool.Reset(); |
|
||||
_majorTickMarkPool.Reset(); |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
OrientedPanel.Children.Clear(); |
|
||||
this.GridLineCoordinatesToDisplay.Clear(); |
|
||||
|
|
||||
if (this.Categories.Count > 0) |
|
||||
{ |
|
||||
double maximumLength = Math.Max(GetLength(availableSize) - 1, 0); |
|
||||
|
|
||||
Action<double> placeTickMarkAt = |
|
||||
(pos) => |
|
||||
{ |
|
||||
Line tickMark = _majorTickMarkPool.Next(); |
|
||||
OrientedPanel.SetCenterCoordinate(tickMark, pos); |
|
||||
OrientedPanel.SetPriority(tickMark, 0); |
|
||||
this.GridLineCoordinatesToDisplay.Add(new UnitValue(pos, Unit.Pixels)); |
|
||||
OrientedPanel.Children.Add(tickMark); |
|
||||
}; |
|
||||
|
|
||||
int index = 0; |
|
||||
int priority = 0; |
|
||||
|
|
||||
foreach (object category in Categories) |
|
||||
{ |
|
||||
Control axisLabel = CreateAndPrepareAxisLabel(category); |
|
||||
double lower = ((index * maximumLength) / Categories.Count) + 0.5; |
|
||||
double upper = (((index + 1) * maximumLength) / Categories.Count) + 0.5; |
|
||||
placeTickMarkAt(lower); |
|
||||
OrientedPanel.SetCenterCoordinate(axisLabel, (lower + upper) / 2); |
|
||||
OrientedPanel.SetPriority(axisLabel, priority + 1); |
|
||||
OrientedPanel.Children.Add(axisLabel); |
|
||||
index++; |
|
||||
priority = (priority + 1) % 2; |
|
||||
} |
|
||||
placeTickMarkAt(maximumLength + 0.5); |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
_labelPool.Done(); |
|
||||
_majorTickMarkPool.Done(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis labels, tick marks, and other visual elements.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
protected override void Render(Size availableSize) |
|
||||
{ |
|
||||
RenderOriented(availableSize); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a value indicating whether a value can be plotted on the
|
|
||||
/// axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">A value which may or may not be able to be
|
|
||||
/// plotted.</param>
|
|
||||
/// <returns>A value indicating whether a value can be plotted on the
|
|
||||
/// axis.</returns>
|
|
||||
public override bool CanPlot(object value) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,28 +0,0 @@ |
|||||
// (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>
|
|
||||
/// The sort order to use when sorting categories.
|
|
||||
/// </summary>
|
|
||||
public enum CategorySortOrder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// No sort order.
|
|
||||
/// </summary>
|
|
||||
None, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Ascending sort order.
|
|
||||
/// </summary>
|
|
||||
Ascending, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Descending sort order.
|
|
||||
/// </summary>
|
|
||||
Descending |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,449 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
using System.Globalization; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Data; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis label for displaying DateTime values.
|
|
||||
/// </summary>
|
|
||||
public class DateTimeAxisLabel : AxisLabel |
|
||||
{ |
|
||||
#region public DateTimeIntervalType IntervalType
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the interval type of the DateTimeAxis2.
|
|
||||
/// </summary>
|
|
||||
public DateTimeIntervalType IntervalType |
|
||||
{ |
|
||||
get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); } |
|
||||
set { SetValue(IntervalTypeProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IntervalType dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty IntervalTypeProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"IntervalType", |
|
||||
typeof(DateTimeIntervalType), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(DateTimeIntervalType.Auto, OnIntervalTypePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IntervalTypeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its IntervalType.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIntervalTypePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
DateTimeIntervalType oldValue = (DateTimeIntervalType)e.OldValue; |
|
||||
DateTimeIntervalType newValue = (DateTimeIntervalType)e.NewValue; |
|
||||
source.OnIntervalTypePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IntervalTypeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnIntervalTypePropertyChanged(DateTimeIntervalType oldValue, DateTimeIntervalType newValue) |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public DateTimeIntervalType IntervalType
|
|
||||
|
|
||||
#region public string YearsIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string YearsIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(YearsIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(YearsIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the YearsIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty YearsIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"YearsIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnYearsIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// YearsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its YearsIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnYearsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnYearsIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// YearsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnYearsIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string YearsIntervalStringFormat
|
|
||||
|
|
||||
#region public string MonthsIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string MonthsIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(MonthsIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(MonthsIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the MonthsIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty MonthsIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"MonthsIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnMonthsIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MonthsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its MonthsIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMonthsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnMonthsIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MonthsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnMonthsIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string MonthsIntervalStringFormat
|
|
||||
|
|
||||
#region public string WeeksIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string WeeksIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(WeeksIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(WeeksIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the WeeksIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty WeeksIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"WeeksIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnWeeksIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// WeeksIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its WeeksIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnWeeksIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnWeeksIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// WeeksIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnWeeksIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string WeeksIntervalStringFormat
|
|
||||
|
|
||||
#region public string DaysIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string DaysIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(DaysIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(DaysIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DaysIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty DaysIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"DaysIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnDaysIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DaysIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its DaysIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDaysIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnDaysIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DaysIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnDaysIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string DaysIntervalStringFormat
|
|
||||
|
|
||||
#region public string HoursIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string HoursIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(HoursIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(HoursIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the HoursIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty HoursIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"HoursIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnHoursIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// HoursIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its HoursIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnHoursIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnHoursIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// HoursIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnHoursIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string HoursIntervalStringFormat
|
|
||||
|
|
||||
#region public string MinutesIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string MinutesIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(MinutesIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(MinutesIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the MinutesIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty MinutesIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"MinutesIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnMinutesIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MinutesIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its MinutesIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMinutesIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnMinutesIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MinutesIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnMinutesIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string MinutesIntervalStringFormat
|
|
||||
|
|
||||
#region public string SecondsIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string SecondsIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(SecondsIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(SecondsIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the SecondsIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty SecondsIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"SecondsIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnSecondsIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SecondsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its SecondsIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnSecondsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnSecondsIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SecondsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnSecondsIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string SecondsIntervalStringFormat
|
|
||||
|
|
||||
#region public string MillisecondsIntervalStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string to use when the interval is hours.
|
|
||||
/// </summary>
|
|
||||
public string MillisecondsIntervalStringFormat |
|
||||
{ |
|
||||
get { return GetValue(MillisecondsIntervalStringFormatProperty) as string; } |
|
||||
set { SetValue(MillisecondsIntervalStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the MillisecondsIntervalStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty MillisecondsIntervalStringFormatProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"MillisecondsIntervalStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DateTimeAxisLabel), |
|
||||
new System.Windows.PropertyMetadata(null, OnMillisecondsIntervalStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MillisecondsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DateTimeAxisLabel that changed its MillisecondsIntervalStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMillisecondsIntervalStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DateTimeAxisLabel source = (DateTimeAxisLabel)d; |
|
||||
source.OnMillisecondsIntervalStringFormatPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MillisecondsIntervalStringFormatProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
protected virtual void OnMillisecondsIntervalStringFormatPropertyChanged() |
|
||||
{ |
|
||||
UpdateFormattedContent(); |
|
||||
} |
|
||||
#endregion public string MillisecondsIntervalStringFormat
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the DateTimeAxisLabel class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static DateTimeAxisLabel() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimeAxisLabel), new FrameworkPropertyMetadata(typeof(DateTimeAxisLabel))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the DateTimeAxisLabel class.
|
|
||||
/// </summary>
|
|
||||
public DateTimeAxisLabel() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(DateTimeAxisLabel); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the formatted text.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Code is not overly complex.")] |
|
||||
protected override void UpdateFormattedContent() |
|
||||
{ |
|
||||
if (StringFormat == null) |
|
||||
{ |
|
||||
switch (IntervalType) |
|
||||
{ |
|
||||
case DateTimeIntervalType.Years: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = YearsIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Months: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MonthsIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Weeks: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = WeeksIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Days: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = DaysIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Hours: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = HoursIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Minutes: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MinutesIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Seconds: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = SecondsIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
case DateTimeIntervalType.Milliseconds: |
|
||||
this.SetBinding(FormattedContentProperty, new Binding { Converter = new StringFormatConverter(), ConverterParameter = MillisecondsIntervalStringFormat ?? StringFormat ?? "{0}" }); |
|
||||
break; |
|
||||
default: |
|
||||
base.UpdateFormattedContent(); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
base.UpdateFormattedContent(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,58 +0,0 @@ |
|||||
// (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 date time interval.
|
|
||||
/// </summary>
|
|
||||
public enum DateTimeIntervalType |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Automatically determine interval.
|
|
||||
/// </summary>
|
|
||||
Auto = 0, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is milliseconds.
|
|
||||
/// </summary>
|
|
||||
Milliseconds = 1, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is seconds.
|
|
||||
/// </summary>
|
|
||||
Seconds = 2, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is minutes.
|
|
||||
/// </summary>
|
|
||||
Minutes = 3, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is hours.
|
|
||||
/// </summary>
|
|
||||
Hours = 4, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is days.
|
|
||||
/// </summary>
|
|
||||
Days = 5, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is weeks.
|
|
||||
/// </summary>
|
|
||||
Weeks = 6, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is months.
|
|
||||
/// </summary>
|
|
||||
Months = 7, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Interval type is years.
|
|
||||
/// </summary>
|
|
||||
Years = 8, |
|
||||
} |
|
||||
} |
|
||||
@ -1,798 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis that has a range.
|
|
||||
/// </summary>
|
|
||||
public abstract class DisplayAxis : Axis, IRequireSeriesHost |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Maximum intervals per 200 pixels.
|
|
||||
/// </summary>
|
|
||||
protected const double MaximumAxisIntervalsPer200Pixels = 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The name of the axis grid template part.
|
|
||||
/// </summary>
|
|
||||
protected const string AxisGridName = "AxisGrid"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The name of the axis title template part.
|
|
||||
/// </summary>
|
|
||||
protected const string AxisTitleName = "AxisTitle"; |
|
||||
|
|
||||
#region public Style AxisLabelStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the style used for the axis labels.
|
|
||||
/// </summary>
|
|
||||
public Style AxisLabelStyle |
|
||||
{ |
|
||||
get { return GetValue(AxisLabelStyleProperty) as Style; } |
|
||||
set { SetValue(AxisLabelStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the AxisLabelStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty AxisLabelStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"AxisLabelStyle", |
|
||||
typeof(Style), |
|
||||
typeof(DisplayAxis), |
|
||||
new PropertyMetadata(null, OnAxisLabelStylePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// AxisLabelStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DisplayAxis that changed its AxisLabelStyle.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnAxisLabelStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DisplayAxis source = (DisplayAxis)d; |
|
||||
Style oldValue = (Style)e.OldValue; |
|
||||
Style newValue = (Style)e.NewValue; |
|
||||
source.OnAxisLabelStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// AxisLabelStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnAxisLabelStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
} |
|
||||
#endregion public Style AxisLabelStyle
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual length.
|
|
||||
/// </summary>
|
|
||||
protected double ActualLength |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return GetLength(new Size(this.ActualWidth, this.ActualHeight)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the length of the axis given an available size.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>The length of the axis given an available size.</returns>
|
|
||||
protected double GetLength(Size availableSize) |
|
||||
{ |
|
||||
if (this.ActualHeight == 0.0 && this.ActualWidth == 0.0) |
|
||||
{ |
|
||||
return 0.0; |
|
||||
} |
|
||||
if (this.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
return availableSize.Width; |
|
||||
} |
|
||||
else if (this.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
return availableSize.Height; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DisplayAxis_GetLength_CannotDetermineTheLengthOfAnAxisWithAnOrientationOfNone); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#region private GridLines GridLines
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This field stores the grid lines element.
|
|
||||
/// </summary>
|
|
||||
private DisplayAxisGridLines _gridLines; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the grid lines property.
|
|
||||
/// </summary>
|
|
||||
internal DisplayAxisGridLines GridLines |
|
||||
{ |
|
||||
get { return _gridLines; } |
|
||||
set |
|
||||
{ |
|
||||
if (value != _gridLines) |
|
||||
{ |
|
||||
DisplayAxisGridLines oldValue = _gridLines; |
|
||||
_gridLines = value; |
|
||||
OnGridLinesPropertyChanged(oldValue, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GridLinesProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnGridLinesPropertyChanged(DisplayAxisGridLines oldValue, DisplayAxisGridLines newValue) |
|
||||
{ |
|
||||
if (SeriesHost != null && oldValue != null) |
|
||||
{ |
|
||||
SeriesHost.BackgroundElements.Remove(oldValue); |
|
||||
} |
|
||||
if (SeriesHost != null && newValue != null) |
|
||||
{ |
|
||||
SeriesHost.BackgroundElements.Add(newValue); |
|
||||
} |
|
||||
} |
|
||||
#endregion private GridLines GridLines
|
|
||||
|
|
||||
#region public Style MajorTickMarkStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the style applied to the Axis tick marks.
|
|
||||
/// </summary>
|
|
||||
/// <value>The Style applied to the Axis tick marks.</value>
|
|
||||
public Style MajorTickMarkStyle |
|
||||
{ |
|
||||
get { return GetValue(MajorTickMarkStyleProperty) as Style; } |
|
||||
set { SetValue(MajorTickMarkStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the MajorTickMarkStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty MajorTickMarkStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"MajorTickMarkStyle", |
|
||||
typeof(Style), |
|
||||
typeof(DisplayAxis), |
|
||||
new PropertyMetadata(null, OnMajorTickMarkStylePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MajorTickMarkStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DisplayAxis that changed its MajorTickMarkStyle.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMajorTickMarkStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DisplayAxis source = (DisplayAxis)d; |
|
||||
Style oldValue = (Style)e.OldValue; |
|
||||
Style newValue = (Style)e.NewValue; |
|
||||
source.OnMajorTickMarkStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MajorTickMarkStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnMajorTickMarkStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
} |
|
||||
#endregion public Style MajorTickMarkStyle
|
|
||||
|
|
||||
#region public object Title
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title property.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return GetValue(TitleProperty) as object; } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Title", |
|
||||
typeof(object), |
|
||||
typeof(DisplayAxis), |
|
||||
new PropertyMetadata(null, OnTitlePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// TitleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">DisplayAxis that changed its Title.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DisplayAxis source = (DisplayAxis)d; |
|
||||
object oldValue = (object)e.OldValue; |
|
||||
object newValue = (object)e.NewValue; |
|
||||
source.OnTitlePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// TitleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnTitlePropertyChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
if (this.AxisTitle != null) |
|
||||
{ |
|
||||
this.AxisTitle.Content = Title; |
|
||||
} |
|
||||
} |
|
||||
#endregion public object Title
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the LayoutTransformControl used to rotate the title.
|
|
||||
/// </summary>
|
|
||||
private LayoutTransformControl TitleLayoutTransformControl { get; set; } |
|
||||
|
|
||||
#region public Style TitleStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the style applied to the Axis title.
|
|
||||
/// </summary>
|
|
||||
/// <value>The Style applied to the Axis title.</value>
|
|
||||
public Style TitleStyle |
|
||||
{ |
|
||||
get { return GetValue(TitleStyleProperty) as Style; } |
|
||||
set { SetValue(TitleStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TitleStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"TitleStyle", |
|
||||
typeof(Style), |
|
||||
typeof(DisplayAxis), |
|
||||
null); |
|
||||
#endregion
|
|
||||
|
|
||||
#region public bool ShowGridLines
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether grid lines should be shown.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected casing.")] |
|
||||
public bool ShowGridLines |
|
||||
{ |
|
||||
get { return (bool)GetValue(ShowGridLinesProperty); } |
|
||||
set { SetValue(ShowGridLinesProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ShowGridLines dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLines", Justification = "This is the expected capitalization.")] |
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] |
|
||||
public static readonly DependencyProperty ShowGridLinesProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ShowGridLines", |
|
||||
typeof(bool), |
|
||||
typeof(DisplayAxis), |
|
||||
new PropertyMetadata(false, OnShowGridLinesPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ShowGridLinesProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Axis that changed its ShowGridLines.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnShowGridLinesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DisplayAxis source = (DisplayAxis)d; |
|
||||
bool oldValue = (bool)e.OldValue; |
|
||||
bool newValue = (bool)e.NewValue; |
|
||||
source.OnShowGridLinesPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ShowGridLinesProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[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.")] |
|
||||
protected virtual void OnShowGridLinesPropertyChanged(bool oldValue, bool newValue) |
|
||||
{ |
|
||||
SetShowGridLines(newValue); |
|
||||
} |
|
||||
#endregion public bool ShowGridLines
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates and destroys a grid lines element based on the specified
|
|
||||
/// value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">A value indicating whether to display grid
|
|
||||
/// lines or not.</param>
|
|
||||
private void SetShowGridLines(bool newValue) |
|
||||
{ |
|
||||
if (newValue == true) |
|
||||
{ |
|
||||
this.GridLines = new OrientedAxisGridLines(this); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.GridLines = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#region public Style GridLineStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style of the Axis's gridlines.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "Current casing is the expected one.")] |
|
||||
public Style GridLineStyle |
|
||||
{ |
|
||||
get { return GetValue(GridLineStyleProperty) as Style; } |
|
||||
set { SetValue(GridLineStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the GridlineStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "Current casing is the expected one.")] |
|
||||
public static readonly DependencyProperty GridLineStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"GridLineStyle", |
|
||||
typeof(Style), |
|
||||
typeof(DisplayAxis), |
|
||||
null); |
|
||||
#endregion
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The grid used to layout the axis.
|
|
||||
/// </summary>
|
|
||||
private Grid _grid; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the grid used to layout the axis.
|
|
||||
/// </summary>
|
|
||||
private Grid AxisGrid |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _grid; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (_grid != value) |
|
||||
{ |
|
||||
if (_grid != null) |
|
||||
{ |
|
||||
_grid.Children.Clear(); |
|
||||
} |
|
||||
|
|
||||
_grid = value; |
|
||||
|
|
||||
if (_grid != null) |
|
||||
{ |
|
||||
_grid.Children.Add(this.OrientedPanel); |
|
||||
if (this.AxisTitle != null) |
|
||||
{ |
|
||||
_grid.Children.Add(this.AxisTitle); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a grid to lay out the dependent axis.
|
|
||||
/// </summary>
|
|
||||
private Grid DependentAxisGrid { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the oriented panel used to layout the axis labels.
|
|
||||
/// </summary>
|
|
||||
internal OrientedPanel OrientedPanel { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The control used to display the axis title.
|
|
||||
/// </summary>
|
|
||||
private Title _axisTitle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title control used to display the title.
|
|
||||
/// </summary>
|
|
||||
private Title AxisTitle |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _axisTitle; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (_axisTitle != value) |
|
||||
{ |
|
||||
if (_axisTitle != null) |
|
||||
{ |
|
||||
_axisTitle.Content = null; |
|
||||
} |
|
||||
|
|
||||
_axisTitle = value; |
|
||||
if (Title != null) |
|
||||
{ |
|
||||
_axisTitle.Content = Title; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a major axis tick mark.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A line to used to render a tick mark.</returns>
|
|
||||
protected virtual Line CreateMajorTickMark() |
|
||||
{ |
|
||||
return CreateTickMark(MajorTickMarkStyle); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a tick mark and applies a style to it.
|
|
||||
/// </summary>
|
|
||||
/// <param name="style">The style to apply.</param>
|
|
||||
/// <returns>The newly created tick mark.</returns>
|
|
||||
protected Line CreateTickMark(Style style) |
|
||||
{ |
|
||||
Line line = new Line(); |
|
||||
line.Style = style; |
|
||||
if (this.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
line.Y1 = 0.5; |
|
||||
line.Y2 = 0.5; |
|
||||
} |
|
||||
else if (this.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
line.X1 = 0.5; |
|
||||
line.X2 = 0.5; |
|
||||
} |
|
||||
return line; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is used to share the grid line coordinates with the
|
|
||||
/// internal grid lines control.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A sequence of the major grid line coordinates.</returns>
|
|
||||
internal IEnumerable<UnitValue> InternalGetMajorGridLinePositions() |
|
||||
{ |
|
||||
return GetMajorGridLineCoordinates(new Size(this.ActualWidth, this.ActualHeight)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the coordinates to use for the grid line control.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>A sequence of coordinates at which to draw grid lines.
|
|
||||
/// </returns>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] |
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Returns the coordinates of the grid lines.")] |
|
||||
protected abstract IEnumerable<UnitValue> GetMajorGridLineCoordinates(Size availableSize); |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the DisplayAxis class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static DisplayAxis() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(DisplayAxis), new FrameworkPropertyMetadata(typeof(DisplayAxis))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the DisplayAxis class.
|
|
||||
/// </summary>
|
|
||||
protected DisplayAxis() |
|
||||
{ |
|
||||
this.OrientedPanel = new OrientedPanel(); |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(DisplayAxis); |
|
||||
this.OrientedPanel.UseLayoutRounding = true; |
|
||||
#endif
|
|
||||
|
|
||||
this.DependentAxisGrid = new Grid(); |
|
||||
|
|
||||
this.TitleLayoutTransformControl = new LayoutTransformControl(); |
|
||||
this.TitleLayoutTransformControl.HorizontalAlignment = HorizontalAlignment.Center; |
|
||||
this.TitleLayoutTransformControl.VerticalAlignment = VerticalAlignment.Center; |
|
||||
|
|
||||
this.SizeChanged += new SizeChangedEventHandler(DisplayAxisSizeChanged); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// If display axis has just become visible, invalidate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void DisplayAxisSizeChanged(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
if (e.PreviousSize.Width == 0.0 && e.PreviousSize.Height == 0.0) |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates an axis label.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The new axis label.</returns>
|
|
||||
protected virtual Control CreateAxisLabel() |
|
||||
{ |
|
||||
return new AxisLabel(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the grid lines element if a suitable dependent axis has
|
|
||||
/// been added to a radial axis.
|
|
||||
/// </summary>
|
|
||||
protected override void OnDependentAxesCollectionChanged() |
|
||||
{ |
|
||||
SetShowGridLines(ShowGridLines); |
|
||||
base.OnDependentAxesCollectionChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Prepares an axis label to be plotted.
|
|
||||
/// </summary>
|
|
||||
/// <param name="label">The axis label to prepare.</param>
|
|
||||
/// <param name="dataContext">The data context to use for the axis
|
|
||||
/// label.</param>
|
|
||||
protected virtual void PrepareAxisLabel(Control label, object dataContext) |
|
||||
{ |
|
||||
label.DataContext = dataContext; |
|
||||
label.SetStyle(AxisLabelStyle); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Retrieves template parts and configures layout.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
base.OnApplyTemplate(); |
|
||||
|
|
||||
this.AxisGrid = GetTemplateChild(AxisGridName) as Grid; |
|
||||
this.AxisTitle = GetTemplateChild(AxisTitleName) as Title; |
|
||||
if (this.AxisTitle != null && this.AxisGrid.Children.Contains(this.AxisTitle)) |
|
||||
{ |
|
||||
this.AxisGrid.Children.Remove(this.AxisTitle); |
|
||||
this.TitleLayoutTransformControl.Child = this.AxisTitle; |
|
||||
this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); |
|
||||
} |
|
||||
|
|
||||
ArrangeAxisGrid(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// When the size of the oriented panel changes invalidate the axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void OnOrientedPanelSizeChanged(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Arranges the grid when the location property is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old location.</param>
|
|
||||
/// <param name="newValue">The new location.</param>
|
|
||||
protected override void OnLocationPropertyChanged(AxisLocation oldValue, AxisLocation newValue) |
|
||||
{ |
|
||||
ArrangeAxisGrid(); |
|
||||
base.OnLocationPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Arranges the elements in the axis grid.
|
|
||||
/// </summary>
|
|
||||
private void ArrangeAxisGrid() |
|
||||
{ |
|
||||
if (this.AxisGrid != null) |
|
||||
{ |
|
||||
this.AxisGrid.ColumnDefinitions.Clear(); |
|
||||
this.AxisGrid.RowDefinitions.Clear(); |
|
||||
this.AxisGrid.Children.Clear(); |
|
||||
|
|
||||
if (this.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
this.OrientedPanel.Orientation = System.Windows.Controls.Orientation.Vertical; |
|
||||
this.OrientedPanel.IsReversed = true; |
|
||||
|
|
||||
if (this.Location == AxisLocation.Left || this.Location == AxisLocation.Right) |
|
||||
{ |
|
||||
this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = -90.0 }; |
|
||||
|
|
||||
this.OrientedPanel.IsInverted = !(Location == AxisLocation.Right); |
|
||||
this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); |
|
||||
this.AxisGrid.RowDefinitions.Add(new RowDefinition()); |
|
||||
|
|
||||
int column = 0; |
|
||||
if (this.AxisTitle != null) |
|
||||
{ |
|
||||
this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); |
|
||||
Grid.SetRow(this.TitleLayoutTransformControl, 0); |
|
||||
Grid.SetColumn(this.TitleLayoutTransformControl, 0); |
|
||||
column++; |
|
||||
} |
|
||||
Grid.SetRow(this.OrientedPanel, 0); |
|
||||
Grid.SetColumn(this.OrientedPanel, column); |
|
||||
|
|
||||
this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); |
|
||||
this.AxisGrid.Children.Add(this.OrientedPanel); |
|
||||
|
|
||||
if (this.Location == AxisLocation.Right) |
|
||||
{ |
|
||||
AxisGrid.Mirror(System.Windows.Controls.Orientation.Vertical); |
|
||||
this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = 90 }; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else if (this.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
this.OrientedPanel.Orientation = System.Windows.Controls.Orientation.Horizontal; |
|
||||
this.OrientedPanel.IsReversed = false; |
|
||||
|
|
||||
if (this.Location == AxisLocation.Top || this.Location == AxisLocation.Bottom) |
|
||||
{ |
|
||||
this.OrientedPanel.IsInverted = (Location == AxisLocation.Top); |
|
||||
this.TitleLayoutTransformControl.Transform = new RotateTransform { Angle = 0 }; |
|
||||
|
|
||||
this.AxisGrid.ColumnDefinitions.Add(new ColumnDefinition()); |
|
||||
this.AxisGrid.RowDefinitions.Add(new RowDefinition()); |
|
||||
|
|
||||
if (this.AxisTitle != null) |
|
||||
{ |
|
||||
this.AxisGrid.RowDefinitions.Add(new RowDefinition()); |
|
||||
Grid.SetColumn(this.TitleLayoutTransformControl, 0); |
|
||||
Grid.SetRow(this.TitleLayoutTransformControl, 1); |
|
||||
} |
|
||||
|
|
||||
Grid.SetColumn(this.OrientedPanel, 0); |
|
||||
Grid.SetRow(this.OrientedPanel, 0); |
|
||||
|
|
||||
this.AxisGrid.Children.Add(this.TitleLayoutTransformControl); |
|
||||
this.AxisGrid.Children.Add(this.OrientedPanel); |
|
||||
|
|
||||
if (this.Location == AxisLocation.Top) |
|
||||
{ |
|
||||
AxisGrid.Mirror(System.Windows.Controls.Orientation.Horizontal); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Invalidate(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>The required size.</returns>
|
|
||||
protected override Size MeasureOverride(Size availableSize) |
|
||||
{ |
|
||||
RenderAxis(availableSize); |
|
||||
return base.MeasureOverride(availableSize); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reformulates the grid when the orientation is changed. Grid is
|
|
||||
/// either separated into two columns or two rows. The title is
|
|
||||
/// inserted with the outermost section from the edge and an oriented
|
|
||||
/// panel is inserted into the innermost section.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old value.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected override void OnOrientationPropertyChanged(AxisOrientation oldValue, AxisOrientation newValue) |
|
||||
{ |
|
||||
ArrangeAxisGrid(); |
|
||||
base.OnOrientationPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the visual appearance of the axis when it is invalidated.
|
|
||||
/// </summary>
|
|
||||
/// <param name="args">Information for the invalidated event.</param>
|
|
||||
protected override void OnInvalidated(RoutedEventArgs args) |
|
||||
{ |
|
||||
InvalidateMeasure(); |
|
||||
base.OnInvalidated(args); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis if there is a valid value for orientation.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size in which to render
|
|
||||
/// the axis.</param>
|
|
||||
private void RenderAxis(Size availableSize) |
|
||||
{ |
|
||||
if (Orientation != AxisOrientation.None && Location != AxisLocation.Auto) |
|
||||
{ |
|
||||
Render(availableSize); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis labels, tick marks, and other visual elements.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
protected abstract void Render(Size availableSize); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Invalidates the axis.
|
|
||||
/// </summary>
|
|
||||
protected void Invalidate() |
|
||||
{ |
|
||||
OnInvalidated(new RoutedEventArgs()); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The series host.
|
|
||||
/// </summary>
|
|
||||
private ISeriesHost _seriesHost; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the series host.
|
|
||||
/// </summary>
|
|
||||
public ISeriesHost SeriesHost |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _seriesHost; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value != _seriesHost) |
|
||||
{ |
|
||||
ISeriesHost oldValue = _seriesHost; |
|
||||
_seriesHost = value; |
|
||||
OnSeriesHostPropertyChanged(oldValue, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is run when the series host property is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old series host.</param>
|
|
||||
/// <param name="newValue">The new series host.</param>
|
|
||||
protected virtual void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) |
|
||||
{ |
|
||||
if (oldValue != null && this.GridLines != null) |
|
||||
{ |
|
||||
oldValue.BackgroundElements.Remove(this.GridLines); |
|
||||
} |
|
||||
|
|
||||
if (newValue != null && this.GridLines != null) |
|
||||
{ |
|
||||
newValue.BackgroundElements.Add(this.GridLines); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,106 +0,0 @@ |
|||||
// (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 = "DisplayAxisGridLines", Justification = "This is the expected capitalization.")] |
|
||||
internal abstract class DisplayAxisGridLines : Canvas, IAxisListener |
|
||||
{ |
|
||||
#region public DisplayAxis Axis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The field that stores the axis that the grid lines are connected to.
|
|
||||
/// </summary>
|
|
||||
private DisplayAxis _axis; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the axis that the grid lines are connected to.
|
|
||||
/// </summary>
|
|
||||
public DisplayAxis Axis |
|
||||
{ |
|
||||
get { return _axis; } |
|
||||
private set |
|
||||
{ |
|
||||
if (_axis != value) |
|
||||
{ |
|
||||
DisplayAxis oldValue = _axis; |
|
||||
_axis = value; |
|
||||
if (oldValue != _axis) |
|
||||
{ |
|
||||
OnAxisPropertyChanged(oldValue, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// AxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnAxisPropertyChanged(DisplayAxis oldValue, DisplayAxis newValue) |
|
||||
{ |
|
||||
Debug.Assert(newValue != null, "Don't set the axis property to null."); |
|
||||
|
|
||||
if (newValue != null) |
|
||||
{ |
|
||||
newValue.RegisteredListeners.Add(this); |
|
||||
} |
|
||||
|
|
||||
if (oldValue != null) |
|
||||
{ |
|
||||
oldValue.RegisteredListeners.Remove(this); |
|
||||
} |
|
||||
} |
|
||||
#endregion public DisplayAxis Axis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the DisplayAxisGridLines class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis used by the DisplayAxisGridLines.</param>
|
|
||||
public DisplayAxisGridLines(DisplayAxis axis) |
|
||||
{ |
|
||||
this.Axis = axis; |
|
||||
this.SizeChanged += new SizeChangedEventHandler(OnSizeChanged); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Redraws grid lines when the size of the control changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Redraws grid lines when the axis is invalidated.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The invalidated axis.</param>
|
|
||||
public void AxisInvalidated(IAxis axis) |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Draws the grid lines.
|
|
||||
/// </summary>
|
|
||||
protected abstract void Invalidate(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Range axes look for this interface on series to determine whether to
|
|
||||
/// anchor the origin to the bottom or top of the screen where possible.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Implementing this interface ensures that value margins will not cause
|
|
||||
/// an origin to float above the bottom or top of the screen if no
|
|
||||
/// data exists below or above.
|
|
||||
/// </remarks>
|
|
||||
public interface IAnchoredToOrigin |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the axis to which the data is anchored.
|
|
||||
/// </summary>
|
|
||||
IRangeAxis AnchoredAxis { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
// (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.ObjectModel; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis interface used to determine the plot area coordinate of values.
|
|
||||
/// </summary>
|
|
||||
public interface IAxis |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the orientation of the axis.
|
|
||||
/// </summary>
|
|
||||
AxisOrientation Orientation { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the Orientation property is changed.
|
|
||||
/// </summary>
|
|
||||
event RoutedPropertyChangedEventHandler<AxisOrientation> OrientationChanged; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a value indicating whether the axis can plot a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to plot.</param>
|
|
||||
/// <returns>A value indicating whether the axis can plot a value.
|
|
||||
/// </returns>
|
|
||||
bool CanPlot(object value); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value for which to retrieve the plot area
|
|
||||
/// coordinate.</param>
|
|
||||
/// <returns>The plot area coordinate.</returns>
|
|
||||
UnitValue GetPlotAreaCoordinate(object value); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the registered IAxisListeners.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<IAxisListener> RegisteredListeners { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of child axes.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<IAxis> DependentAxes { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,19 +0,0 @@ |
|||||
// (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>
|
|
||||
/// An object that listens for changes in an axis.
|
|
||||
/// </summary>
|
|
||||
public interface IAxisListener |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// This method is called when the axis is invalidated.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis that has been invalidated.</param>
|
|
||||
void AxisInvalidated(IAxis axis); |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis that is arranged by category.
|
|
||||
/// </summary>
|
|
||||
public interface ICategoryAxis : IAxis, IDataConsumer |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Accepts a category and returns the coordinate range of that category
|
|
||||
/// on the axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="category">A category for which to retrieve the
|
|
||||
/// coordinate location.</param>
|
|
||||
/// <returns>The coordinate range of the category on the axis.</returns>
|
|
||||
Range<UnitValue> GetPlotAreaCoordinateRange(object category); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the category at a given coordinate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="position">The plot are coordinate.</param>
|
|
||||
/// <returns>The category at the given plot area coordinate.</returns>
|
|
||||
object GetCategoryAtPosition(UnitValue position); |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An object that consumes data.
|
|
||||
/// </summary>
|
|
||||
public interface IDataConsumer |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Supplies the consumer with data.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataProvider">The data provider.</param>
|
|
||||
/// <param name="data">The data used by the consumer.</param>
|
|
||||
void DataChanged(IDataProvider dataProvider, IEnumerable<object> data); |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides information to a category axis.
|
|
||||
/// </summary>
|
|
||||
public interface IDataProvider |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Retrieves the data to be plotted on the axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis to retrieve the data for.</param>
|
|
||||
/// <returns>The data to plot on the axis.</returns>
|
|
||||
IEnumerable<object> GetData(IDataConsumer axis); |
|
||||
} |
|
||||
} |
|
||||
@ -1,35 +0,0 @@ |
|||||
// (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; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An object that consumes a range.
|
|
||||
/// </summary>
|
|
||||
public interface IRangeConsumer |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Informs a range consumer that a provider's range has changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="provider">The range provider.</param>
|
|
||||
/// <param name="range">The range of data.</param>
|
|
||||
void RangeChanged(IRangeProvider provider, Range<IComparable> range); |
|
||||
} |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
// (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; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides information to a RangeConsumer.
|
|
||||
/// </summary>
|
|
||||
public interface IRangeProvider |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Returns the range of values.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rangeConsumer">The range consumer requesting the data
|
|
||||
/// range.</param>
|
|
||||
/// <returns>A data range.</returns>
|
|
||||
Range<IComparable> GetRange(IRangeConsumer rangeConsumer); |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Consumes value margins and uses them to lay out objects.
|
|
||||
/// </summary>
|
|
||||
public interface IValueMarginConsumer |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Updates layout to accommodate for value margins.
|
|
||||
/// </summary>
|
|
||||
/// <param name="provider">A value margin provider.</param>
|
|
||||
/// <param name="valueMargins">A sequence of value margins.</param>
|
|
||||
void ValueMarginsChanged(IValueMarginProvider provider, IEnumerable<ValueMargin> valueMargins); |
|
||||
} |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides information about margins necessary for values.
|
|
||||
/// </summary>
|
|
||||
public interface IValueMarginProvider |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the margins required for values.
|
|
||||
/// </summary>
|
|
||||
/// <param name="consumer">The axis to retrieve the value margins
|
|
||||
/// for.</param>
|
|
||||
/// <returns>The margins required for values.</returns>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method does a substantial amount of work.")] |
|
||||
IEnumerable<ValueMargin> GetValueMargins(IValueMarginConsumer consumer); |
|
||||
} |
|
||||
} |
|
||||
@ -1,400 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
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(NumericAxisLabel))] |
|
||||
[StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] |
|
||||
[TemplatePart(Name = AxisGridName, Type = typeof(Grid))] |
|
||||
[TemplatePart(Name = AxisTitleName, Type = typeof(Title))] |
|
||||
public class LinearAxis : NumericAxis |
|
||||
{ |
|
||||
#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(LinearAxis), |
|
||||
new PropertyMetadata(null, OnIntervalPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IntervalProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">LinearAxis that changed its Interval.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
LinearAxis source = (LinearAxis)d; |
|
||||
source.OnIntervalPropertyChanged(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IntervalProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
private void OnIntervalPropertyChanged() |
|
||||
{ |
|
||||
OnInvalidated(new RoutedEventArgs()); |
|
||||
} |
|
||||
#endregion public double? Interval
|
|
||||
|
|
||||
#region public double ActualInterval
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual interval of the axis.
|
|
||||
/// </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(LinearAxis), |
|
||||
new PropertyMetadata(double.NaN)); |
|
||||
#endregion public double ActualInterval
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the LinearAxis class.
|
|
||||
/// </summary>
|
|
||||
public LinearAxis() |
|
||||
{ |
|
||||
this.ActualRange = new Range<IComparable>(0.0, 1.0); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual range of double values.
|
|
||||
/// </summary>
|
|
||||
protected Range<double> ActualDoubleRange { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates ActualDoubleRange when ActualRange changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="range">New ActualRange value.</param>
|
|
||||
protected override void OnActualRangeChanged(Range<IComparable> range) |
|
||||
{ |
|
||||
ActualDoubleRange = range.ToDoubleRange(); |
|
||||
base.OnActualRangeChanged(range); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to plot.</param>
|
|
||||
/// <param name="length">The length of axis.</param>
|
|
||||
/// <returns>The plot area coordinate of a value.</returns>
|
|
||||
protected override UnitValue GetPlotAreaCoordinate(object value, double length) |
|
||||
{ |
|
||||
return GetPlotAreaCoordinate(value, ActualDoubleRange, length); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to plot.</param>
|
|
||||
/// <param name="currentRange">The range of values.</param>
|
|
||||
/// <param name="length">The length of axis.</param>
|
|
||||
/// <returns>The plot area coordinate of a value.</returns>
|
|
||||
protected override UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length) |
|
||||
{ |
|
||||
return GetPlotAreaCoordinate(value, currentRange.ToDoubleRange(), length); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to plot.</param>
|
|
||||
/// <param name="currentRange">The range of values.</param>
|
|
||||
/// <param name="length">The length of axis.</param>
|
|
||||
/// <returns>The plot area coordinate of a value.</returns>
|
|
||||
private static UnitValue GetPlotAreaCoordinate(object value, Range<double> currentRange, double length) |
|
||||
{ |
|
||||
if (currentRange.HasData) |
|
||||
{ |
|
||||
double doubleValue = ValueHelper.ToDouble(value); |
|
||||
|
|
||||
double pixelLength = Math.Max(length - 1, 0); |
|
||||
double rangelength = currentRange.Maximum - currentRange.Minimum; |
|
||||
|
|
||||
return new UnitValue((doubleValue - currentRange.Minimum) * (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>Actual interval to use to determine which values are
|
|
||||
/// displayed in the axis.
|
|
||||
/// </returns>
|
|
||||
protected virtual double CalculateActualInterval(Size availableSize) |
|
||||
{ |
|
||||
if (Interval != null) |
|
||||
{ |
|
||||
return Interval.Value; |
|
||||
} |
|
||||
|
|
||||
// Adjust maximum interval count adjusted for current axis
|
|
||||
double adjustedMaximumIntervalsPer200Pixels = (Orientation == AxisOrientation.X ? 0.8 : 1.0) * MaximumAxisIntervalsPer200Pixels; |
|
||||
// Calculate maximum interval count for current space
|
|
||||
double maximumIntervalCount = Math.Max(GetLength(availableSize) * adjustedMaximumIntervalsPer200Pixels / 200.0, 1.0); |
|
||||
// Calculate range
|
|
||||
double range = ActualDoubleRange.Maximum - ActualDoubleRange.Minimum; |
|
||||
// Calculate largest acceptable interval
|
|
||||
double bestInterval = range / maximumIntervalCount; |
|
||||
// Calculate mimimum ideal interval (ideal => something that gives nice axis values)
|
|
||||
double minimumIdealInterval = Math.Pow(10, Math.Floor(Math.Log10(bestInterval))); |
|
||||
// Walk the list of ideal multipliers
|
|
||||
foreach (int idealMultiplier in new int[] { 10, 5, 2, 1 }) |
|
||||
{ |
|
||||
// Check the current ideal multiplier against the maximum count
|
|
||||
double currentIdealInterval = minimumIdealInterval * idealMultiplier; |
|
||||
if (maximumIntervalCount < (range / currentIdealInterval)) |
|
||||
{ |
|
||||
// Went too far, break out
|
|
||||
break; |
|
||||
} |
|
||||
// Update the best interval
|
|
||||
bestInterval = currentIdealInterval; |
|
||||
} |
|
||||
// Return best interval
|
|
||||
return bestInterval; |
|
||||
} |
|
||||
|
|
||||
/// <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 GetMajorValues(availableSize).CastWrapper<IComparable>(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a sequence of major axis values.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>A sequence of major axis values.
|
|
||||
/// </returns>
|
|
||||
private IEnumerable<double> GetMajorValues(Size availableSize) |
|
||||
{ |
|
||||
if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0) |
|
||||
{ |
|
||||
yield break; |
|
||||
} |
|
||||
this.ActualInterval = CalculateActualInterval(availableSize); |
|
||||
double startValue = AlignToInterval(ActualDoubleRange.Minimum, this.ActualInterval); |
|
||||
if (startValue < ActualDoubleRange.Minimum) |
|
||||
{ |
|
||||
startValue = AlignToInterval(ActualDoubleRange.Minimum + this.ActualInterval, this.ActualInterval); |
|
||||
} |
|
||||
double nextValue = startValue; |
|
||||
for (int counter = 1; nextValue <= ActualDoubleRange.Maximum; counter++) |
|
||||
{ |
|
||||
yield return nextValue; |
|
||||
nextValue = startValue + (counter * this.ActualInterval); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <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 GetMajorValues(availableSize).CastWrapper<IComparable>(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Aligns a value to the provided interval value. The aligned value
|
|
||||
/// should always be smaller than or equal to than the provided value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to align to the interval.</param>
|
|
||||
/// <param name="interval">The interval to align to.</param>
|
|
||||
/// <returns>The aligned value.</returns>
|
|
||||
private static double AlignToInterval(double value, double interval) |
|
||||
{ |
|
||||
double typedInterval = (double)interval; |
|
||||
double typedValue = (double)value; |
|
||||
return ValueHelper.RemoveNoiseFromDoubleMath(ValueHelper.RemoveNoiseFromDoubleMath(Math.Floor(typedValue / typedInterval)) * typedInterval); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value range given a plot area coordinate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The plot area position.</param>
|
|
||||
/// <returns>The value at that plot area coordinate.</returns>
|
|
||||
protected override IComparable GetValueAtPosition(UnitValue value) |
|
||||
{ |
|
||||
if (ActualRange.HasData && ActualLength != 0.0) |
|
||||
{ |
|
||||
if (value.Unit == Unit.Pixels) |
|
||||
{ |
|
||||
double coordinate = value.Value; |
|
||||
|
|
||||
double rangelength = ActualDoubleRange.Maximum - ActualDoubleRange.Minimum; |
|
||||
double output = ((coordinate * (rangelength / ActualLength)) + ActualDoubleRange.Minimum); |
|
||||
|
|
||||
return output; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Function that uses the mid point of all the data values
|
|
||||
/// in the value margins to convert a length into a range
|
|
||||
/// of data with the mid point as the center of that range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="midPoint">The mid point of the range.</param>
|
|
||||
/// <param name="length">The length of the range.</param>
|
|
||||
/// <returns>The range object.</returns>
|
|
||||
private static Range<double> LengthToRange(double midPoint, double length) |
|
||||
{ |
|
||||
double halfLength = length / 2.0; |
|
||||
return new Range<double>(midPoint - halfLength, midPoint + halfLength); |
|
||||
} |
|
||||
|
|
||||
/// <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>Returns the overridden range.</returns>
|
|
||||
protected override Range<IComparable> OverrideDataRange(Range<IComparable> range) |
|
||||
{ |
|
||||
range = base.OverrideDataRange(range); |
|
||||
if (!range.HasData) |
|
||||
{ |
|
||||
return new Range<IComparable>(0.0, 1.0); |
|
||||
} |
|
||||
else if (ValueHelper.Compare(range.Minimum, range.Maximum) == 0) |
|
||||
{ |
|
||||
Range<IComparable> outputRange = new Range<IComparable>((ValueHelper.ToDouble(range.Minimum)) - 1, (ValueHelper.ToDouble(range.Maximum)) + 1); |
|
||||
return outputRange; |
|
||||
} |
|
||||
|
|
||||
// ActualLength of 1.0 or less maps all points to the same coordinate
|
|
||||
if (range.HasData && this.ActualLength > 1.0) |
|
||||
{ |
|
||||
bool isDataAnchoredToOrigin = false; |
|
||||
IList<ValueMarginCoordinateAndOverlap> valueMargins = new List<ValueMarginCoordinateAndOverlap>(); |
|
||||
foreach (IValueMarginProvider valueMarginProvider in this.RegisteredListeners.OfType<IValueMarginProvider>()) |
|
||||
{ |
|
||||
foreach (ValueMargin valueMargin in valueMarginProvider.GetValueMargins(this)) |
|
||||
{ |
|
||||
IAnchoredToOrigin dataAnchoredToOrigin = valueMarginProvider as IAnchoredToOrigin; |
|
||||
isDataAnchoredToOrigin = (dataAnchoredToOrigin != null && dataAnchoredToOrigin.AnchoredAxis == 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<double> originalRange = range.ToDoubleRange(); |
|
||||
Range<double> currentRange = range.ToDoubleRange(); |
|
||||
|
|
||||
// Ensure range is not empty.
|
|
||||
if (currentRange.Minimum == currentRange.Maximum) |
|
||||
{ |
|
||||
currentRange = new Range<double>(currentRange.Maximum - 1, currentRange.Maximum + 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) |
|
||||
{ |
|
||||
double unitOverPixels = currentRange.GetLength().Value / actualLength; |
|
||||
double newMinimum = currentRange.Minimum - ((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels); |
|
||||
double newMaximum = currentRange.Maximum + ((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels); |
|
||||
|
|
||||
currentRange = new Range<double>(newMinimum, newMaximum); |
|
||||
UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); |
|
||||
GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); |
|
||||
} |
|
||||
|
|
||||
if (isDataAnchoredToOrigin) |
|
||||
{ |
|
||||
if (originalRange.Minimum >= 0 && currentRange.Minimum < 0) |
|
||||
{ |
|
||||
currentRange = new Range<double>(0, currentRange.Maximum); |
|
||||
} |
|
||||
else if (originalRange.Maximum <= 0 && currentRange.Maximum > 0) |
|
||||
{ |
|
||||
currentRange = new Range<double>(currentRange.Minimum, 0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return currentRange.ToComparableRange(); |
|
||||
} |
|
||||
} |
|
||||
return range; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,125 +0,0 @@ |
|||||
// (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; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Collections.Specialized; |
|
||||
using System.Globalization; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Data; |
|
||||
using ResxResources = System.Windows.Controls.DataVisualization.Properties.Resources; |
|
||||
using System.ComponentModel; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Converts a string or base value to a <see cref="Nullable"/> value.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type should be value type.</typeparam>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class NullableConverter<T> : TypeConverter where T : struct |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Returns whether the type converter can convert an object from the
|
|
||||
/// specified type to the type of this converter.
|
|
||||
/// </summary>
|
|
||||
/// <param name="context">An object that provides a format context.
|
|
||||
/// </param>
|
|
||||
/// <param name="sourceType">The type you want to convert from.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns true if this converter can perform the conversion;
|
|
||||
/// otherwise, false.
|
|
||||
/// </returns>
|
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|
||||
{ |
|
||||
if (sourceType == typeof(T)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
else if (sourceType == typeof(string)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns whether the type converter can convert an object from the
|
|
||||
/// specified type to the type of this converter.
|
|
||||
/// </summary>
|
|
||||
/// <param name="context">An object that provides a format context.
|
|
||||
/// </param>
|
|
||||
/// <param name="destinationType">The type you want to convert to.
|
|
||||
/// </param>
|
|
||||
/// <returns>
|
|
||||
/// Returns true if this converter can perform the conversion;
|
|
||||
/// otherwise, false.
|
|
||||
/// </returns>
|
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) |
|
||||
{ |
|
||||
return (destinationType == typeof(T)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts from the specified value to the type of this converter.
|
|
||||
/// </summary>
|
|
||||
/// <param name="context">An object that provides a format context.
|
|
||||
/// </param>
|
|
||||
/// <param name="culture">The
|
|
||||
/// <see cref="T:System.Globalization.CultureInfo"/> to use as the
|
|
||||
/// current culture.</param>
|
|
||||
/// <param name="value">The value to convert to the type of this
|
|
||||
/// converter.</param>
|
|
||||
/// <returns>The converted value.</returns>
|
|
||||
/// <exception cref="T:System.NotSupportedException">
|
|
||||
/// The conversion cannot be performed.
|
|
||||
/// </exception>
|
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) |
|
||||
{ |
|
||||
string stringValue = value as string; |
|
||||
if (value is T) |
|
||||
{ |
|
||||
return new Nullable<T>((T)value); |
|
||||
} |
|
||||
else if (string.IsNullOrEmpty(stringValue) || String.Equals(stringValue, "Auto", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
return new Nullable<T>(); |
|
||||
} |
|
||||
return new Nullable<T>((T)Convert.ChangeType(value, typeof(T), culture)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts from the specified value to the a specified type from the
|
|
||||
/// type of this converter.
|
|
||||
/// </summary>
|
|
||||
/// <param name="context">An object that provides a format context.
|
|
||||
/// </param>
|
|
||||
/// <param name="culture">The
|
|
||||
/// <see cref="T:System.Globalization.CultureInfo"/> to use as the
|
|
||||
/// current culture.</param>
|
|
||||
/// <param name="value">The value to convert to the type of this
|
|
||||
/// converter.</param>
|
|
||||
/// <param name="destinationType">The type of convert the value to
|
|
||||
/// .</param>
|
|
||||
/// <returns>The converted value.</returns>
|
|
||||
/// <exception cref="T:System.NotSupportedException">
|
|
||||
/// The conversion cannot be performed.
|
|
||||
/// </exception>
|
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) |
|
||||
{ |
|
||||
if (value == null) |
|
||||
{ |
|
||||
return string.Empty; |
|
||||
} |
|
||||
else if (destinationType == typeof(string)) |
|
||||
{ |
|
||||
return value.ToString(); |
|
||||
} |
|
||||
return base.ConvertTo(context, culture, value, destinationType); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,289 +0,0 @@ |
|||||
// (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.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>
|
|
||||
public abstract class NumericAxis : RangeAxis |
|
||||
{ |
|
||||
#region public double? ActualMaximum
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual maximum value plotted on the chart.
|
|
||||
/// </summary>
|
|
||||
public double? ActualMaximum |
|
||||
{ |
|
||||
get { return (double?)GetValue(ActualMaximumProperty); } |
|
||||
private set { SetValue(ActualMaximumProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualMaximum dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualMaximumProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualMaximum", |
|
||||
typeof(double?), |
|
||||
typeof(NumericAxis), |
|
||||
null); |
|
||||
|
|
||||
#endregion public double? ActualMaximum
|
|
||||
|
|
||||
#region public double? ActualMinimum
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual maximum value plotted on the chart.
|
|
||||
/// </summary>
|
|
||||
public double? ActualMinimum |
|
||||
{ |
|
||||
get { return (double?)GetValue(ActualMinimumProperty); } |
|
||||
private set { SetValue(ActualMinimumProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualMinimum dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualMinimumProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualMinimum", |
|
||||
typeof(double?), |
|
||||
typeof(NumericAxis), |
|
||||
null); |
|
||||
|
|
||||
#endregion public double? ActualMinimum
|
|
||||
|
|
||||
#region public double? Maximum
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the maximum value plotted on the axis.
|
|
||||
/// </summary>
|
|
||||
[TypeConverter(typeof(NullableConverter<double>))] |
|
||||
public double? Maximum |
|
||||
{ |
|
||||
get { return (double?)GetValue(MaximumProperty); } |
|
||||
set { SetValue(MaximumProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Maximum dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty MaximumProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Maximum", |
|
||||
typeof(double?), |
|
||||
typeof(NumericAxis), |
|
||||
new PropertyMetadata(null, OnMaximumPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MaximumProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">NumericAxis that changed its Maximum.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
NumericAxis source = (NumericAxis)d; |
|
||||
double? newValue = (double?)e.NewValue; |
|
||||
source.OnMaximumPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MaximumProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnMaximumPropertyChanged(double? newValue) |
|
||||
{ |
|
||||
this.ProtectedMaximum = newValue; |
|
||||
} |
|
||||
#endregion public double? Maximum
|
|
||||
|
|
||||
#region public double? Minimum
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the minimum value to plot on the axis.
|
|
||||
/// </summary>
|
|
||||
[TypeConverter(typeof(NullableConverter<double>))] |
|
||||
public double? Minimum |
|
||||
{ |
|
||||
get { return (double?)GetValue(MinimumProperty); } |
|
||||
set { SetValue(MinimumProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Minimum dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty MinimumProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Minimum", |
|
||||
typeof(double?), |
|
||||
typeof(NumericAxis), |
|
||||
new PropertyMetadata(null, OnMinimumPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MinimumProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">NumericAxis that changed its Minimum.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
NumericAxis source = (NumericAxis)d; |
|
||||
double? newValue = (double?)e.NewValue; |
|
||||
source.OnMinimumPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MinimumProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnMinimumPropertyChanged(double? newValue) |
|
||||
{ |
|
||||
this.ProtectedMinimum = newValue; |
|
||||
} |
|
||||
#endregion public double? Minimum
|
|
||||
|
|
||||
#region public bool ExtendRangeToOrigin
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether to always show the origin.
|
|
||||
/// </summary>
|
|
||||
public bool ExtendRangeToOrigin |
|
||||
{ |
|
||||
get { return (bool)GetValue(ExtendRangeToOriginProperty); } |
|
||||
set { SetValue(ExtendRangeToOriginProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ExtendRangeToOrigin dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ExtendRangeToOriginProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ExtendRangeToOrigin", |
|
||||
typeof(bool), |
|
||||
typeof(NumericAxis), |
|
||||
new PropertyMetadata(false, OnExtendRangeToOriginPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ExtendRangeToOriginProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">NumericAxis that changed its ExtendRangeToOrigin.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnExtendRangeToOriginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
NumericAxis source = (NumericAxis)d; |
|
||||
bool oldValue = (bool)e.OldValue; |
|
||||
bool newValue = (bool)e.NewValue; |
|
||||
source.OnExtendRangeToOriginPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ExtendRangeToOriginProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnExtendRangeToOriginPropertyChanged(bool oldValue, bool newValue) |
|
||||
{ |
|
||||
this.ActualRange = this.OverrideDataRange(this.ActualRange); |
|
||||
} |
|
||||
#endregion public bool ExtendRangeToOrigin
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the origin value on the axis.
|
|
||||
/// </summary>
|
|
||||
protected override IComparable Origin |
|
||||
{ |
|
||||
get { return 0.0; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the NumericAxis class.
|
|
||||
/// </summary>
|
|
||||
protected NumericAxis() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <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 = (double)range.Maximum; |
|
||||
this.ActualMinimum = (double)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) |
|
||||
{ |
|
||||
double val; |
|
||||
return ValueHelper.TryConvert(value, out val); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a numeric axis label.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A numeric axis label.</returns>
|
|
||||
protected override Control CreateAxisLabel() |
|
||||
{ |
|
||||
return new NumericAxisLabel(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Overrides the data value range and returns a range that takes the
|
|
||||
/// margins of the values into account.
|
|
||||
/// </summary>
|
|
||||
/// <param name="range">The range of data values.</param>
|
|
||||
/// <returns>A range that can store both the data values and their
|
|
||||
/// margins.</returns>
|
|
||||
protected override Range<IComparable> OverrideDataRange(Range<IComparable> range) |
|
||||
{ |
|
||||
range = base.OverrideDataRange(range); |
|
||||
|
|
||||
if (ExtendRangeToOrigin) |
|
||||
{ |
|
||||
Range<double> adjustedRange = range.ToDoubleRange(); |
|
||||
|
|
||||
if (!adjustedRange.HasData) |
|
||||
{ |
|
||||
return new Range<IComparable>(0.0, 0.0); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
double minimum = adjustedRange.Minimum; |
|
||||
double maximum = adjustedRange.Maximum; |
|
||||
if (minimum > 0.0) |
|
||||
{ |
|
||||
minimum = 0.0; |
|
||||
} |
|
||||
else if (maximum < 0.0) |
|
||||
{ |
|
||||
maximum = 0.0; |
|
||||
} |
|
||||
return new Range<IComparable>(minimum, maximum); |
|
||||
} |
|
||||
} |
|
||||
return range; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,36 +0,0 @@ |
|||||
// (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.Windows; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A label used to display numeric axis values.
|
|
||||
/// </summary>
|
|
||||
public class NumericAxisLabel : AxisLabel |
|
||||
{ |
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the NumericAxisLabel class.
|
|
||||
/// </summary>
|
|
||||
static NumericAxisLabel() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericAxisLabel), new FrameworkPropertyMetadata(typeof(NumericAxisLabel))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the NumericAxisLabel class.
|
|
||||
/// </summary>
|
|
||||
public NumericAxisLabel() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(NumericAxisLabel); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,90 +0,0 @@ |
|||||
// (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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,622 +0,0 @@ |
|||||
// (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.Collections.ObjectModel; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axis that has a range.
|
|
||||
/// </summary>
|
|
||||
public abstract class RangeAxis : DisplayAxis, IRangeAxis, IValueMarginConsumer |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A pool of major tick marks.
|
|
||||
/// </summary>
|
|
||||
private ObjectPool<Line> _majorTickMarkPool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A pool of major tick marks.
|
|
||||
/// </summary>
|
|
||||
private ObjectPool<Line> _minorTickMarkPool; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A pool of labels.
|
|
||||
/// </summary>
|
|
||||
private ObjectPool<Control> _labelPool; |
|
||||
|
|
||||
#region public Style MinorTickMarkStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the minor tick mark style.
|
|
||||
/// </summary>
|
|
||||
public Style MinorTickMarkStyle |
|
||||
{ |
|
||||
get { return GetValue(MinorTickMarkStyleProperty) as Style; } |
|
||||
set { SetValue(MinorTickMarkStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the MinorTickMarkStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty MinorTickMarkStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"MinorTickMarkStyle", |
|
||||
typeof(Style), |
|
||||
typeof(RangeAxis), |
|
||||
new PropertyMetadata(null)); |
|
||||
|
|
||||
#endregion public Style MinorTickMarkStyle
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The actual range of values.
|
|
||||
/// </summary>
|
|
||||
private Range<IComparable> _actualRange; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual range of values.
|
|
||||
/// </summary>
|
|
||||
protected Range<IComparable> ActualRange |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _actualRange; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
Range<IComparable> oldValue = _actualRange; |
|
||||
Range<IComparable> minMaxEnforcedValue = EnforceMaximumAndMinimum(value); |
|
||||
|
|
||||
if (!oldValue.Equals(minMaxEnforcedValue)) |
|
||||
{ |
|
||||
_actualRange = minMaxEnforcedValue; |
|
||||
OnActualRangeChanged(minMaxEnforcedValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The maximum value displayed in the range axis.
|
|
||||
/// </summary>
|
|
||||
private IComparable _protectedMaximum; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the maximum value displayed in the range axis.
|
|
||||
/// </summary>
|
|
||||
protected IComparable ProtectedMaximum |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _protectedMaximum; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value != null && ProtectedMinimum != null && ValueHelper.Compare(ProtectedMinimum, value) > 0) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.RangeAxis_MaximumValueMustBeLargerThanOrEqualToMinimumValue); |
|
||||
} |
|
||||
if (!object.ReferenceEquals(_protectedMaximum, value) && !object.Equals(_protectedMaximum, value)) |
|
||||
{ |
|
||||
_protectedMaximum = value; |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The minimum value displayed in the range axis.
|
|
||||
/// </summary>
|
|
||||
private IComparable _protectedMinimum; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the minimum value displayed in the range axis.
|
|
||||
/// </summary>
|
|
||||
protected IComparable ProtectedMinimum |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _protectedMinimum; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value != null && ProtectedMaximum != null && ValueHelper.Compare(value, ProtectedMaximum) > 0) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.RangeAxis_MinimumValueMustBeLargerThanOrEqualToMaximumValue); |
|
||||
} |
|
||||
if (!object.ReferenceEquals(_protectedMinimum, value) && !object.Equals(_protectedMinimum, value)) |
|
||||
{ |
|
||||
_protectedMinimum = value; |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the RangeAxis class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static RangeAxis() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeAxis), new FrameworkPropertyMetadata(typeof(RangeAxis))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the RangeAxis class.
|
|
||||
/// </summary>
|
|
||||
protected RangeAxis() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(RangeAxis); |
|
||||
#endif
|
|
||||
this._labelPool = new ObjectPool<Control>(() => CreateAxisLabel()); |
|
||||
this._majorTickMarkPool = new ObjectPool<Line>(() => CreateMajorTickMark()); |
|
||||
this._minorTickMarkPool = new ObjectPool<Line>(() => CreateMinorTickMark()); |
|
||||
|
|
||||
// Update actual range when size changes for the first time. This
|
|
||||
// is necessary because the value margins may have changed after
|
|
||||
// the first layout pass.
|
|
||||
SizeChangedEventHandler handler = null; |
|
||||
handler = delegate |
|
||||
{ |
|
||||
SizeChanged -= handler; |
|
||||
UpdateActualRange(); |
|
||||
}; |
|
||||
SizeChanged += handler; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a minor axis tick mark.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A line to used to render a tick mark.</returns>
|
|
||||
protected Line CreateMinorTickMark() |
|
||||
{ |
|
||||
return CreateTickMark(MinorTickMarkStyle); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Invalidates axis when the actual range changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="range">The new actual range.</param>
|
|
||||
protected virtual void OnActualRangeChanged(Range<IComparable> range) |
|
||||
{ |
|
||||
Invalidate(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a given value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to return the plot area coordinate for.</param>
|
|
||||
/// <returns>The plot area coordinate of the given value.</returns>
|
|
||||
public override UnitValue GetPlotAreaCoordinate(object value) |
|
||||
{ |
|
||||
if (value == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException("value"); |
|
||||
} |
|
||||
|
|
||||
return GetPlotAreaCoordinate(value, ActualLength); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a given value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to return the plot area coordinate for.</param>
|
|
||||
/// <param name="length">The length of the axis.</param>
|
|
||||
/// <returns>The plot area coordinate of the given value.</returns>
|
|
||||
protected abstract UnitValue GetPlotAreaCoordinate(object value, double length); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the plot area coordinate of a given value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to return the plot area coordinate for.</param>
|
|
||||
/// <param name="currentRange">The value range to use when calculating the plot area coordinate.</param>
|
|
||||
/// <param name="length">The length of the axis.</param>
|
|
||||
/// <returns>The plot area coordinate of the given value.</returns>
|
|
||||
protected abstract UnitValue GetPlotAreaCoordinate(object value, Range<IComparable> currentRange, double length); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Overrides the data range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="range">The range to potentially override.</param>
|
|
||||
/// <returns>The overridden range.</returns>
|
|
||||
protected virtual Range<IComparable> OverrideDataRange(Range<IComparable> range) |
|
||||
{ |
|
||||
return range; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Modifies a range to respect the minimum and maximum axis values.
|
|
||||
/// </summary>
|
|
||||
/// <param name="range">The range of data.</param>
|
|
||||
/// <returns>A range modified to respect the minimum and maximum axis
|
|
||||
/// values.</returns>
|
|
||||
private Range<IComparable> EnforceMaximumAndMinimum(Range<IComparable> range) |
|
||||
{ |
|
||||
if (range.HasData) |
|
||||
{ |
|
||||
IComparable minimum = ProtectedMinimum ?? range.Minimum; |
|
||||
IComparable maximum = ProtectedMaximum ?? range.Maximum; |
|
||||
|
|
||||
if (ValueHelper.Compare(minimum, maximum) > 0) |
|
||||
{ |
|
||||
IComparable temp = maximum; |
|
||||
maximum = minimum; |
|
||||
minimum = temp; |
|
||||
} |
|
||||
|
|
||||
return new Range<IComparable>(minimum, maximum); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
IComparable minimum = ProtectedMinimum; |
|
||||
IComparable maximum = ProtectedMaximum; |
|
||||
if (ProtectedMinimum != null && ProtectedMaximum == null) |
|
||||
{ |
|
||||
maximum = minimum; |
|
||||
} |
|
||||
else if (ProtectedMaximum != null && ProtectedMinimum == null) |
|
||||
{ |
|
||||
minimum = maximum; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return range; |
|
||||
} |
|
||||
return new Range<IComparable>(minimum, maximum); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the actual range displayed on the axis.
|
|
||||
/// </summary>
|
|
||||
private void UpdateActualRange() |
|
||||
{ |
|
||||
Action action = () => |
|
||||
{ |
|
||||
Range<IComparable> dataRange; |
|
||||
if (ProtectedMaximum == null || ProtectedMinimum == null) |
|
||||
{ |
|
||||
if (Orientation == AxisOrientation.None) |
|
||||
{ |
|
||||
if (ProtectedMinimum != null) |
|
||||
{ |
|
||||
this.ActualRange = OverrideDataRange(new Range<IComparable>(ProtectedMinimum, ProtectedMinimum)); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.ActualRange = OverrideDataRange(new Range<IComparable>(ProtectedMaximum, ProtectedMaximum)); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataRange = |
|
||||
this.RegisteredListeners |
|
||||
.OfType<IRangeProvider>() |
|
||||
.Select(rangeProvider => rangeProvider.GetRange(this)) |
|
||||
.Sum(); |
|
||||
|
|
||||
this.ActualRange = OverrideDataRange(dataRange); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.ActualRange = new Range<IComparable>(ProtectedMinimum, ProtectedMaximum); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// Repeat this after layout pass.
|
|
||||
if (this.ActualLength == 0.0) |
|
||||
{ |
|
||||
this.Dispatcher.BeginInvoke(action); |
|
||||
} |
|
||||
|
|
||||
action(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis as an oriented axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
private void RenderOriented(Size availableSize) |
|
||||
{ |
|
||||
_minorTickMarkPool.Reset(); |
|
||||
_majorTickMarkPool.Reset(); |
|
||||
_labelPool.Reset(); |
|
||||
|
|
||||
double length = GetLength(availableSize); |
|
||||
try |
|
||||
{ |
|
||||
OrientedPanel.Children.Clear(); |
|
||||
if (ActualRange.HasData && !Object.Equals(ActualRange.Minimum, ActualRange.Maximum)) |
|
||||
{ |
|
||||
foreach (IComparable axisValue in GetMajorTickMarkValues(availableSize)) |
|
||||
{ |
|
||||
UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); |
|
||||
if (ValueHelper.CanGraph(coordinate.Value)) |
|
||||
{ |
|
||||
Line line = _majorTickMarkPool.Next(); |
|
||||
OrientedPanel.SetCenterCoordinate(line, coordinate.Value); |
|
||||
OrientedPanel.SetPriority(line, 0); |
|
||||
OrientedPanel.Children.Add(line); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
foreach (IComparable axisValue in GetMinorTickMarkValues(availableSize)) |
|
||||
{ |
|
||||
UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); |
|
||||
if (ValueHelper.CanGraph(coordinate.Value)) |
|
||||
{ |
|
||||
Line line = _minorTickMarkPool.Next(); |
|
||||
OrientedPanel.SetCenterCoordinate(line, coordinate.Value); |
|
||||
OrientedPanel.SetPriority(line, 0); |
|
||||
OrientedPanel.Children.Add(line); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
int count = 0; |
|
||||
foreach (IComparable axisValue in GetLabelValues(availableSize)) |
|
||||
{ |
|
||||
UnitValue coordinate = GetPlotAreaCoordinate(axisValue, length); |
|
||||
if (ValueHelper.CanGraph(coordinate.Value)) |
|
||||
{ |
|
||||
Control axisLabel = _labelPool.Next(); |
|
||||
PrepareAxisLabel(axisLabel, axisValue); |
|
||||
OrientedPanel.SetCenterCoordinate(axisLabel, coordinate.Value); |
|
||||
OrientedPanel.SetPriority(axisLabel, count + 1); |
|
||||
OrientedPanel.Children.Add(axisLabel); |
|
||||
count = (count + 1) % 2; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
_minorTickMarkPool.Done(); |
|
||||
_majorTickMarkPool.Done(); |
|
||||
_labelPool.Done(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Renders the axis labels, tick marks, and other visual elements.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
protected override void Render(Size availableSize) |
|
||||
{ |
|
||||
RenderOriented(availableSize); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a sequence of the major grid line coordinates.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>A sequence of the major grid line coordinates.</returns>
|
|
||||
protected override IEnumerable<UnitValue> GetMajorGridLineCoordinates(Size availableSize) |
|
||||
{ |
|
||||
return GetMajorTickMarkValues(availableSize).Select(value => GetPlotAreaCoordinate(value)).Where(value => ValueHelper.CanGraph(value.Value)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a sequence of the values at which to plot major grid lines.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size.</param>
|
|
||||
/// <returns>A sequence of the values at which to plot major grid lines.
|
|
||||
/// </returns>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "GridLine", Justification = "This is the expected capitalization.")] |
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] |
|
||||
protected virtual IEnumerable<IComparable> GetMajorGridLineValues(Size availableSize) |
|
||||
{ |
|
||||
return GetMajorTickMarkValues(availableSize); |
|
||||
} |
|
||||
|
|
||||
/// <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>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] |
|
||||
protected abstract IEnumerable<IComparable> GetMajorTickMarkValues(Size availableSize); |
|
||||
|
|
||||
/// <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>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] |
|
||||
protected virtual IEnumerable<IComparable> GetMinorTickMarkValues(Size availableSize) |
|
||||
{ |
|
||||
yield break; |
|
||||
} |
|
||||
|
|
||||
/// <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>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may do a lot of work and is therefore not a suitable candidate for a property.")] |
|
||||
protected abstract IEnumerable<IComparable> GetLabelValues(Size availableSize); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value range given a plot area coordinate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The plot area coordinate.</param>
|
|
||||
/// <returns>A range of values at that plot area coordinate.</returns>
|
|
||||
protected abstract IComparable GetValueAtPosition(UnitValue value); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual maximum value.
|
|
||||
/// </summary>
|
|
||||
Range<IComparable> IRangeAxis.Range |
|
||||
{ |
|
||||
get { return ActualRange; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value range given a plot area coordinate.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The plot area coordinate.</param>
|
|
||||
/// <returns>A range of values at that plot area coordinate.</returns>
|
|
||||
IComparable IRangeAxis.GetValueAtPosition(UnitValue value) |
|
||||
{ |
|
||||
return GetValueAtPosition(value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the axis with information about a provider's data range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="usesRangeAxis">The information provider.</param>
|
|
||||
/// <param name="range">The range of data in the information provider.
|
|
||||
/// </param>
|
|
||||
void IRangeConsumer.RangeChanged(IRangeProvider usesRangeAxis, Range<IComparable> range) |
|
||||
{ |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the layout of the axis to accommodate a sequence of value
|
|
||||
/// margins.
|
|
||||
/// </summary>
|
|
||||
/// <param name="provider">A value margin provider.</param>
|
|
||||
/// <param name="valueMargins">A sequence of value margins.</param>
|
|
||||
void IValueMarginConsumer.ValueMarginsChanged(IValueMarginProvider provider, IEnumerable<ValueMargin> valueMargins) |
|
||||
{ |
|
||||
Action action = () => |
|
||||
{ |
|
||||
if (this.Orientation != AxisOrientation.None) |
|
||||
{ |
|
||||
// Determine if any of the value margins are outside the axis
|
|
||||
// area. If so update range.
|
|
||||
bool updateRange = |
|
||||
valueMargins |
|
||||
.Select( |
|
||||
valueMargin => |
|
||||
{ |
|
||||
double coordinate = GetPlotAreaCoordinate(valueMargin.Value).Value; |
|
||||
return new Range<double>(coordinate - valueMargin.LowMargin, coordinate + valueMargin.HighMargin); |
|
||||
}) |
|
||||
.Where(range => range.Minimum < 0 || range.Maximum > this.ActualLength) |
|
||||
.Any(); |
|
||||
|
|
||||
if (updateRange) |
|
||||
{ |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// Repeat this after layout pass.
|
|
||||
if (this.ActualLength == 0) |
|
||||
{ |
|
||||
this.Dispatcher.BeginInvoke(action); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
action(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// If a new range provider is registered, update actual range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The axis listener being registered.</param>
|
|
||||
protected override void OnObjectRegistered(IAxisListener series) |
|
||||
{ |
|
||||
base.OnObjectRegistered(series); |
|
||||
if (series is IRangeProvider || series is IValueMarginProvider) |
|
||||
{ |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// If a range provider is unregistered, update actual range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The axis listener being unregistered.</param>
|
|
||||
protected override void OnObjectUnregistered(IAxisListener series) |
|
||||
{ |
|
||||
base.OnObjectUnregistered(series); |
|
||||
if (series is IRangeProvider || series is IValueMarginProvider) |
|
||||
{ |
|
||||
UpdateActualRange(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Create function that when given a range will return the
|
|
||||
/// amount in pixels by which the value margin range
|
|
||||
/// overlaps. Positive numbers represent values outside the
|
|
||||
/// range.
|
|
||||
/// </summary>
|
|
||||
/// <param name="valueMargins">The list of value margins, coordinates, and overlaps.</param>
|
|
||||
/// <param name="comparableRange">The new range to use to calculate coordinates.</param>
|
|
||||
internal void UpdateValueMargins(IList<ValueMarginCoordinateAndOverlap> valueMargins, Range<IComparable> comparableRange) |
|
||||
{ |
|
||||
double actualLength = this.ActualLength; |
|
||||
int valueMarginsCount = valueMargins.Count; |
|
||||
for (int count = 0; count < valueMarginsCount; count++) |
|
||||
{ |
|
||||
ValueMarginCoordinateAndOverlap item = valueMargins[count]; |
|
||||
item.Coordinate = GetPlotAreaCoordinate(item.ValueMargin.Value, comparableRange, actualLength).Value; |
|
||||
item.LeftOverlap = -(item.Coordinate - item.ValueMargin.LowMargin); |
|
||||
item.RightOverlap = (item.Coordinate + item.ValueMargin.HighMargin) - actualLength; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value margin, coordinate, and overlap triples that have the largest left and right overlap.
|
|
||||
/// </summary>
|
|
||||
/// <param name="valueMargins">The list of value margin, coordinate, and
|
|
||||
/// overlap triples.</param>
|
|
||||
/// <param name="maxLeftOverlapValueMargin">The value margin,
|
|
||||
/// coordinate, and overlap triple that has the largest left overlap.
|
|
||||
/// </param>
|
|
||||
/// <param name="maxRightOverlapValueMargin">The value margin,
|
|
||||
/// coordinate, and overlap triple that has the largest right overlap.
|
|
||||
/// </param>
|
|
||||
internal static void GetMaxLeftAndRightOverlap(IList<ValueMarginCoordinateAndOverlap> valueMargins, out ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin, out ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin) |
|
||||
{ |
|
||||
maxLeftOverlapValueMargin = new ValueMarginCoordinateAndOverlap(); |
|
||||
maxRightOverlapValueMargin = new ValueMarginCoordinateAndOverlap(); |
|
||||
double maxLeftOverlap = double.MinValue; |
|
||||
double maxRightOverlap = double.MinValue; |
|
||||
int valueMarginsCount = valueMargins.Count; |
|
||||
for (int cnt = 0; cnt < valueMarginsCount; cnt++) |
|
||||
{ |
|
||||
ValueMarginCoordinateAndOverlap valueMargin = valueMargins[cnt]; |
|
||||
double leftOverlap = valueMargin.LeftOverlap; |
|
||||
if (leftOverlap > maxLeftOverlap) |
|
||||
{ |
|
||||
maxLeftOverlap = leftOverlap; |
|
||||
maxLeftOverlapValueMargin = valueMargin; |
|
||||
} |
|
||||
double rightOverlap = valueMargin.RightOverlap; |
|
||||
if (rightOverlap > maxRightOverlap) |
|
||||
{ |
|
||||
maxRightOverlap = rightOverlap; |
|
||||
maxRightOverlapValueMargin = valueMargin; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the origin value on the axis.
|
|
||||
/// </summary>
|
|
||||
IComparable IRangeAxis.Origin { get { return this.Origin; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the origin value on the axis.
|
|
||||
/// </summary>
|
|
||||
protected abstract IComparable Origin { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,90 +0,0 @@ |
|||||
// (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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,747 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Controls.DataVisualization.Charting.Primitives; |
|
||||
using System.Windows.Markup; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that displays a Chart.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[TemplatePart(Name = Chart.ChartAreaName, Type = typeof(EdgePanel))] |
|
||||
[TemplatePart(Name = Chart.LegendName, Type = typeof(Legend))] |
|
||||
[StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))] |
|
||||
[StyleTypedProperty(Property = "LegendStyle", StyleTargetType = typeof(Legend))] |
|
||||
[StyleTypedProperty(Property = "ChartAreaStyle", StyleTargetType = typeof(EdgePanel))] |
|
||||
[StyleTypedProperty(Property = "PlotAreaStyle", StyleTargetType = typeof(Grid))] |
|
||||
[ContentProperty("Series")] |
|
||||
public partial class Chart : Control, ISeriesHost |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Specifies the name of the ChartArea TemplatePart.
|
|
||||
/// </summary>
|
|
||||
private const string ChartAreaName = "ChartArea"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Specifies the name of the legend TemplatePart.
|
|
||||
/// </summary>
|
|
||||
private const string LegendName = "Legend"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the chart area children collection.
|
|
||||
/// </summary>
|
|
||||
private AggregatedObservableCollection<UIElement> ChartAreaChildren { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// An adapter that synchronizes changes to the ChartAreaChildren
|
|
||||
/// property to the ChartArea panel's children collection.
|
|
||||
/// </summary>
|
|
||||
private ObservableCollectionListAdapter<UIElement> _chartAreaChildrenListAdapter = new ObservableCollectionListAdapter<UIElement>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a collection of Axes in the Chart.
|
|
||||
/// </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<IAxis> Axes |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _axes; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
throw new NotSupportedException(Properties.Resources.Chart_Axes_SetterNotSupported); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the collection of Axes in the Chart.
|
|
||||
/// </summary>
|
|
||||
private Collection<IAxis> _axes; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The collection of foreground elements.
|
|
||||
/// </summary>
|
|
||||
private ObservableCollection<UIElement> _foregroundElements = new NoResetObservableCollection<UIElement>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The collection of background elements.
|
|
||||
/// </summary>
|
|
||||
private ObservableCollection<UIElement> _backgroundElements = new NoResetObservableCollection<UIElement>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of foreground elements.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<UIElement> ISeriesHost.ForegroundElements { get { return ForegroundElements; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of foreground elements.
|
|
||||
/// </summary>
|
|
||||
protected ObservableCollection<UIElement> ForegroundElements { get { return _foregroundElements; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of background elements.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<UIElement> ISeriesHost.BackgroundElements { get { return BackgroundElements; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of background elements.
|
|
||||
/// </summary>
|
|
||||
protected ObservableCollection<UIElement> BackgroundElements { get { return _backgroundElements; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Axes arranged along the edges.
|
|
||||
/// </summary>
|
|
||||
private ObservableCollection<Axis> _edgeAxes = new NoResetObservableCollection<Axis>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the axes that are currently in the chart.
|
|
||||
/// </summary>
|
|
||||
private IList<IAxis> InternalActualAxes { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the actual axes displayed in the chart.
|
|
||||
/// </summary>
|
|
||||
public ReadOnlyCollection<IAxis> ActualAxes { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the reference to the template's ChartArea.
|
|
||||
/// </summary>
|
|
||||
private EdgePanel ChartArea { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the reference to the Chart's Legend.
|
|
||||
/// </summary>
|
|
||||
private Legend Legend { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the collection of Series displayed by the Chart.
|
|
||||
/// </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<ISeries> Series |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _series; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
throw new NotSupportedException(Properties.Resources.Chart_Series_SetterNotSupported); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the collection of Series displayed by the Chart.
|
|
||||
/// </summary>
|
|
||||
private Collection<ISeries> _series; |
|
||||
|
|
||||
#region public Style ChartAreaStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style of the ISeriesHost's ChartArea.
|
|
||||
/// </summary>
|
|
||||
public Style ChartAreaStyle |
|
||||
{ |
|
||||
get { return GetValue(ChartAreaStyleProperty) as Style; } |
|
||||
set { SetValue(ChartAreaStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ChartAreaStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ChartAreaStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ChartAreaStyle", |
|
||||
typeof(Style), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion public Style ChartAreaStyle
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of legend items.
|
|
||||
/// </summary>
|
|
||||
public Collection<object> LegendItems { get; private set; } |
|
||||
|
|
||||
#region public Style LegendStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style of the ISeriesHost's Legend.
|
|
||||
/// </summary>
|
|
||||
public Style LegendStyle |
|
||||
{ |
|
||||
get { return GetValue(LegendStyleProperty) as Style; } |
|
||||
set { SetValue(LegendStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"LegendStyle", |
|
||||
typeof(Style), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion public Style LegendStyle
|
|
||||
|
|
||||
#region public object LegendTitle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Title content of the Legend.
|
|
||||
/// </summary>
|
|
||||
public object LegendTitle |
|
||||
{ |
|
||||
get { return GetValue(LegendTitleProperty); } |
|
||||
set { SetValue(LegendTitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendTitle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendTitleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"LegendTitle", |
|
||||
typeof(object), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion public object LegendTitle
|
|
||||
|
|
||||
#region public Style PlotAreaStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style of the ISeriesHost's PlotArea.
|
|
||||
/// </summary>
|
|
||||
public Style PlotAreaStyle |
|
||||
{ |
|
||||
get { return GetValue(PlotAreaStyleProperty) as Style; } |
|
||||
set { SetValue(PlotAreaStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the PlotAreaStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PlotAreaStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"PlotAreaStyle", |
|
||||
typeof(Style), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion public Style PlotAreaStyle
|
|
||||
|
|
||||
#region public Collection<ResourceDictionary> Palette
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a palette of ResourceDictionaries used by the children of the Chart.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Want to allow this to be set from XAML.")] |
|
||||
public Collection<ResourceDictionary> Palette |
|
||||
{ |
|
||||
get { return GetValue(PaletteProperty) as Collection<ResourceDictionary>; } |
|
||||
set { SetValue(PaletteProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Palette dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PaletteProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Palette", |
|
||||
typeof(Collection<ResourceDictionary>), |
|
||||
typeof(Chart), |
|
||||
new PropertyMetadata(OnPalettePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the Palette property is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Chart that contains the changed Palette.
|
|
||||
/// </param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnPalettePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
Chart source = (Chart) d; |
|
||||
Collection<ResourceDictionary> newValue = (Collection<ResourceDictionary>)e.NewValue; |
|
||||
source.OnPalettePropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the Palette property is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">The new value for the Palette.</param>
|
|
||||
private void OnPalettePropertyChanged(Collection<ResourceDictionary> newValue) |
|
||||
{ |
|
||||
ResourceDictionaryDispenser.ResourceDictionaries = newValue; |
|
||||
} |
|
||||
#endregion public Collection<ResourceDictionary> Palette
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets an object that rotates through the palette.
|
|
||||
/// </summary>
|
|
||||
private ResourceDictionaryDispenser ResourceDictionaryDispenser { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Event that is invoked when the ResourceDictionaryDispenser's collection has changed.
|
|
||||
/// </summary>
|
|
||||
public event EventHandler ResourceDictionariesChanged; |
|
||||
|
|
||||
#region public object Title
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title displayed for the Chart.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Title", |
|
||||
typeof(object), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion
|
|
||||
|
|
||||
#region public Style TitleStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style of the ISeriesHost's Title.
|
|
||||
/// </summary>
|
|
||||
public Style TitleStyle |
|
||||
{ |
|
||||
get { return GetValue(TitleStyleProperty) as Style; } |
|
||||
set { SetValue(TitleStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TitleStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"TitleStyle", |
|
||||
typeof(Style), |
|
||||
typeof(Chart), |
|
||||
null); |
|
||||
#endregion public Style TitleStyle
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the Chart class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static Chart() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(Chart), new FrameworkPropertyMetadata(typeof(Chart))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Chart class.
|
|
||||
/// </summary>
|
|
||||
public Chart() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
DefaultStyleKey = typeof(Chart); |
|
||||
#endif
|
|
||||
// Create the backing collection for Series
|
|
||||
UniqueObservableCollection<ISeries> series = new UniqueObservableCollection<ISeries>(); |
|
||||
series.CollectionChanged += new NotifyCollectionChangedEventHandler(SeriesCollectionChanged); |
|
||||
_series = series; |
|
||||
|
|
||||
// Create the backing collection for Axes
|
|
||||
UniqueObservableCollection<IAxis> axes = new UniqueObservableCollection<IAxis>(); |
|
||||
_axes = axes; |
|
||||
|
|
||||
ObservableCollection<IAxis> actualAxes = new SeriesHostAxesCollection(this, axes); |
|
||||
actualAxes.CollectionChanged += ActualAxesCollectionChanged; |
|
||||
this.InternalActualAxes = actualAxes; |
|
||||
this.ActualAxes = new ReadOnlyCollection<IAxis>(InternalActualAxes); |
|
||||
|
|
||||
// Create collection for LegendItems
|
|
||||
LegendItems = new AggregatedObservableCollection<object>(); |
|
||||
|
|
||||
ChartAreaChildren = new AggregatedObservableCollection<UIElement>(); |
|
||||
ChartAreaChildren.ChildCollections.Add(_edgeAxes); |
|
||||
ChartAreaChildren.ChildCollections.Add(_backgroundElements); |
|
||||
ChartAreaChildren.ChildCollections.Add(Series); |
|
||||
ChartAreaChildren.ChildCollections.Add(_foregroundElements); |
|
||||
|
|
||||
_chartAreaChildrenListAdapter.Collection = ChartAreaChildren; |
|
||||
|
|
||||
// Create a dispenser
|
|
||||
ResourceDictionaryDispenser = new ResourceDictionaryDispenser(); |
|
||||
ResourceDictionaryDispenser.ResourceDictionariesChanged += delegate |
|
||||
{ |
|
||||
OnResourceDictionariesChanged(EventArgs.Empty); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Invokes the ResourceDictionariesChanged event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void OnResourceDictionariesChanged(EventArgs e) |
|
||||
{ |
|
||||
// Forward event on to listeners
|
|
||||
EventHandler handler = ResourceDictionariesChanged; |
|
||||
if (null != handler) |
|
||||
{ |
|
||||
handler.Invoke(this, e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines the location of an axis based on the existing axes in
|
|
||||
/// the chart.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis to determine the location of.</param>
|
|
||||
/// <returns>The location of the axis.</returns>
|
|
||||
private AxisLocation GetAutoAxisLocation(Axis axis) |
|
||||
{ |
|
||||
if (axis.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
int numberOfTopAxes = InternalActualAxes.OfType<Axis>().Where(currentAxis => currentAxis.Location == AxisLocation.Top).Count(); |
|
||||
int numberOfBottomAxes = InternalActualAxes.OfType<Axis>().Where(currentAxis => currentAxis.Location == AxisLocation.Bottom).Count(); |
|
||||
return (numberOfBottomAxes > numberOfTopAxes) ? AxisLocation.Top : AxisLocation.Bottom; |
|
||||
} |
|
||||
else if (axis.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
int numberOfLeftAxes = InternalActualAxes.OfType<Axis>().Where(currentAxis => currentAxis.Location == AxisLocation.Left).Count(); |
|
||||
int numberOfRightAxes = InternalActualAxes.OfType<Axis>().Where(currentAxis => currentAxis.Location == AxisLocation.Right).Count(); |
|
||||
return (numberOfLeftAxes > numberOfRightAxes) ? AxisLocation.Right : AxisLocation.Left; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return AxisLocation.Auto; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds an axis to the ISeriesHost area.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis to add to the ISeriesHost area.</param>
|
|
||||
private void AddAxisToChartArea(Axis axis) |
|
||||
{ |
|
||||
IRequireSeriesHost requiresSeriesHost = axis as IRequireSeriesHost; |
|
||||
if (requiresSeriesHost != null) |
|
||||
{ |
|
||||
requiresSeriesHost.SeriesHost = this; |
|
||||
} |
|
||||
|
|
||||
if (axis.Location == AxisLocation.Auto) |
|
||||
{ |
|
||||
axis.Location = GetAutoAxisLocation(axis); |
|
||||
} |
|
||||
|
|
||||
SetEdge(axis); |
|
||||
|
|
||||
axis.LocationChanged += AxisLocationChanged; |
|
||||
axis.OrientationChanged += AxisOrientationChanged; |
|
||||
|
|
||||
if (axis.Location != AxisLocation.Auto) |
|
||||
{ |
|
||||
_edgeAxes.Add(axis); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rebuilds the chart area if an axis orientation is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void AxisOrientationChanged(object sender, RoutedPropertyChangedEventArgs<AxisOrientation> args) |
|
||||
{ |
|
||||
Axis axis = (Axis)sender; |
|
||||
|
|
||||
axis.Location = GetAutoAxisLocation(axis); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the Edge property of an axis based on its location and
|
|
||||
/// orientation.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis to set the edge property of.</param>
|
|
||||
private static void SetEdge(Axis axis) |
|
||||
{ |
|
||||
switch (axis.Location) |
|
||||
{ |
|
||||
case AxisLocation.Bottom: |
|
||||
EdgePanel.SetEdge(axis, Edge.Bottom); |
|
||||
break; |
|
||||
case AxisLocation.Top: |
|
||||
EdgePanel.SetEdge(axis, Edge.Top); |
|
||||
break; |
|
||||
case AxisLocation.Left: |
|
||||
EdgePanel.SetEdge(axis, Edge.Left); |
|
||||
break; |
|
||||
case AxisLocation.Right: |
|
||||
EdgePanel.SetEdge(axis, Edge.Right); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rebuild the chart area if an axis location is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void AxisLocationChanged(object sender, RoutedPropertyChangedEventArgs<AxisLocation> args) |
|
||||
{ |
|
||||
Axis axis = (Axis)sender; |
|
||||
|
|
||||
if (args.NewValue == AxisLocation.Auto) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.Chart_AxisLocationChanged_CantBeChangedToAutoWhenHostedInsideOfASeriesHost); |
|
||||
} |
|
||||
|
|
||||
SetEdge(axis); |
|
||||
|
|
||||
_edgeAxes.Remove(axis); |
|
||||
_edgeAxes.Add(axis); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds a series to the plot area and injects chart services.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series to add to the plot area.</param>
|
|
||||
private void AddSeriesToPlotArea(ISeries series) |
|
||||
{ |
|
||||
series.SeriesHost = this; |
|
||||
|
|
||||
AggregatedObservableCollection<object> chartLegendItems = this.LegendItems as AggregatedObservableCollection<object>; |
|
||||
int indexOfSeries = this.Series.IndexOf(series); |
|
||||
chartLegendItems.ChildCollections.Insert(indexOfSeries, series.LegendItems); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Builds the visual tree for the Chart control when a new template
|
|
||||
/// is applied.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
// Call base implementation
|
|
||||
base.OnApplyTemplate(); |
|
||||
|
|
||||
// Unhook events from former template parts
|
|
||||
if (null != ChartArea) |
|
||||
{ |
|
||||
ChartArea.Children.Clear(); |
|
||||
} |
|
||||
|
|
||||
if (null != Legend) |
|
||||
{ |
|
||||
Legend.ItemsSource = null; |
|
||||
} |
|
||||
|
|
||||
// Access new template parts
|
|
||||
ChartArea = GetTemplateChild(ChartAreaName) as EdgePanel; |
|
||||
|
|
||||
Legend = GetTemplateChild(LegendName) as Legend; |
|
||||
|
|
||||
if (ChartArea != null) |
|
||||
{ |
|
||||
_chartAreaChildrenListAdapter.TargetList = ChartArea.Children; |
|
||||
_chartAreaChildrenListAdapter.Populate(); |
|
||||
} |
|
||||
|
|
||||
if (Legend != null) |
|
||||
{ |
|
||||
Legend.ItemsSource = this.LegendItems; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Ensures that ISeriesHost is in a consistent state when axes collection is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void ActualAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (e.NewItems != null) |
|
||||
{ |
|
||||
foreach (Axis axis in e.NewItems.OfType<Axis>()) |
|
||||
{ |
|
||||
AddAxisToChartArea(axis); |
|
||||
} |
|
||||
} |
|
||||
if (e.OldItems != null) |
|
||||
{ |
|
||||
foreach (Axis axis in e.OldItems.OfType<Axis>()) |
|
||||
{ |
|
||||
RemoveAxisFromChartArea(axis); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes an axis from the Chart area.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">The axis to remove from the ISeriesHost area.</param>
|
|
||||
private void RemoveAxisFromChartArea(Axis axis) |
|
||||
{ |
|
||||
axis.LocationChanged -= AxisLocationChanged; |
|
||||
axis.OrientationChanged -= AxisOrientationChanged; |
|
||||
IRequireSeriesHost requiresSeriesHost = axis as IRequireSeriesHost; |
|
||||
if (requiresSeriesHost != null) |
|
||||
{ |
|
||||
requiresSeriesHost.SeriesHost = null; |
|
||||
} |
|
||||
|
|
||||
_edgeAxes.Remove(axis); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes a series from the plot area.
|
|
||||
/// </summary>
|
|
||||
/// <param name="series">The series to remove from the plot area.
|
|
||||
/// </param>
|
|
||||
private void RemoveSeriesFromPlotArea(ISeries series) |
|
||||
{ |
|
||||
AggregatedObservableCollection<object> legendItemsList = LegendItems as AggregatedObservableCollection<object>; |
|
||||
legendItemsList.ChildCollections.Remove(series.LegendItems); |
|
||||
|
|
||||
series.SeriesHost = null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the ObservableCollection.CollectionChanged property
|
|
||||
/// changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The object that raised the event.</param>
|
|
||||
/// <param name="e">The event data.</param>
|
|
||||
private void SeriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
// Clear ISeriesHost property of old Series
|
|
||||
if (null != e.OldItems) |
|
||||
{ |
|
||||
foreach (ISeries series in e.OldItems) |
|
||||
{ |
|
||||
ISeriesHost host = series as ISeriesHost; |
|
||||
if (host != null) |
|
||||
{ |
|
||||
foreach (IRequireGlobalSeriesIndex tracksGlobalIndex in host.GetDescendentSeries().OfType<IRequireGlobalSeriesIndex>()) |
|
||||
{ |
|
||||
tracksGlobalIndex.GlobalSeriesIndexChanged(null); |
|
||||
} |
|
||||
host.Series.CollectionChanged -= new NotifyCollectionChangedEventHandler(ChildSeriesCollectionChanged); |
|
||||
} |
|
||||
IRequireGlobalSeriesIndex require = series as IRequireGlobalSeriesIndex; |
|
||||
if (require != null) |
|
||||
{ |
|
||||
require.GlobalSeriesIndexChanged(null); |
|
||||
} |
|
||||
|
|
||||
RemoveSeriesFromPlotArea(series); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Set ISeriesHost property of new Series
|
|
||||
if (null != e.NewItems) |
|
||||
{ |
|
||||
foreach (ISeries series in e.NewItems) |
|
||||
{ |
|
||||
ISeriesHost host = series as ISeriesHost; |
|
||||
if (null != host) |
|
||||
{ |
|
||||
host.Series.CollectionChanged += new NotifyCollectionChangedEventHandler(ChildSeriesCollectionChanged); |
|
||||
} |
|
||||
AddSeriesToPlotArea(series); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (e.Action != NotifyCollectionChangedAction.Replace) |
|
||||
{ |
|
||||
OnGlobalSeriesIndexesInvalidated(this, new RoutedEventArgs()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the collections of child ISeries implementing ISeriesHost.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void ChildSeriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
OnGlobalSeriesIndexesInvalidated(this, new RoutedEventArgs()); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a rotating enumerator of ResourceDictionary objects that coordinates
|
|
||||
/// with the dispenser object to ensure that no two enumerators are on the same
|
|
||||
/// item. If the dispenser is reset or its collection is changed then the
|
|
||||
/// enumerators are also reset.
|
|
||||
/// </summary>
|
|
||||
/// <param name="predicate">A predicate that returns a value indicating
|
|
||||
/// whether to return an item.</param>
|
|
||||
/// <returns>An enumerator of ResourceDictionaries.</returns>
|
|
||||
public IEnumerator<ResourceDictionary> GetResourceDictionariesWhere(Func<ResourceDictionary, bool> predicate) |
|
||||
{ |
|
||||
return ResourceDictionaryDispenser.GetResourceDictionariesWhere(predicate); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the global indexes of all descendents that require a global
|
|
||||
/// index.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">The event data.</param>
|
|
||||
private void OnGlobalSeriesIndexesInvalidated(object sender, RoutedEventArgs args) |
|
||||
{ |
|
||||
UpdateGlobalIndexes(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the global index property of all Series that track their
|
|
||||
/// global index.
|
|
||||
/// </summary>
|
|
||||
private void UpdateGlobalIndexes() |
|
||||
{ |
|
||||
(this as ISeriesHost).GetDescendentSeries().OfType<IRequireGlobalSeriesIndex>().ForEachWithIndex( |
|
||||
(seriesThatTracksGlobalIndex, index) => |
|
||||
{ |
|
||||
seriesThatTracksGlobalIndex.GlobalSeriesIndexChanged(index); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Series host of the chart.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>This will always return null.</remarks>
|
|
||||
ISeriesHost IRequireSeriesHost.SeriesHost |
|
||||
{ |
|
||||
get { return SeriesHost; } |
|
||||
set { SeriesHost = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Series host of the chart.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>This will always return null.</remarks>
|
|
||||
protected ISeriesHost SeriesHost { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the axes collection of the chart.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<IAxis> ISeriesHost.Axes |
|
||||
{ |
|
||||
get { return InternalActualAxes as ObservableCollection<IAxis>; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the Series collection of the chart.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<ISeries> ISeriesHost.Series |
|
||||
{ |
|
||||
get { return (ObservableCollection<ISeries>)Series; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,152 +0,0 @@ |
|||||
// (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.Specialized; |
|
||||
using System.Diagnostics; |
|
||||
using System.Linq; |
|
||||
using System.Windows; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An axes collection used by a series host.
|
|
||||
/// </summary>
|
|
||||
internal class SeriesHostAxesCollection : UniqueObservableCollection<IAxis> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the series host field.
|
|
||||
/// </summary>
|
|
||||
private ISeriesHost SeriesHost { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a collection of axes cannot be removed under any
|
|
||||
/// circumstances.
|
|
||||
/// </summary>
|
|
||||
private UniqueObservableCollection<IAxis> PersistentAxes { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the SeriesHostAxesCollection class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="seriesHost">The series host.</param>
|
|
||||
internal SeriesHostAxesCollection(ISeriesHost seriesHost) |
|
||||
{ |
|
||||
this.SeriesHost = seriesHost; |
|
||||
this.PersistentAxes = new UniqueObservableCollection<IAxis>(); |
|
||||
this.CollectionChanged += ThisCollectionChanged; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Instantiates a new instance of the SeriesHostAxesCollection class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="seriesHost">The series host.</param>
|
|
||||
/// <param name="persistentAxes">A collection of axes that can never be
|
|
||||
/// removed from the chart.</param>
|
|
||||
internal SeriesHostAxesCollection(ISeriesHost seriesHost, UniqueObservableCollection<IAxis> persistentAxes) |
|
||||
: this(seriesHost) |
|
||||
{ |
|
||||
Debug.Assert(persistentAxes != null, "Persistent axes collection cannot be null."); |
|
||||
this.SeriesHost = seriesHost; |
|
||||
this.PersistentAxes = persistentAxes; |
|
||||
this.PersistentAxes.CollectionChanged += PersistentAxesCollectionChanged; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A method that attaches and removes listeners to axes added to this
|
|
||||
/// collection.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">This object.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void ThisCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (e.NewItems != null) |
|
||||
{ |
|
||||
foreach (IAxis axis in e.NewItems) |
|
||||
{ |
|
||||
axis.RegisteredListeners.CollectionChanged += AxisRegisteredListenersCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
if (e.OldItems != null) |
|
||||
{ |
|
||||
foreach (IAxis axis in e.OldItems) |
|
||||
{ |
|
||||
axis.RegisteredListeners.CollectionChanged -= AxisRegisteredListenersCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Remove an axis from the collection if it is no longer used.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The axis that has had its registered
|
|
||||
/// listeners collection changed.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void AxisRegisteredListenersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
IAxis axis = this.Where(currentAxis => currentAxis.RegisteredListeners == sender).First(); |
|
||||
|
|
||||
if (e.OldItems != null) |
|
||||
{ |
|
||||
if (!PersistentAxes.Contains(axis) && !SeriesHost.IsUsedByASeries(axis)) |
|
||||
{ |
|
||||
this.Remove(axis); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method synchronizes the collection with the persistent axes
|
|
||||
/// collection when it is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
public void PersistentAxesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (e.NewItems != null) |
|
||||
{ |
|
||||
foreach (IAxis axis in e.NewItems) |
|
||||
{ |
|
||||
if (!this.Contains(axis)) |
|
||||
{ |
|
||||
this.Add(axis); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
if (e.OldItems != null) |
|
||||
{ |
|
||||
foreach (IAxis axis in e.OldItems) |
|
||||
{ |
|
||||
if (this.Contains(axis) && !SeriesHost.IsUsedByASeries(axis)) |
|
||||
{ |
|
||||
this.Remove(axis); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes an item from the axes collection but throws an exception
|
|
||||
/// if a series in the series host is listening to it.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The index of the item being removed.</param>
|
|
||||
protected override void RemoveItem(int index) |
|
||||
{ |
|
||||
IAxis axis = this[index]; |
|
||||
|
|
||||
if (SeriesHost.IsUsedByASeries(axis)) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.SeriesHostAxesCollection_RemoveItem_AxisCannotBeRemovedFromASeriesHostWhenOneOrMoreSeriesAreListeningToIt); |
|
||||
} |
|
||||
else if (PersistentAxes.Contains(axis)) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.SeriesHostAxesCollection_InvalidAttemptToRemovePermanentAxisFromSeriesHost); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
base.RemoveItem(index); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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 an area 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 AreaDataPoint : DataPoint |
|
||||
{ |
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the AreaDataPoint class.
|
|
||||
/// </summary>
|
|
||||
static AreaDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(AreaDataPoint), new FrameworkPropertyMetadata(typeof(AreaDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the AreaDataPoint class.
|
|
||||
/// </summary>
|
|
||||
public AreaDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(AreaDataPoint); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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 bar 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 BarDataPoint : DataPoint |
|
||||
{ |
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the BarDataPoint class.
|
|
||||
/// </summary>
|
|
||||
static BarDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(BarDataPoint), new FrameworkPropertyMetadata(typeof(BarDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the BarDataPoint class.
|
|
||||
/// </summary>
|
|
||||
public BarDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(BarDataPoint); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,156 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a data point used for a bubble 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 class BubbleDataPoint : DataPoint |
|
||||
{ |
|
||||
#region public double Size
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the size value of the bubble data point.
|
|
||||
/// </summary>
|
|
||||
public double Size |
|
||||
{ |
|
||||
get { return (double)GetValue(SizeProperty); } |
|
||||
set { SetValue(SizeProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Size dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty SizeProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Size", |
|
||||
typeof(double), |
|
||||
typeof(BubbleDataPoint), |
|
||||
new PropertyMetadata(0.0, OnSizePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SizeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">BubbleDataPoint that changed its Size.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
BubbleDataPoint source = (BubbleDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnSizePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SizeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnSizePropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<double> handler = SizePropertyChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
if (this.State == DataPointState.Created) |
|
||||
{ |
|
||||
this.ActualSize = newValue; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the size property is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> SizePropertyChanged; |
|
||||
|
|
||||
#endregion public double Size
|
|
||||
|
|
||||
#region public double ActualSize
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual size of the bubble data point.
|
|
||||
/// </summary>
|
|
||||
public double ActualSize |
|
||||
{ |
|
||||
get { return (double)GetValue(ActualSizeProperty); } |
|
||||
set { SetValue(ActualSizeProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualSize dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualSizeProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualSize", |
|
||||
typeof(double), |
|
||||
typeof(BubbleDataPoint), |
|
||||
new PropertyMetadata(0.0, OnActualSizePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ActualSizeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">BubbleDataPoint that changed its ActualSize.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnActualSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
BubbleDataPoint source = (BubbleDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnActualSizePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ActualSizeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnActualSizePropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<double> handler = ActualSizePropertyChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the actual size property is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> ActualSizePropertyChanged; |
|
||||
|
|
||||
#endregion public double ActualSize
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the BubbleDataPoint class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static BubbleDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(BubbleDataPoint), new FrameworkPropertyMetadata(typeof(BubbleDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the bubble data point.
|
|
||||
/// </summary>
|
|
||||
public BubbleDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(BubbleDataPoint); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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 column 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 ColumnDataPoint : DataPoint |
|
||||
{ |
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the ColumnDataPoint class.
|
|
||||
/// </summary>
|
|
||||
static ColumnDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColumnDataPoint), new FrameworkPropertyMetadata(typeof(ColumnDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ColumnDataPoint class.
|
|
||||
/// </summary>
|
|
||||
public ColumnDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(ColumnDataPoint); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,996 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Input; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Threading; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that displays a data point.
|
|
||||
/// </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 abstract partial class DataPoint : Control |
|
||||
{ |
|
||||
#region CommonStates
|
|
||||
/// <summary>
|
|
||||
/// Common state group.
|
|
||||
/// </summary>
|
|
||||
internal const string GroupCommonStates = "CommonStates"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Normal state of the Common group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateCommonNormal = "Normal"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// MouseOver state of the Common group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateCommonMouseOver = "MouseOver"; |
|
||||
#endregion CommonStates
|
|
||||
|
|
||||
#region SelectionStates
|
|
||||
/// <summary>
|
|
||||
/// Selection state group.
|
|
||||
/// </summary>
|
|
||||
internal const string GroupSelectionStates = "SelectionStates"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Unselected state of the Selection group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateSelectionUnselected = "Unselected"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Selected state of the Selection group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateSelectionSelected = "Selected"; |
|
||||
#endregion SelectionStates
|
|
||||
|
|
||||
#region GroupRevealStates
|
|
||||
/// <summary>
|
|
||||
/// Reveal state group.
|
|
||||
/// </summary>
|
|
||||
internal const string GroupRevealStates = "RevealStates"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Shown state of the Reveal group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateRevealShown = "Shown"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Hidden state of the Reveal group.
|
|
||||
/// </summary>
|
|
||||
internal const string StateRevealHidden = "Hidden"; |
|
||||
#endregion GroupRevealStates
|
|
||||
|
|
||||
#region public bool IsSelectionEnabled
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IsSelectionEnabled", |
|
||||
typeof(bool), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(false, OnIsSelectionEnabledPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsSelectionEnabledProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its IsSelectionEnabled.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIsSelectionEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
bool oldValue = (bool)e.OldValue; |
|
||||
bool newValue = (bool)e.NewValue; |
|
||||
source.OnIsSelectionEnabledPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsSelectionEnabledProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnIsSelectionEnabledPropertyChanged(bool oldValue, bool newValue) |
|
||||
{ |
|
||||
if (newValue == false) |
|
||||
{ |
|
||||
IsSelected = false; |
|
||||
IsHovered = false; |
|
||||
} |
|
||||
} |
|
||||
#endregion public bool IsSelectionEnabled
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the data point is active.
|
|
||||
/// </summary>
|
|
||||
internal bool IsActive { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// An event raised when the IsSelected property is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<bool> IsSelectedChanged; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A value indicating whether the mouse is hovering over the data
|
|
||||
/// point.
|
|
||||
/// </summary>
|
|
||||
private bool _isHovered; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the mouse is hovering over
|
|
||||
/// the data point.
|
|
||||
/// </summary>
|
|
||||
protected bool IsHovered |
|
||||
{ |
|
||||
get { return _isHovered; } |
|
||||
private set |
|
||||
{ |
|
||||
bool oldValue = _isHovered; |
|
||||
_isHovered = value; |
|
||||
if (oldValue != _isHovered) |
|
||||
{ |
|
||||
OnIsHoveredPropertyChanged(oldValue, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsHoveredProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected virtual void OnIsHoveredPropertyChanged(bool oldValue, bool newValue) |
|
||||
{ |
|
||||
VisualStateManager.GoToState(this, (newValue == true) ? StateCommonMouseOver : StateCommonNormal, true); |
|
||||
} |
|
||||
|
|
||||
#region internal bool IsSelected
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether the data point is selected.
|
|
||||
/// </summary>
|
|
||||
internal bool IsSelected |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectedProperty); } |
|
||||
set { SetValue(IsSelectedProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelected dependency property.
|
|
||||
/// </summary>
|
|
||||
internal static readonly DependencyProperty IsSelectedProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IsSelected", |
|
||||
typeof(bool), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(false, OnIsSelectedPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsSelectedProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its IsSelected.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
bool oldValue = (bool)e.OldValue; |
|
||||
bool newValue = (bool)e.NewValue; |
|
||||
source.OnIsSelectedPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsSelectedProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnIsSelectedPropertyChanged(bool oldValue, bool newValue) |
|
||||
{ |
|
||||
VisualStateManager.GoToState(this, newValue ? StateSelectionSelected : StateSelectionUnselected, true); |
|
||||
|
|
||||
RoutedPropertyChangedEventHandler<bool> handler = this.IsSelectedChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<bool>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
#endregion internal bool IsSelected
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Event raised when the actual dependent value of the data point is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<IComparable> ActualDependentValueChanged; |
|
||||
|
|
||||
#region public IComparable ActualDependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual dependent value displayed in the chart.
|
|
||||
/// </summary>
|
|
||||
public IComparable ActualDependentValue |
|
||||
{ |
|
||||
get { return (IComparable)GetValue(ActualDependentValueProperty); } |
|
||||
set { SetValue(ActualDependentValueProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly System.Windows.DependencyProperty ActualDependentValueProperty = |
|
||||
System.Windows.DependencyProperty.Register( |
|
||||
"ActualDependentValue", |
|
||||
typeof(IComparable), |
|
||||
typeof(DataPoint), |
|
||||
new System.Windows.PropertyMetadata(0.0, OnActualDependentValuePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualDependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its ActualDependentValue.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnActualDependentValuePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
IComparable oldValue = (IComparable)e.OldValue; |
|
||||
IComparable newValue = (IComparable)e.NewValue; |
|
||||
source.OnActualDependentValuePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A value indicating whether the actual independent value is being
|
|
||||
/// coerced.
|
|
||||
/// </summary>
|
|
||||
private bool _isCoercingActualDependentValue; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The preserved previous actual dependent value before coercion.
|
|
||||
/// </summary>
|
|
||||
private IComparable _oldActualDependentValueBeforeCoercion; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualDependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnActualDependentValuePropertyChanged(IComparable oldValue, IComparable newValue) |
|
||||
{ |
|
||||
double coercedValue = 0.0; |
|
||||
if (!(newValue is double) && ValueHelper.TryConvert(newValue, out coercedValue)) |
|
||||
{ |
|
||||
_isCoercingActualDependentValue = true; |
|
||||
_oldActualDependentValueBeforeCoercion = oldValue; |
|
||||
} |
|
||||
|
|
||||
if (!_isCoercingActualDependentValue) |
|
||||
{ |
|
||||
if (_oldActualDependentValueBeforeCoercion != null) |
|
||||
{ |
|
||||
oldValue = _oldActualDependentValueBeforeCoercion; |
|
||||
_oldActualDependentValueBeforeCoercion = null; |
|
||||
} |
|
||||
|
|
||||
RoutedPropertyChangedEventHandler<IComparable> handler = this.ActualDependentValueChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<IComparable>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (_isCoercingActualDependentValue) |
|
||||
{ |
|
||||
_isCoercingActualDependentValue = false; |
|
||||
this.ActualDependentValue = coercedValue; |
|
||||
} |
|
||||
} |
|
||||
#endregion public IComparable ActualDependentValue
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This event is raised when the dependent value of the data point is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<IComparable> DependentValueChanged; |
|
||||
|
|
||||
#region public IComparable DependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the dependent value of the Control.
|
|
||||
/// </summary>
|
|
||||
public IComparable DependentValue |
|
||||
{ |
|
||||
get { return (IComparable) GetValue(DependentValueProperty); } |
|
||||
set { SetValue(DependentValueProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentValueProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentValue", |
|
||||
typeof(IComparable), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(null, OnDependentValuePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the DependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its DependentValue.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
IComparable oldValue = (IComparable) e.OldValue; |
|
||||
IComparable newValue = (IComparable) e.NewValue; |
|
||||
source.OnDependentValuePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the DependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnDependentValuePropertyChanged(IComparable oldValue, IComparable newValue) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedDependentValueProperty, DependentValueStringFormat, newValue); |
|
||||
RoutedPropertyChangedEventHandler<IComparable> handler = this.DependentValueChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<IComparable>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
if (this.State == DataPointState.Created) |
|
||||
{ |
|
||||
// Prefer setting the value as a double...
|
|
||||
double coercedNewValue; |
|
||||
if (ValueHelper.TryConvert(newValue, out coercedNewValue)) |
|
||||
{ |
|
||||
ActualDependentValue = coercedNewValue; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// ... but fall back otherwise
|
|
||||
ActualDependentValue = newValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
#endregion public IComparable DependentValue
|
|
||||
|
|
||||
#region public string DependentValueStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string for the FormattedDependentValue property.
|
|
||||
/// </summary>
|
|
||||
public string DependentValueStringFormat |
|
||||
{ |
|
||||
get { return GetValue(DependentValueStringFormatProperty) as string; } |
|
||||
set { SetValue(DependentValueStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentValueStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentValueStringFormatProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentValueStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(null, OnDependentValueStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when DependentValueStringFormat property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its DependentValueStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentValueStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = d as DataPoint; |
|
||||
string oldValue = e.OldValue as string; |
|
||||
string newValue = e.NewValue as string; |
|
||||
source.OnDependentValueStringFormatPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when DependentValueStringFormat property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnDependentValueStringFormatPropertyChanged(string oldValue, string newValue) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedDependentValueProperty, newValue, DependentValue); |
|
||||
} |
|
||||
#endregion public string DependentValueStringFormat
|
|
||||
|
|
||||
#region public string FormattedDependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets the DependentValue as formatted by the DependentValueStringFormat property.
|
|
||||
/// </summary>
|
|
||||
public string FormattedDependentValue |
|
||||
{ |
|
||||
get { return GetValue(FormattedDependentValueProperty) as string; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the FormattedDependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty FormattedDependentValueProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"FormattedDependentValue", |
|
||||
typeof(string), |
|
||||
typeof(DataPoint), |
|
||||
null); |
|
||||
#endregion public string FormattedDependentValue
|
|
||||
|
|
||||
#region public string FormattedIndependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets the IndependentValue as formatted by the IndependentValueStringFormat property.
|
|
||||
/// </summary>
|
|
||||
public string FormattedIndependentValue |
|
||||
{ |
|
||||
get { return GetValue(FormattedIndependentValueProperty) as string; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the FormattedIndependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty FormattedIndependentValueProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"FormattedIndependentValue", |
|
||||
typeof(string), |
|
||||
typeof(DataPoint), |
|
||||
null); |
|
||||
#endregion public string FormattedIndependentValue
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the independent value of the data point is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<object> IndependentValueChanged; |
|
||||
|
|
||||
#region public object IndependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the independent value.
|
|
||||
/// </summary>
|
|
||||
public object IndependentValue |
|
||||
{ |
|
||||
get { return GetValue(IndependentValueProperty); } |
|
||||
set { SetValue(IndependentValueProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IndependentValueProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentValue", |
|
||||
typeof(object), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(null, OnIndependentValuePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the IndependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its IndependentValue.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
object oldValue = e.OldValue; |
|
||||
object newValue = e.NewValue; |
|
||||
source.OnIndependentValuePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the IndependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old value.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnIndependentValuePropertyChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedIndependentValueProperty, IndependentValueStringFormat, newValue); |
|
||||
RoutedPropertyChangedEventHandler<object> handler = this.IndependentValueChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<object>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
if (this.State == DataPointState.Created) |
|
||||
{ |
|
||||
// Prefer setting the value as a double...
|
|
||||
double coercedNewValue; |
|
||||
if (ValueHelper.TryConvert(newValue, out coercedNewValue)) |
|
||||
{ |
|
||||
ActualIndependentValue = coercedNewValue; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// ... but fall back otherwise
|
|
||||
ActualIndependentValue = newValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
#endregion public object IndependentValue
|
|
||||
|
|
||||
#region public string IndependentValueStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string for the FormattedIndependentValue property.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValueStringFormat |
|
||||
{ |
|
||||
get { return GetValue(IndependentValueStringFormatProperty) as string; } |
|
||||
set { SetValue(IndependentValueStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentValueStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IndependentValueStringFormatProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentValueStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(null, OnIndependentValueStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the IndependentValueStringFormat property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its IndependentValueStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentValueStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = d as DataPoint; |
|
||||
string oldValue = e.OldValue as string; |
|
||||
string newValue = e.NewValue as string; |
|
||||
source.OnIndependentValueStringFormatPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the IndependentValueStringFormat property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnIndependentValueStringFormatPropertyChanged(string oldValue, string newValue) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedIndependentValueProperty, newValue, IndependentValue); |
|
||||
} |
|
||||
#endregion public string IndependentValueStringFormat
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Occurs when the actual independent value of the data point is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<object> ActualIndependentValueChanged; |
|
||||
|
|
||||
#region public object ActualIndependentValue
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual independent value.
|
|
||||
/// </summary>
|
|
||||
public object ActualIndependentValue |
|
||||
{ |
|
||||
get { return (object)GetValue(ActualIndependentValueProperty); } |
|
||||
set { SetValue(ActualIndependentValueProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A value indicating whether the actual independent value is being
|
|
||||
/// coerced.
|
|
||||
/// </summary>
|
|
||||
private bool _isCoercingActualIndependentValue; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The preserved previous actual dependent value before coercion.
|
|
||||
/// </summary>
|
|
||||
private object _oldActualIndependentValueBeforeCoercion; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualIndependentValue dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualIndependentValueProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualIndependentValue", |
|
||||
typeof(object), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(OnActualIndependentValuePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the ActualIndependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its ActualIndependentValue.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnActualIndependentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
object oldValue = (object)e.OldValue; |
|
||||
object newValue = (object)e.NewValue; |
|
||||
source.OnActualIndependentValuePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the ActualIndependentValue property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnActualIndependentValuePropertyChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
double coercedValue = 0.0; |
|
||||
if (!(newValue is double) && ValueHelper.TryConvert(newValue, out coercedValue)) |
|
||||
{ |
|
||||
_isCoercingActualIndependentValue = true; |
|
||||
_oldActualIndependentValueBeforeCoercion = oldValue; |
|
||||
} |
|
||||
|
|
||||
if (!_isCoercingActualIndependentValue) |
|
||||
{ |
|
||||
if (_oldActualIndependentValueBeforeCoercion != null) |
|
||||
{ |
|
||||
oldValue = _oldActualIndependentValueBeforeCoercion; |
|
||||
_oldActualIndependentValueBeforeCoercion = null; |
|
||||
} |
|
||||
|
|
||||
RoutedPropertyChangedEventHandler<object> handler = this.ActualIndependentValueChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<object>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (_isCoercingActualIndependentValue) |
|
||||
{ |
|
||||
_isCoercingActualIndependentValue = false; |
|
||||
this.ActualIndependentValue = coercedValue; |
|
||||
} |
|
||||
} |
|
||||
#endregion public object ActualIndependentValue
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Occurs when the state of a data point is changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<DataPointState> StateChanged; |
|
||||
|
|
||||
#region public DataPointState State
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether the State property is being
|
|
||||
/// coerced to its previous value.
|
|
||||
/// </summary>
|
|
||||
private bool IsCoercingState { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the state of the data point.
|
|
||||
/// </summary>
|
|
||||
internal DataPointState State |
|
||||
{ |
|
||||
get { return (DataPointState)GetValue(StateProperty); } |
|
||||
set { SetValue(StateProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the State dependency property.
|
|
||||
/// </summary>
|
|
||||
internal static readonly DependencyProperty StateProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"State", |
|
||||
typeof(DataPointState), |
|
||||
typeof(DataPoint), |
|
||||
new PropertyMetadata(DataPointState.Created, OnStatePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the State property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Control that changed its State.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnStatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint source = (DataPoint)d; |
|
||||
DataPointState oldValue = (DataPointState)e.OldValue; |
|
||||
DataPointState newValue = (DataPointState)e.NewValue; |
|
||||
source.OnStatePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the State property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected virtual void OnStatePropertyChanged(DataPointState oldValue, DataPointState newValue) |
|
||||
{ |
|
||||
if (!IsCoercingState) |
|
||||
{ |
|
||||
// If state ever goes to or past PendingRemoval, the DataPoint is no longer active
|
|
||||
if (DataPointState.PendingRemoval <= newValue) |
|
||||
{ |
|
||||
IsActive = false; |
|
||||
} |
|
||||
|
|
||||
if (newValue < oldValue) |
|
||||
{ |
|
||||
// If we've somehow gone backwards in the life cycle (other
|
|
||||
// than when we go back to normal from updating) coerce to
|
|
||||
// old value.
|
|
||||
IsCoercingState = true; |
|
||||
this.State = oldValue; |
|
||||
IsCoercingState = false; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Update selection
|
|
||||
if (newValue > DataPointState.Normal) |
|
||||
{ |
|
||||
this.IsSelectionEnabled = false; |
|
||||
} |
|
||||
|
|
||||
// Start state transition
|
|
||||
bool transitionStarted = false; |
|
||||
switch (newValue) |
|
||||
{ |
|
||||
case DataPointState.Showing: |
|
||||
case DataPointState.Hiding: |
|
||||
transitionStarted = GoToCurrentRevealState(); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
// Fire Changed event
|
|
||||
RoutedPropertyChangedEventHandler<DataPointState> handler = this.StateChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<DataPointState>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
// Change state if no transition started
|
|
||||
if (!transitionStarted && _templateApplied) |
|
||||
{ |
|
||||
switch (newValue) |
|
||||
{ |
|
||||
case DataPointState.Showing: |
|
||||
State = DataPointState.Normal; |
|
||||
break; |
|
||||
case DataPointState.Hiding: |
|
||||
State = DataPointState.Hidden; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
#endregion internal DataPointState State
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the implementation root of the Control.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Implements Silverlight's corresponding internal property on Control.
|
|
||||
/// </remarks>
|
|
||||
private FrameworkElement ImplementationRoot |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return (1 == VisualTreeHelper.GetChildrenCount(this)) ? VisualTreeHelper.GetChild(this, 0) as FrameworkElement : null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Tracks whether the Reveal/Shown VisualState is available.
|
|
||||
/// </summary>
|
|
||||
private bool _haveStateRevealShown; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Tracks whether the Reveal/Hidden VisualState is available.
|
|
||||
/// </summary>
|
|
||||
private bool _haveStateRevealHidden; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Tracks whether the template has been applied yet.
|
|
||||
/// </summary>
|
|
||||
private bool _templateApplied; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the DataPoint class.
|
|
||||
/// </summary>
|
|
||||
protected DataPoint() |
|
||||
{ |
|
||||
Loaded += new RoutedEventHandler(OnLoaded); |
|
||||
IsActive = true; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Control's visuals to reflect the current state(s).
|
|
||||
/// </summary>
|
|
||||
/// <returns>True if a state transition was started.</returns>
|
|
||||
private bool GoToCurrentRevealState() |
|
||||
{ |
|
||||
bool transitionStarted = false; |
|
||||
string stateName = null; |
|
||||
switch (State) |
|
||||
{ |
|
||||
case DataPointState.Showing: |
|
||||
if (_haveStateRevealShown) |
|
||||
{ |
|
||||
stateName = StateRevealShown; |
|
||||
} |
|
||||
break; |
|
||||
case DataPointState.Hiding: |
|
||||
if (_haveStateRevealHidden) |
|
||||
{ |
|
||||
stateName = StateRevealHidden; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
if (null != stateName) |
|
||||
{ |
|
||||
if (!DesignerProperties.GetIsInDesignMode(this)) |
|
||||
{ |
|
||||
// The use of Dispatcher.BeginInvoke here is necessary to
|
|
||||
// work around the StackOverflowException Silverlight throws
|
|
||||
// when it tries to play too many VSM animations.
|
|
||||
Dispatcher.BeginInvoke(new Action(() => VisualStateManager.GoToState(this, stateName, true))); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
VisualStateManager.GoToState(this, stateName, false); |
|
||||
} |
|
||||
transitionStarted = true; |
|
||||
} |
|
||||
return transitionStarted; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Builds the visual tree for the DataPoint when a new template is applied.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
// Unhook CurrentStateChanged handler
|
|
||||
VisualStateGroup groupReveal = VisualStateManager.GetVisualStateGroups(ImplementationRoot).CastWrapper<VisualStateGroup>().Where(group => GroupRevealStates == group.Name).FirstOrDefault(); |
|
||||
if (null != groupReveal) |
|
||||
{ |
|
||||
groupReveal.CurrentStateChanged -= new EventHandler<VisualStateChangedEventArgs>(OnCurrentStateChanged); |
|
||||
} |
|
||||
|
|
||||
base.OnApplyTemplate(); |
|
||||
|
|
||||
// Hook CurrentStateChanged handler
|
|
||||
_haveStateRevealShown = false; |
|
||||
_haveStateRevealHidden = false; |
|
||||
groupReveal = VisualStateManager.GetVisualStateGroups(ImplementationRoot).CastWrapper<VisualStateGroup>().Where(group => GroupRevealStates == group.Name).FirstOrDefault(); |
|
||||
if (null != groupReveal) |
|
||||
{ |
|
||||
groupReveal.CurrentStateChanged += new EventHandler<VisualStateChangedEventArgs>(OnCurrentStateChanged); |
|
||||
_haveStateRevealShown = groupReveal.States.CastWrapper<VisualState>().Where(state => StateRevealShown == state.Name).Any(); |
|
||||
_haveStateRevealHidden = groupReveal.States.CastWrapper<VisualState>().Where(state => StateRevealHidden == state.Name).Any(); |
|
||||
} |
|
||||
|
|
||||
_templateApplied = true; |
|
||||
|
|
||||
// Go to current state(s)
|
|
||||
GoToCurrentRevealState(); |
|
||||
|
|
||||
if (DesignerProperties.GetIsInDesignMode(this)) |
|
||||
{ |
|
||||
// Transition to Showing state in design mode so DataPoint will be visible
|
|
||||
State = DataPointState.Showing; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Changes the DataPoint object's state after one of the VSM state animations completes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void OnCurrentStateChanged(object sender, VisualStateChangedEventArgs e) |
|
||||
{ |
|
||||
switch (e.NewState.Name) |
|
||||
{ |
|
||||
case StateRevealShown: |
|
||||
if (State == DataPointState.Showing) |
|
||||
{ |
|
||||
State = DataPointState.Normal; |
|
||||
} |
|
||||
break; |
|
||||
case StateRevealHidden: |
|
||||
if (State == DataPointState.Hiding) |
|
||||
{ |
|
||||
State = DataPointState.Hidden; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the Control's Loaded event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The Control.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void OnLoaded(object sender, RoutedEventArgs e) |
|
||||
{ |
|
||||
GoToCurrentRevealState(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseEnter event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
protected override void OnMouseEnter(MouseEventArgs e) |
|
||||
{ |
|
||||
base.OnMouseEnter(e); |
|
||||
|
|
||||
if (IsSelectionEnabled) |
|
||||
{ |
|
||||
IsHovered = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseLeave event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
protected override void OnMouseLeave(MouseEventArgs e) |
|
||||
{ |
|
||||
base.OnMouseLeave(e); |
|
||||
if (IsSelectionEnabled) |
|
||||
{ |
|
||||
IsHovered = false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseLeftButtonDown event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) |
|
||||
{ |
|
||||
if (DefinitionSeriesIsSelectionEnabledHandling) |
|
||||
{ |
|
||||
// DefinitionSeries-compatible handling
|
|
||||
if (!IsSelectionEnabled) |
|
||||
{ |
|
||||
// Prevents clicks from bubbling to background, but necessary
|
|
||||
// to avoid letting ListBoxItem select the item
|
|
||||
e.Handled = true; |
|
||||
} |
|
||||
base.OnMouseLeftButtonDown(e); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Traditional handling
|
|
||||
base.OnMouseLeftButtonDown(e); |
|
||||
if (IsSelectionEnabled) |
|
||||
{ |
|
||||
IsSelected = (ModifierKeys.None == (ModifierKeys.Control & Keyboard.Modifiers)); |
|
||||
e.Handled = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether to handle IsSelectionEnabled in the DefinitionSeries manner.
|
|
||||
/// </summary>
|
|
||||
internal bool DefinitionSeriesIsSelectionEnabledHandling { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets a dependency property with the specified format.
|
|
||||
/// </summary>
|
|
||||
/// <param name="property">The DependencyProperty to set.</param>
|
|
||||
/// <param name="format">The Format string to apply to the value.</param>
|
|
||||
/// <param name="value">The value of the dependency property to be formatted.</param>
|
|
||||
internal void SetFormattedProperty(DependencyProperty property, string format, object value) |
|
||||
{ |
|
||||
SetValue(property, string.Format(CultureInfo.CurrentCulture, format ?? "{0}", value)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,43 +0,0 @@ |
|||||
// (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, |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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 line 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 LineDataPoint : DataPoint |
|
||||
{ |
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the LineDataPoint class.
|
|
||||
/// </summary>
|
|
||||
static LineDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(LineDataPoint), new FrameworkPropertyMetadata(typeof(LineDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LineDataPoint class.
|
|
||||
/// </summary>
|
|
||||
public LineDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(LineDataPoint); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,576 +0,0 @@ |
|||||
// (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.Globalization; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Input; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Media.Animation; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a data point used for a pie series.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[TemplatePart(Name = SliceName, Type = typeof(UIElement))] |
|
||||
[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 class PieDataPoint : DataPoint |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The name of the slice template part.
|
|
||||
/// </summary>
|
|
||||
private const string SliceName = "Slice"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the ActualDataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
internal const string ActualDataPointStyleName = "ActualDataPointStyle"; |
|
||||
|
|
||||
#region public Geometry Geometry
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Geometry property which defines the shape of the
|
|
||||
/// data point.
|
|
||||
/// </summary>
|
|
||||
public Geometry Geometry |
|
||||
{ |
|
||||
get { return GetValue(GeometryProperty) as Geometry; } |
|
||||
set { SetValue(GeometryProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Geometry dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty GeometryProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Geometry", |
|
||||
typeof(Geometry), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion public Geometry Geometry
|
|
||||
|
|
||||
// GeometrySelection and GeometryHighlight exist on Silverlight because
|
|
||||
// a single Geometry object can not be the target of multiple
|
|
||||
// TemplateBindings - yet the default template has 3 Paths that bind.
|
|
||||
|
|
||||
#region public Geometry GeometrySelection
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Geometry which defines the shape of a point. The
|
|
||||
/// GeometrySelection property is a copy of the Geometry property.
|
|
||||
/// </summary>
|
|
||||
public Geometry GeometrySelection |
|
||||
{ |
|
||||
get { return GetValue(GeometrySelectionProperty) as Geometry; } |
|
||||
set { SetValue(GeometrySelectionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the GeometrySelection dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty GeometrySelectionProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"GeometrySelection", |
|
||||
typeof(Geometry), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion public Geometry GeometrySelection
|
|
||||
|
|
||||
#region public Geometry GeometryHighlight
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the GeometryHighlight property which is a clone of the
|
|
||||
/// Geometry property.
|
|
||||
/// </summary>
|
|
||||
public Geometry GeometryHighlight |
|
||||
{ |
|
||||
get { return GetValue(GeometryHighlightProperty) as Geometry; } |
|
||||
set { SetValue(GeometryHighlightProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the GeometryHighlight dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty GeometryHighlightProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"GeometryHighlight", |
|
||||
typeof(Geometry), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion public Geometry GeometryHighlight
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Occurs when the actual offset ratio of the pie data point changes.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> ActualOffsetRatioChanged; |
|
||||
|
|
||||
#region public double ActualOffsetRatio
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the offset ratio that is displayed on the screen.
|
|
||||
/// </summary>
|
|
||||
public double ActualOffsetRatio |
|
||||
{ |
|
||||
get { return (double)GetValue(ActualOffsetRatioProperty); } |
|
||||
set { SetValue(ActualOffsetRatioProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualOffsetRatio dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualOffsetRatioProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualOffsetRatio", |
|
||||
typeof(double), |
|
||||
typeof(PieDataPoint), |
|
||||
new PropertyMetadata(OnActualOffsetRatioPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualOffsetRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">PieDataPoint that changed its ActualOffsetRatio.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnActualOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieDataPoint source = (PieDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnActualOffsetRatioPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualOffsetRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
private void OnActualOffsetRatioPropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<double> handler = this.ActualOffsetRatioChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
if (DesignerProperties.GetIsInDesignMode(this)) |
|
||||
{ |
|
||||
PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight); |
|
||||
} |
|
||||
} |
|
||||
#endregion public double ActualOffsetRatio
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// An event raised when the actual ratio of the pie data point is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> ActualRatioChanged; |
|
||||
|
|
||||
#region public double ActualRatio
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the ratio displayed on the screen.
|
|
||||
/// </summary>
|
|
||||
public double ActualRatio |
|
||||
{ |
|
||||
get { return (double)GetValue(ActualRatioProperty); } |
|
||||
set { SetValue(ActualRatioProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualRatio dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualRatioProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"ActualRatio", |
|
||||
typeof(double), |
|
||||
typeof(PieDataPoint), |
|
||||
new PropertyMetadata(OnActualRatioPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">PieDataPoint that changed its ActualRatio.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnActualRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieDataPoint source = (PieDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnActualRatioPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the ActualRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
private void OnActualRatioPropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
if (ValueHelper.CanGraph(newValue)) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<double> handler = this.ActualRatioChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.ActualRatio = 0.0; |
|
||||
} |
|
||||
|
|
||||
if (DesignerProperties.GetIsInDesignMode(this)) |
|
||||
{ |
|
||||
PieSeries.UpdatePieDataPointGeometry(this, ActualWidth, ActualHeight); |
|
||||
} |
|
||||
} |
|
||||
#endregion public double ActualRatio
|
|
||||
|
|
||||
#region public string FormattedRatio
|
|
||||
/// <summary>
|
|
||||
/// Gets the Ratio with the value of the RatioStringFormat property applied.
|
|
||||
/// </summary>
|
|
||||
public string FormattedRatio |
|
||||
{ |
|
||||
get { return GetValue(FormattedRatioProperty) as string; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the FormattedRatio dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty FormattedRatioProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"FormattedRatio", |
|
||||
typeof(string), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion public string FormattedRatio
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// An event raised when the offset ratio of the pie data point is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> OffsetRatioChanged; |
|
||||
|
|
||||
#region public double OffsetRatio
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the offset ratio of the pie data point.
|
|
||||
/// </summary>
|
|
||||
public double OffsetRatio |
|
||||
{ |
|
||||
get { return (double)GetValue(OffsetRatioProperty); } |
|
||||
set { SetValue(OffsetRatioProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the OffsetRatio dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty OffsetRatioProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"OffsetRatio", |
|
||||
typeof(double), |
|
||||
typeof(PieDataPoint), |
|
||||
new PropertyMetadata(OnOffsetRatioPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the OffsetRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">PieDataPoint that changed its OffsetRatio.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnOffsetRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieDataPoint source = (PieDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnOffsetRatioPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the OffsetRatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
private void OnOffsetRatioPropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
if (ValueHelper.CanGraph(newValue)) |
|
||||
{ |
|
||||
RoutedPropertyChangedEventHandler<double> handler = this.OffsetRatioChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
if (this.State == DataPointState.Created) |
|
||||
{ |
|
||||
ActualOffsetRatio = newValue; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.OffsetRatio = 0.0; |
|
||||
} |
|
||||
} |
|
||||
#endregion public double OffsetRatio
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// An event raised when the ratio of the pie data point is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
internal event RoutedPropertyChangedEventHandler<double> RatioChanged; |
|
||||
|
|
||||
#region public double Ratio
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the ratio of the total that the data point
|
|
||||
/// represents.
|
|
||||
/// </summary>
|
|
||||
public double Ratio |
|
||||
{ |
|
||||
get { return (double)GetValue(RatioProperty); } |
|
||||
set { SetValue(RatioProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Ratio dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty RatioProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Ratio", |
|
||||
typeof(double), |
|
||||
typeof(PieDataPoint), |
|
||||
new PropertyMetadata(OnRatioPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the RatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">PieDataPoint that changed its Ratio.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnRatioPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieDataPoint source = (PieDataPoint)d; |
|
||||
double oldValue = (double)e.OldValue; |
|
||||
double newValue = (double)e.NewValue; |
|
||||
source.OnRatioPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the RatioProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The value to be replaced.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
private void OnRatioPropertyChanged(double oldValue, double newValue) |
|
||||
{ |
|
||||
if (ValueHelper.CanGraph(newValue)) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedRatioProperty, RatioStringFormat, newValue); |
|
||||
RoutedPropertyChangedEventHandler<double> handler = this.RatioChanged; |
|
||||
if (handler != null) |
|
||||
{ |
|
||||
handler(this, new RoutedPropertyChangedEventArgs<double>(oldValue, newValue)); |
|
||||
} |
|
||||
|
|
||||
if (this.State == DataPointState.Created) |
|
||||
{ |
|
||||
ActualRatio = newValue; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.Ratio = 0.0; |
|
||||
} |
|
||||
} |
|
||||
#endregion public double Ratio
|
|
||||
|
|
||||
#region public string RatioStringFormat
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the format string for the FormattedRatio property.
|
|
||||
/// </summary>
|
|
||||
public string RatioStringFormat |
|
||||
{ |
|
||||
get { return GetValue(RatioStringFormatProperty) as string; } |
|
||||
set { SetValue(RatioStringFormatProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the RatioStringFormat dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty RatioStringFormatProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"RatioStringFormat", |
|
||||
typeof(string), |
|
||||
typeof(PieDataPoint), |
|
||||
new PropertyMetadata(null, OnRatioStringFormatPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the RatioStringFormatProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">PieDataPoint that changed its RatioStringFormat.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnRatioStringFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieDataPoint source = d as PieDataPoint; |
|
||||
string newValue = e.NewValue as string; |
|
||||
source.OnRatioStringFormatPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the RatioStringFormatProperty property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
private void OnRatioStringFormatPropertyChanged(string newValue) |
|
||||
{ |
|
||||
SetFormattedProperty(FormattedRatioProperty, newValue, Ratio); |
|
||||
} |
|
||||
#endregion public string RatioStringFormat
|
|
||||
|
|
||||
#region internal Style ActualDataPointStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual style used for the data points.
|
|
||||
/// </summary>
|
|
||||
internal Style ActualDataPointStyle |
|
||||
{ |
|
||||
get { return GetValue(ActualDataPointStyleProperty) as Style; } |
|
||||
set { SetValue(ActualDataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
internal static readonly DependencyProperty ActualDataPointStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
ActualDataPointStyleName, |
|
||||
typeof(Style), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion internal Style ActualDataPointStyle
|
|
||||
|
|
||||
#region internal Style ActualLegendItemStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual style used for the legend item.
|
|
||||
/// </summary>
|
|
||||
internal Style ActualLegendItemStyle |
|
||||
{ |
|
||||
get { return GetValue(ActualLegendItemStyleProperty) as Style; } |
|
||||
set { SetValue(ActualLegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualLegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
internal static readonly DependencyProperty ActualLegendItemStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
DataPointSeries.ActualLegendItemStyleName, |
|
||||
typeof(Style), |
|
||||
typeof(PieDataPoint), |
|
||||
null); |
|
||||
#endregion protected Style ActualLegendItemStyle
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the Palette-dispensed ResourceDictionary for the Series.
|
|
||||
/// </summary>
|
|
||||
protected internal ResourceDictionary PaletteResources { get; internal set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the element that represents the pie slice.
|
|
||||
/// </summary>
|
|
||||
private UIElement SliceElement { get; set; } |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the PieDataPoint class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static PieDataPoint() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(PieDataPoint), new FrameworkPropertyMetadata(typeof(PieDataPoint))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the PieDataPoint class.
|
|
||||
/// </summary>
|
|
||||
public PieDataPoint() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
DefaultStyleKey = typeof(PieDataPoint); |
|
||||
#endif
|
|
||||
|
|
||||
if (DesignerProperties.GetIsInDesignMode(this)) |
|
||||
{ |
|
||||
// Create default design-mode-friendly settings
|
|
||||
ActualRatio = 0.2; |
|
||||
SizeChanged += delegate(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
// Handle SizeChanged event to update Geometry dynamically
|
|
||||
PieSeries.UpdatePieDataPointGeometry(this, e.NewSize.Width, e.NewSize.Height); |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Builds the visual tree for the PieDataPoint when a new template is applied.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
if (null != SliceElement) |
|
||||
{ |
|
||||
SliceElement.MouseEnter -= new MouseEventHandler(SliceElement_MouseEnter); |
|
||||
SliceElement.MouseLeave -= new MouseEventHandler(SliceElement_MouseLeave); |
|
||||
} |
|
||||
|
|
||||
base.OnApplyTemplate(); |
|
||||
|
|
||||
SliceElement = GetTemplateChild(SliceName) as UIElement; |
|
||||
|
|
||||
if (null != SliceElement) |
|
||||
{ |
|
||||
SliceElement.MouseEnter += new MouseEventHandler(SliceElement_MouseEnter); |
|
||||
SliceElement.MouseLeave += new MouseEventHandler(SliceElement_MouseLeave); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseEnter event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">The event data.</param>
|
|
||||
protected override void OnMouseEnter(MouseEventArgs e) |
|
||||
{ |
|
||||
// Do nothing because PieDataPoint handles SliceElement.MouseEnter instead
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseLeave event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">The event data.</param>
|
|
||||
protected override void OnMouseLeave(MouseEventArgs e) |
|
||||
{ |
|
||||
// Do nothing because PieDataPoint handles SliceElement.MouseLeave instead
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseEnter event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">The event data.</param>
|
|
||||
private void SliceElement_MouseEnter(object sender, MouseEventArgs e) |
|
||||
{ |
|
||||
// Defer to Control's default MouseEnter handling
|
|
||||
base.OnMouseEnter(e); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides handling for the MouseLeave event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">The event data.</param>
|
|
||||
private void SliceElement_MouseLeave(object sender, MouseEventArgs e) |
|
||||
{ |
|
||||
// Defer to Control's default MouseLeave handling
|
|
||||
base.OnMouseLeave(e); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +0,0 @@ |
|||||
// (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 set of extension methods for the DataPoint class.
|
|
||||
/// </summary>
|
|
||||
internal static class FrameworkElementExtensions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Returns the actual margin for a given framework element and axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">The framework element.</param>
|
|
||||
/// <param name="axis">The axis along which to return the margin.
|
|
||||
/// </param>
|
|
||||
/// <returns>The margin for a given framework element and axis.
|
|
||||
/// </returns>
|
|
||||
public static double GetActualMargin(this FrameworkElement element, IAxis axis) |
|
||||
{ |
|
||||
double length = 0.0; |
|
||||
if (axis.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
length = element.ActualWidth; |
|
||||
} |
|
||||
else if (axis.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
length = element.ActualHeight; |
|
||||
} |
|
||||
return length / 2.0; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the margin for a given framework element and axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">The framework element.</param>
|
|
||||
/// <param name="axis">The axis along which to return the margin.
|
|
||||
/// </param>
|
|
||||
/// <returns>The margin for a given framework element and axis.
|
|
||||
/// </returns>
|
|
||||
public static double GetMargin(this FrameworkElement element, IAxis axis) |
|
||||
{ |
|
||||
double length = 0.0; |
|
||||
if (axis.Orientation == AxisOrientation.X) |
|
||||
{ |
|
||||
length = !double.IsNaN(element.Width) ? element.Width : element.ActualWidth; |
|
||||
} |
|
||||
else if (axis.Orientation == AxisOrientation.Y) |
|
||||
{ |
|
||||
length = !double.IsNaN(element.Height) ? element.Height : element.ActualHeight; |
|
||||
} |
|
||||
return length / 2.0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
// (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>
|
|
||||
/// An object that implements this interface requires a series host.
|
|
||||
/// </summary>
|
|
||||
public interface IRequireSeriesHost |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the series host.
|
|
||||
/// </summary>
|
|
||||
ISeriesHost SeriesHost { get; set; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,40 +0,0 @@ |
|||||
// (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.Collections.ObjectModel; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines properties, methods and events for classes that host a
|
|
||||
/// collection of Series objects.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public interface ISeriesHost : IRequireSeriesHost, IResourceDictionaryDispenser |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the collection of axes the series host has available.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<IAxis> Axes { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of series the series host has available.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<ISeries> Series { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the foreground elements.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<UIElement> ForegroundElements { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the background elements.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<UIElement> BackgroundElements { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,54 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Extension methods for series hosts.
|
|
||||
/// </summary>
|
|
||||
internal static class ISeriesHostExtensions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets all series that track their global indexes recursively.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rootSeriesHost">The root series host.</param>
|
|
||||
/// <returns>A sequence of series.</returns>
|
|
||||
public static IEnumerable<ISeries> GetDescendentSeries(this ISeriesHost rootSeriesHost) |
|
||||
{ |
|
||||
Queue<ISeries> series = new Queue<ISeries>(rootSeriesHost.Series); |
|
||||
while (series.Count != 0) |
|
||||
{ |
|
||||
ISeries currentSeries = series.Dequeue(); |
|
||||
yield return currentSeries; |
|
||||
|
|
||||
ISeriesHost seriesHost = currentSeries as ISeriesHost; |
|
||||
if (seriesHost != null) |
|
||||
{ |
|
||||
foreach (ISeries childSeries in seriesHost.Series) |
|
||||
{ |
|
||||
series.Enqueue(childSeries); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether an axis is in use by the series
|
|
||||
/// host.
|
|
||||
/// </summary>
|
|
||||
/// <param name="that">The series host.</param>
|
|
||||
/// <param name="axis">The axis that may or may not be used by a
|
|
||||
/// series.</param>
|
|
||||
/// <returns>A value indicating whether an axis is in use by the series
|
|
||||
/// host.</returns>
|
|
||||
public static bool IsUsedByASeries(this ISeriesHost that, IAxis axis) |
|
||||
{ |
|
||||
return axis.RegisteredListeners.OfType<ISeries>().Intersect(that.Series).Any(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,111 +0,0 @@ |
|||||
// (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.Primitives |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Subclasses ListBox to provide an easy way for a consumer of
|
|
||||
/// ListBox to hook into the four standard ListBox *Container*
|
|
||||
/// overrides.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class DelegatingListBox : ListBox |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets a function to call when the
|
|
||||
/// IsItemItsOwnContainerOverride method executes.
|
|
||||
/// </summary>
|
|
||||
public Func<object, bool> IsItemItsOwnContainer { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a function to call when the
|
|
||||
/// GetContainerForItem method executes.
|
|
||||
/// </summary>
|
|
||||
public Func<DependencyObject> GetContainerForItem { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets an action to call when the
|
|
||||
/// PrepareContainerForItem method executes.
|
|
||||
/// </summary>
|
|
||||
public Action<DependencyObject, object> PrepareContainerForItem { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets an action to call when the
|
|
||||
/// ClearContainerForItem method executes.
|
|
||||
/// </summary>
|
|
||||
public Action<DependencyObject, object> ClearContainerForItem { get; set; } |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes static members of the DelegatingListBox class.
|
|
||||
/// </summary>
|
|
||||
static DelegatingListBox() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(DelegatingListBox), new FrameworkPropertyMetadata(typeof(DelegatingListBox))); |
|
||||
} |
|
||||
#endif
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the DelegatingListBox class.
|
|
||||
/// </summary>
|
|
||||
public DelegatingListBox() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
DefaultStyleKey = typeof(DelegatingListBox); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines if the specified item is (or is eligible to be) its own container.
|
|
||||
/// </summary>
|
|
||||
/// <param name="item">The item to check.</param>
|
|
||||
/// <returns>True if the item is (or is eligible to be) its own container; otherwise, false.</returns>
|
|
||||
protected override bool IsItemItsOwnContainerOverride(object item) |
|
||||
{ |
|
||||
return (null != IsItemItsOwnContainer) ? |
|
||||
IsItemItsOwnContainer(item) : |
|
||||
base.IsItemItsOwnContainerOverride(item); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates or identifies the element that is used to display the given item.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The element that is used to display the given item.</returns>
|
|
||||
protected override DependencyObject GetContainerForItemOverride() |
|
||||
{ |
|
||||
return (null != GetContainerForItem) ? |
|
||||
GetContainerForItem() : |
|
||||
base.GetContainerForItemOverride(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Prepares the specified element to display the specified item.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">The element used to display the specified item.</param>
|
|
||||
/// <param name="item">The item to display.</param>
|
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item) |
|
||||
{ |
|
||||
base.PrepareContainerForItemOverride(element, item); |
|
||||
if (null != PrepareContainerForItem) |
|
||||
{ |
|
||||
PrepareContainerForItem(element, item); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Undoes the effects of the PrepareContainerForItemOverride method.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">The container element.</param>
|
|
||||
/// <param name="item">The item to display.</param>
|
|
||||
protected override void ClearContainerForItemOverride(DependencyObject element, object item) |
|
||||
{ |
|
||||
base.ClearContainerForItemOverride(element, item); |
|
||||
if (null != ClearContainerForItem) |
|
||||
{ |
|
||||
ClearContainerForItem(element, item); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
// (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; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Primitives |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Specifies the edge position of a child element that is inside an
|
|
||||
/// EdgePanel.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public enum Edge |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A child element that is positioned in the center of a EdgePanel.
|
|
||||
/// </summary>
|
|
||||
Center, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A child element that is positioned on the left side of the
|
|
||||
/// EdgePanel.
|
|
||||
/// </summary>
|
|
||||
Left, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A child element that is positioned at the top of the EdgePanel.
|
|
||||
/// </summary>
|
|
||||
Top, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A child element that is positioned on the right side of the
|
|
||||
/// EdgePanel.
|
|
||||
/// </summary>
|
|
||||
Right, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A child element that is positioned at the bottom of the EdgePanel.
|
|
||||
/// </summary>
|
|
||||
Bottom, |
|
||||
} |
|
||||
} |
|
||||
@ -1,554 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Globalization; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Media; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Primitives |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines an area where you can arrange child elements either horizontally
|
|
||||
/// or vertically, relative to each other.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class EdgePanel : Panel |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The maximum number of iterations.
|
|
||||
/// </summary>
|
|
||||
private const int MaximumIterations = 10; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A flag that ignores a property change when set.
|
|
||||
/// </summary>
|
|
||||
private static bool _ignorePropertyChange; |
|
||||
|
|
||||
#region public attached Edge Edge
|
|
||||
/// <summary>
|
|
||||
/// Gets the value of the Edge attached property for a specified
|
|
||||
/// UIElement.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">
|
|
||||
/// The element from which the property value is read.
|
|
||||
/// </param>
|
|
||||
/// <returns>The Edge property value for the element.</returns>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "EdgePanel only has UIElement children")] |
|
||||
public static Edge GetEdge(UIElement element) |
|
||||
{ |
|
||||
if (element == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException("element"); |
|
||||
} |
|
||||
return (Edge)element.GetValue(EdgeProperty); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the value of the Edge attached property to a specified element.
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">
|
|
||||
/// The element to which the attached property is written.
|
|
||||
/// </param>
|
|
||||
/// <param name="edge">The needed Edge value.</param>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "EdgePanel only has UIElement children")] |
|
||||
public static void SetEdge(UIElement element, Edge edge) |
|
||||
{ |
|
||||
if (element == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException("element"); |
|
||||
} |
|
||||
element.SetValue(EdgeProperty, edge); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Edge dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty EdgeProperty = |
|
||||
DependencyProperty.RegisterAttached( |
|
||||
"Edge", |
|
||||
typeof(Edge), |
|
||||
typeof(EdgePanel), |
|
||||
new PropertyMetadata(Edge.Center, OnEdgePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// EdgeProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">UIElement that changed its Edge.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the attached property CLR setter.")] |
|
||||
private static void OnEdgePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
// Ignore the change if requested
|
|
||||
if (_ignorePropertyChange) |
|
||||
{ |
|
||||
_ignorePropertyChange = false; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
UIElement element = (UIElement)d; |
|
||||
Edge value = (Edge)e.NewValue; |
|
||||
|
|
||||
// Validate the Edge property
|
|
||||
if ((value != Edge.Left) && |
|
||||
(value != Edge.Top) && |
|
||||
(value != Edge.Right) && |
|
||||
(value != Edge.Center) && |
|
||||
(value != Edge.Bottom)) |
|
||||
{ |
|
||||
// Reset the property to its original state before throwing
|
|
||||
_ignorePropertyChange = true; |
|
||||
element.SetValue(EdgeProperty, (Edge)e.OldValue); |
|
||||
|
|
||||
string message = string.Format( |
|
||||
CultureInfo.InvariantCulture, |
|
||||
Properties.Resources.EdgePanel_OnEdgePropertyChanged, |
|
||||
value); |
|
||||
|
|
||||
throw new ArgumentException(message, "value"); |
|
||||
} |
|
||||
|
|
||||
// Cause the EdgePanel to update its layout when a child changes
|
|
||||
EdgePanel panel = VisualTreeHelper.GetParent(element) as EdgePanel; |
|
||||
if (panel != null) |
|
||||
{ |
|
||||
panel.InvalidateMeasure(); |
|
||||
} |
|
||||
} |
|
||||
#endregion public attached Edge Edge
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the EdgePanel class.
|
|
||||
/// </summary>
|
|
||||
public EdgePanel() |
|
||||
{ |
|
||||
this.SizeChanged += new SizeChangedEventHandler(EdgePanelSizeChanged); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Invalidate measure when edge panel is resized.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void EdgePanelSizeChanged(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
InvalidateMeasure(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The left rectangle in which to render left elements.
|
|
||||
/// </summary>
|
|
||||
private Rect _leftRect; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The right rectangle in which to render right elements.
|
|
||||
/// </summary>
|
|
||||
private Rect _rightRect; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The top rectangle in which to render top elements.
|
|
||||
/// </summary>
|
|
||||
private Rect _topRect; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The bottom rectangle in which to render bottom elements.
|
|
||||
/// </summary>
|
|
||||
private Rect _bottomRect; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Measures the children of a EdgePanel in anticipation of arranging
|
|
||||
/// them during the ArrangeOverride pass.
|
|
||||
/// </summary>
|
|
||||
/// <param name="constraint">A maximum Size to not exceed.</param>
|
|
||||
/// <returns>The desired size of the EdgePanel.</returns>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification = "Code is by nature difficult to refactor into several methods.")] |
|
||||
[SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")] |
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Splitting up method will make it more difficult to understand.")] |
|
||||
protected override Size MeasureOverride(Size constraint) |
|
||||
{ |
|
||||
constraint = new Size(this.ActualWidth, this.ActualHeight); |
|
||||
|
|
||||
IList<UIElement> leftElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Left).ToList(); |
|
||||
IList<UIElement> rightElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Right).ToList(); |
|
||||
IList<UIElement> bottomElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Bottom).ToList(); |
|
||||
IList<UIElement> topElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Top).ToList(); |
|
||||
|
|
||||
Rect totalRect = SafeCreateRect(0, 0, constraint.Width, constraint.Height); |
|
||||
|
|
||||
_leftRect = (leftElements.Count > 0) ? totalRect : Rect.Empty; |
|
||||
_bottomRect = (bottomElements.Count > 0) ? totalRect : Rect.Empty; |
|
||||
_rightRect = (rightElements.Count > 0) ? totalRect : Rect.Empty; |
|
||||
_topRect = (topElements.Count > 0) ? totalRect : Rect.Empty; |
|
||||
|
|
||||
double rightAxesWidth = 0.0; |
|
||||
double leftAxesWidth = 0.0; |
|
||||
double topAxesHeight = 0.0; |
|
||||
double bottomAxesHeight = 0.0; |
|
||||
|
|
||||
double maxRightRequestedWidth = 0; |
|
||||
double maxLeftRequestedWidth = 0; |
|
||||
double maxTopRequestedHeight = 0; |
|
||||
double maxBottomRequestedHeight = 0; |
|
||||
|
|
||||
double previousRightAxesWidth = rightAxesWidth; |
|
||||
double previousLeftAxesWidth = leftAxesWidth; |
|
||||
double previousTopAxesHeight = topAxesHeight; |
|
||||
double previousBottomAxesHeight = bottomAxesHeight; |
|
||||
|
|
||||
int iterations = 0; |
|
||||
// Measure each of the Children
|
|
||||
while (true) |
|
||||
{ |
|
||||
// Measure the children using the rectangle regions.
|
|
||||
if (rightElements.Count > 0) |
|
||||
{ |
|
||||
Size rightSize = new Size(constraint.Width, _rightRect.Height); |
|
||||
foreach (UIElement rightUIElement in rightElements) |
|
||||
{ |
|
||||
rightUIElement.Measure(rightSize); |
|
||||
} |
|
||||
|
|
||||
previousRightAxesWidth = rightAxesWidth; |
|
||||
rightAxesWidth = rightElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(); |
|
||||
maxRightRequestedWidth = Math.Max(maxRightRequestedWidth, rightAxesWidth); |
|
||||
_rightRect = |
|
||||
SafeCreateRect( |
|
||||
constraint.Width - rightAxesWidth, |
|
||||
_rightRect.Top, |
|
||||
rightAxesWidth, |
|
||||
_rightRect.Height); |
|
||||
} |
|
||||
|
|
||||
if (topElements.Count > 0) |
|
||||
{ |
|
||||
Size topSize = new Size(_topRect.Width, constraint.Height); |
|
||||
foreach (UIElement topUIElement in topElements) |
|
||||
{ |
|
||||
topUIElement.Measure(topSize); |
|
||||
} |
|
||||
|
|
||||
previousTopAxesHeight = topAxesHeight; |
|
||||
topAxesHeight = topElements.Select(axis => axis.DesiredSize.Height).SumOrDefault(); |
|
||||
maxTopRequestedHeight = Math.Max(maxTopRequestedHeight, topAxesHeight); |
|
||||
_topRect = |
|
||||
SafeCreateRect( |
|
||||
_topRect.Left, |
|
||||
_topRect.Top, |
|
||||
_topRect.Width, |
|
||||
topAxesHeight); |
|
||||
} |
|
||||
|
|
||||
if (leftElements.Count > 0) |
|
||||
{ |
|
||||
Size leftSize = new Size(constraint.Width, _leftRect.Height); |
|
||||
foreach (UIElement leftUIElement in leftElements) |
|
||||
{ |
|
||||
leftUIElement.Measure(leftSize); |
|
||||
} |
|
||||
|
|
||||
previousLeftAxesWidth = leftAxesWidth; |
|
||||
leftAxesWidth = leftElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(); |
|
||||
maxLeftRequestedWidth = Math.Max(maxLeftRequestedWidth, leftAxesWidth); |
|
||||
_leftRect = |
|
||||
SafeCreateRect( |
|
||||
_leftRect.Left, |
|
||||
_leftRect.Top, |
|
||||
leftElements.Select(axis => axis.DesiredSize.Width).SumOrDefault(), |
|
||||
_leftRect.Height); |
|
||||
} |
|
||||
|
|
||||
if (bottomElements.Count > 0) |
|
||||
{ |
|
||||
Size bottomSize = new Size(_bottomRect.Width, constraint.Height); |
|
||||
foreach (UIElement bottomUIElement in bottomElements) |
|
||||
{ |
|
||||
bottomUIElement.Measure(bottomSize); |
|
||||
} |
|
||||
|
|
||||
previousBottomAxesHeight = bottomAxesHeight; |
|
||||
bottomAxesHeight = bottomElements.Select(axis => axis.DesiredSize.Height).SumOrDefault(); |
|
||||
maxBottomRequestedHeight = Math.Max(maxBottomRequestedHeight, bottomAxesHeight); |
|
||||
_bottomRect = |
|
||||
SafeCreateRect( |
|
||||
_bottomRect.Left, |
|
||||
constraint.Height - bottomAxesHeight, |
|
||||
_bottomRect.Width, |
|
||||
bottomAxesHeight); |
|
||||
} |
|
||||
|
|
||||
// Ensuring that parallel axes don't collide
|
|
||||
Rect leftRightCollisionRect = _leftRect; |
|
||||
leftRightCollisionRect.Intersect(_rightRect); |
|
||||
|
|
||||
Rect topBottomCollisionRect = _topRect; |
|
||||
topBottomCollisionRect.Intersect(_bottomRect); |
|
||||
|
|
||||
if (!leftRightCollisionRect.IsEmptyOrHasNoSize() || !topBottomCollisionRect.IsEmptyOrHasNoSize()) |
|
||||
{ |
|
||||
return new Size(); |
|
||||
} |
|
||||
|
|
||||
// Resolving perpendicular axes collisions
|
|
||||
Rect leftTopCollisionRect = _leftRect; |
|
||||
leftTopCollisionRect.Intersect(_topRect); |
|
||||
|
|
||||
Rect rightTopCollisionRect = _rightRect; |
|
||||
rightTopCollisionRect.Intersect(_topRect); |
|
||||
|
|
||||
Rect leftBottomCollisionRect = _leftRect; |
|
||||
leftBottomCollisionRect.Intersect(_bottomRect); |
|
||||
|
|
||||
Rect rightBottomCollisionRect = _rightRect; |
|
||||
rightBottomCollisionRect.Intersect(_bottomRect); |
|
||||
|
|
||||
if (leftBottomCollisionRect.IsEmptyOrHasNoSize() |
|
||||
&& rightBottomCollisionRect.IsEmptyOrHasNoSize() |
|
||||
&& leftTopCollisionRect.IsEmptyOrHasNoSize() |
|
||||
&& rightTopCollisionRect.IsEmptyOrHasNoSize() |
|
||||
&& previousBottomAxesHeight == bottomAxesHeight |
|
||||
&& previousLeftAxesWidth == leftAxesWidth |
|
||||
&& previousRightAxesWidth == rightAxesWidth |
|
||||
&& previousTopAxesHeight == topAxesHeight) |
|
||||
{ |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
if (iterations == MaximumIterations) |
|
||||
{ |
|
||||
_leftRect = SafeCreateRect(0, maxTopRequestedHeight, maxLeftRequestedWidth, (constraint.Height - maxTopRequestedHeight) - maxBottomRequestedHeight); |
|
||||
_rightRect = SafeCreateRect(constraint.Width - maxRightRequestedWidth, maxTopRequestedHeight, maxRightRequestedWidth, (constraint.Height - maxTopRequestedHeight) - maxBottomRequestedHeight); |
|
||||
_bottomRect = SafeCreateRect(maxLeftRequestedWidth, constraint.Height - maxBottomRequestedHeight, (constraint.Width - maxLeftRequestedWidth) - maxRightRequestedWidth, maxBottomRequestedHeight); |
|
||||
_topRect = SafeCreateRect(maxLeftRequestedWidth, 0, (constraint.Width - maxLeftRequestedWidth) - maxRightRequestedWidth, maxTopRequestedHeight); |
|
||||
|
|
||||
foreach (UIElement leftElement in leftElements) |
|
||||
{ |
|
||||
leftElement.Measure(new Size(_leftRect.Width, _leftRect.Height)); |
|
||||
} |
|
||||
|
|
||||
foreach (UIElement rightElement in rightElements) |
|
||||
{ |
|
||||
rightElement.Measure(new Size(_rightRect.Width, _rightRect.Height)); |
|
||||
} |
|
||||
|
|
||||
foreach (UIElement bottomElement in bottomElements) |
|
||||
{ |
|
||||
bottomElement.Measure(new Size(_bottomRect.Width, _bottomRect.Height)); |
|
||||
} |
|
||||
|
|
||||
foreach (UIElement topElement in topElements) |
|
||||
{ |
|
||||
topElement.Measure(new Size(_topRect.Width, _topRect.Height)); |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
if (!leftBottomCollisionRect.IsEmptyOrHasNoSize()) |
|
||||
{ |
|
||||
_leftRect = |
|
||||
SafeCreateRect( |
|
||||
_leftRect.Left, |
|
||||
_leftRect.Top, |
|
||||
_leftRect.Width, |
|
||||
_leftRect.Height - leftBottomCollisionRect.Height); |
|
||||
|
|
||||
_bottomRect = |
|
||||
SafeCreateRect( |
|
||||
_bottomRect.Left + leftBottomCollisionRect.Width, |
|
||||
_bottomRect.Top, |
|
||||
_bottomRect.Width - leftBottomCollisionRect.Width, |
|
||||
_bottomRect.Height); |
|
||||
} |
|
||||
|
|
||||
if (!leftTopCollisionRect.IsEmptyOrHasNoSize()) |
|
||||
{ |
|
||||
_leftRect = |
|
||||
SafeCreateRect( |
|
||||
_leftRect.Left, |
|
||||
_leftRect.Top + leftTopCollisionRect.Height, |
|
||||
_leftRect.Width, |
|
||||
_leftRect.Height - leftTopCollisionRect.Height); |
|
||||
|
|
||||
_topRect = |
|
||||
SafeCreateRect( |
|
||||
_topRect.Left + leftTopCollisionRect.Width, |
|
||||
_topRect.Top, |
|
||||
_topRect.Width - leftTopCollisionRect.Width, |
|
||||
_topRect.Height); |
|
||||
} |
|
||||
|
|
||||
if (!rightBottomCollisionRect.IsEmptyOrHasNoSize()) |
|
||||
{ |
|
||||
_rightRect = |
|
||||
SafeCreateRect( |
|
||||
_rightRect.Left, |
|
||||
_rightRect.Top, |
|
||||
_rightRect.Width, |
|
||||
_rightRect.Height - rightBottomCollisionRect.Height); |
|
||||
|
|
||||
_bottomRect = |
|
||||
SafeCreateRect( |
|
||||
_bottomRect.Left, |
|
||||
_bottomRect.Top, |
|
||||
_bottomRect.Width - rightBottomCollisionRect.Width, |
|
||||
_bottomRect.Height); |
|
||||
} |
|
||||
|
|
||||
if (!rightTopCollisionRect.IsEmptyOrHasNoSize()) |
|
||||
{ |
|
||||
_rightRect = |
|
||||
SafeCreateRect( |
|
||||
_rightRect.Left, |
|
||||
_rightRect.Top + rightTopCollisionRect.Height, |
|
||||
_rightRect.Width, |
|
||||
_rightRect.Height - rightTopCollisionRect.Height); |
|
||||
|
|
||||
_topRect = |
|
||||
SafeCreateRect( |
|
||||
_topRect.Left, |
|
||||
_topRect.Top, |
|
||||
_topRect.Width - rightTopCollisionRect.Width, |
|
||||
_topRect.Height); |
|
||||
} |
|
||||
|
|
||||
// Bring axis measure rectangles together if there are gaps
|
|
||||
// between them.
|
|
||||
if (!_leftRect.IsEmpty) |
|
||||
{ |
|
||||
_leftRect = |
|
||||
new Rect( |
|
||||
new Point(_leftRect.Left, _topRect.BottomOrDefault(0)), |
|
||||
new Point(_leftRect.Right, _bottomRect.TopOrDefault(constraint.Height))); |
|
||||
} |
|
||||
|
|
||||
if (!_rightRect.IsEmpty) |
|
||||
{ |
|
||||
_rightRect = |
|
||||
new Rect( |
|
||||
new Point(_rightRect.Left, _topRect.BottomOrDefault(0)), |
|
||||
new Point(_rightRect.Right, _bottomRect.TopOrDefault(constraint.Height))); |
|
||||
} |
|
||||
|
|
||||
if (!_bottomRect.IsEmpty) |
|
||||
{ |
|
||||
_bottomRect = |
|
||||
new Rect( |
|
||||
new Point(_leftRect.RightOrDefault(0), _bottomRect.Top), |
|
||||
new Point(_rightRect.LeftOrDefault(constraint.Width), _bottomRect.Bottom)); |
|
||||
} |
|
||||
|
|
||||
if (!_topRect.IsEmpty) |
|
||||
{ |
|
||||
_topRect = |
|
||||
new Rect( |
|
||||
new Point(_leftRect.RightOrDefault(0), _topRect.Top), |
|
||||
new Point(_rightRect.LeftOrDefault(constraint.Width), _topRect.Bottom)); |
|
||||
} |
|
||||
|
|
||||
iterations++; |
|
||||
} |
|
||||
|
|
||||
Size centerSize = |
|
||||
new Size( |
|
||||
(constraint.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), |
|
||||
(constraint.Height - _topRect.HeightOrDefault(0)) - _bottomRect.HeightOrDefault(0)); |
|
||||
|
|
||||
foreach (UIElement element in Children.OfType<UIElement>().Where(child => GetEdge(child) == Edge.Center)) |
|
||||
{ |
|
||||
element.Measure(centerSize); |
|
||||
} |
|
||||
|
|
||||
return new Size(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Arranges the content (child elements) of a EdgePanel element.
|
|
||||
/// </summary>
|
|
||||
/// <param name="arrangeSize">
|
|
||||
/// The Size the EdgePanel uses to arrange its child elements.
|
|
||||
/// </param>
|
|
||||
/// <returns>The arranged size of the EdgePanel.</returns>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Splitting up method will make it more difficult to understand.")] |
|
||||
[SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")] |
|
||||
protected override Size ArrangeOverride(Size arrangeSize) |
|
||||
{ |
|
||||
if (arrangeSize.Width == 0 || arrangeSize.Height == 0 || !ValueHelper.CanGraph(arrangeSize.Width) || !ValueHelper.CanGraph(arrangeSize.Height)) |
|
||||
{ |
|
||||
return arrangeSize; |
|
||||
} |
|
||||
|
|
||||
IList<UIElement> leftElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Left).ToList(); |
|
||||
IList<UIElement> rightElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Right).ToList(); |
|
||||
IList<UIElement> bottomElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Bottom).ToList(); |
|
||||
IList<UIElement> topElements = this.Children.OfType<UIElement>().Where(element => GetEdge(element) == Edge.Top).ToList(); |
|
||||
|
|
||||
if (!_bottomRect.IsEmpty) |
|
||||
{ |
|
||||
double workingHeight = _bottomRect.Top; |
|
||||
foreach (UIElement bottomUIElement in bottomElements) |
|
||||
{ |
|
||||
bottomUIElement.Arrange(SafeCreateRect(_leftRect.RightOrDefault(0), workingHeight, (arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), bottomUIElement.DesiredSize.Height)); |
|
||||
workingHeight += bottomUIElement.DesiredSize.Height; |
|
||||
} |
|
||||
} |
|
||||
if (!_topRect.IsEmpty) |
|
||||
{ |
|
||||
double workingTop = _topRect.Bottom; |
|
||||
foreach (UIElement topUIElement in topElements) |
|
||||
{ |
|
||||
workingTop -= topUIElement.DesiredSize.Height; |
|
||||
topUIElement.Arrange(SafeCreateRect(_leftRect.RightOrDefault(0), workingTop, (arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0), topUIElement.DesiredSize.Height)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!_rightRect.IsEmpty) |
|
||||
{ |
|
||||
double workingRight = _rightRect.Left; |
|
||||
foreach (UIElement rightUIElement in rightElements) |
|
||||
{ |
|
||||
rightUIElement.Arrange(SafeCreateRect(workingRight, _topRect.BottomOrDefault(0), rightUIElement.DesiredSize.Width, (arrangeSize.Height - _bottomRect.HeightOrDefault(0)) - _topRect.HeightOrDefault(0))); |
|
||||
workingRight += rightUIElement.DesiredSize.Width; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!_leftRect.IsEmpty) |
|
||||
{ |
|
||||
double workingLeft = _leftRect.Right; |
|
||||
foreach (UIElement leftUIElement in leftElements) |
|
||||
{ |
|
||||
workingLeft -= leftUIElement.DesiredSize.Width; |
|
||||
Rect leftRect = SafeCreateRect(workingLeft, _topRect.BottomOrDefault(0), leftUIElement.DesiredSize.Width, (arrangeSize.Height - _bottomRect.HeightOrDefault(0)) - _topRect.HeightOrDefault(0)); |
|
||||
leftUIElement.Arrange(leftRect); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Rect centerRect = SafeCreateRect( |
|
||||
_leftRect.RightOrDefault(0), |
|
||||
_topRect.BottomOrDefault(0), |
|
||||
((arrangeSize.Width - _leftRect.WidthOrDefault(0)) - _rightRect.WidthOrDefault(0)), |
|
||||
((arrangeSize.Height - _topRect.HeightOrDefault(0)) - _bottomRect.HeightOrDefault(0))); |
|
||||
|
|
||||
foreach (UIElement element in Children.OfType<UIElement>().Where(child => GetEdge(child) == Edge.Center)) |
|
||||
{ |
|
||||
element.Arrange(centerRect); |
|
||||
} |
|
||||
|
|
||||
return arrangeSize; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a Rect safely by forcing width/height to be valid.
|
|
||||
/// </summary>
|
|
||||
/// <param name="left">Rect left parameter.</param>
|
|
||||
/// <param name="top">Rect top parameter.</param>
|
|
||||
/// <param name="width">Rect width parameter.</param>
|
|
||||
/// <param name="height">Rect height parameter.</param>
|
|
||||
/// <returns>New Rect struct.</returns>
|
|
||||
private static Rect SafeCreateRect(double left, double top, double width, double height) |
|
||||
{ |
|
||||
return new Rect(left, top, Math.Max(0.0, width), Math.Max(0.0, height)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,56 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Information describing the ResourceDictionary dispensed when a
|
|
||||
/// ResourceDictionaryDispensed event is raised.
|
|
||||
/// </summary>
|
|
||||
internal class ResourceDictionaryDispensedEventArgs : EventArgs |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ResourceDictionaryDispensedEventArgs class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The index of the ResourceDictionary dispensed.</param>
|
|
||||
/// <param name="resourceDictionary">The ResourceDictionary dispensed.</param>
|
|
||||
public ResourceDictionaryDispensedEventArgs(int index, ResourceDictionary resourceDictionary) |
|
||||
{ |
|
||||
this.ResourceDictionary = resourceDictionary; |
|
||||
this.Index = index; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the index of the ResourceDictionary dispensed.
|
|
||||
/// </summary>
|
|
||||
public int Index { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the ResourceDictionary dispensed.
|
|
||||
/// </summary>
|
|
||||
public ResourceDictionary ResourceDictionary { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a value indicating whether two objects are equal.
|
|
||||
/// </summary>
|
|
||||
/// <param name="obj">The other object.</param>
|
|
||||
/// <returns>
|
|
||||
/// A value indicating whether the two objects are equal.
|
|
||||
/// </returns>
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
return base.Equals(obj); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a hash code.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A hash code.</returns>
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return base.GetHashCode(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,250 +0,0 @@ |
|||||
// (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.Collections.Specialized; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A class that rotates through a list of ResourceDictionaries.
|
|
||||
/// </summary>
|
|
||||
internal class ResourceDictionaryDispenser : IResourceDictionaryDispenser |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A linked list of ResourceDictionaries dispensed.
|
|
||||
/// </summary>
|
|
||||
private LinkedList<ResourceDictionaryDispensedEventArgs> _resourceDictionariesDispensed = new LinkedList<ResourceDictionaryDispensedEventArgs>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A bag of weak references to connected style enumerators.
|
|
||||
/// </summary>
|
|
||||
private WeakReferenceBag<ResourceDictionaryEnumerator> _resourceDictionaryEnumerators = new WeakReferenceBag<ResourceDictionaryEnumerator>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Value indicating whether to ignore that the enumerator has
|
|
||||
/// dispensed a ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
private bool _ignoreResourceDictionaryDispensedByEnumerator; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The list of ResourceDictionaries of rotate.
|
|
||||
/// </summary>
|
|
||||
private IList<ResourceDictionary> _resourceDictionaries; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the list of ResourceDictionaries to rotate.
|
|
||||
/// </summary>
|
|
||||
public IList<ResourceDictionary> ResourceDictionaries |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _resourceDictionaries; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value != _resourceDictionaries) |
|
||||
{ |
|
||||
{ |
|
||||
INotifyCollectionChanged notifyCollectionChanged = _resourceDictionaries as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged -= ResourceDictionariesCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
_resourceDictionaries = value; |
|
||||
{ |
|
||||
INotifyCollectionChanged notifyCollectionChanged = _resourceDictionaries as INotifyCollectionChanged; |
|
||||
if (notifyCollectionChanged != null) |
|
||||
{ |
|
||||
notifyCollectionChanged.CollectionChanged += ResourceDictionariesCollectionChanged; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is raised when the ResourceDictionaries collection is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void ResourceDictionariesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (!(e.Action == NotifyCollectionChangedAction.Add && (this.ResourceDictionaries.Count - e.NewItems.Count) == e.NewStartingIndex)) |
|
||||
{ |
|
||||
Reset(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The parent of the ResourceDictionaryDispenser.
|
|
||||
/// </summary>
|
|
||||
private IResourceDictionaryDispenser _parent; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Event that is invoked when the ResourceDictionaryDispenser's contents have changed.
|
|
||||
/// </summary>
|
|
||||
public event EventHandler ResourceDictionariesChanged; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the parent of the ResourceDictionaryDispenser.
|
|
||||
/// </summary>
|
|
||||
public IResourceDictionaryDispenser Parent |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _parent; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (_parent != value) |
|
||||
{ |
|
||||
if (null != _parent) |
|
||||
{ |
|
||||
_parent.ResourceDictionariesChanged -= new EventHandler(ParentResourceDictionariesChanged); |
|
||||
} |
|
||||
_parent = value; |
|
||||
if (null != _parent) |
|
||||
{ |
|
||||
_parent.ResourceDictionariesChanged += new EventHandler(ParentResourceDictionariesChanged); |
|
||||
} |
|
||||
OnParentChanged(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ResourceDictionaryDispenser class.
|
|
||||
/// </summary>
|
|
||||
public ResourceDictionaryDispenser() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Resets the state of the ResourceDictionaryDispenser and its enumerators.
|
|
||||
/// </summary>
|
|
||||
private void Reset() |
|
||||
{ |
|
||||
OnResetting(); |
|
||||
|
|
||||
// Invoke event
|
|
||||
EventHandler handler = ResourceDictionariesChanged; |
|
||||
if (null != handler) |
|
||||
{ |
|
||||
handler.Invoke(this, EventArgs.Empty); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Unregisters an enumerator so that it can be garbage collected.
|
|
||||
/// </summary>
|
|
||||
/// <param name="enumerator">The enumerator.</param>
|
|
||||
internal void Unregister(ResourceDictionaryEnumerator enumerator) |
|
||||
{ |
|
||||
_resourceDictionaryEnumerators.Remove(enumerator); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a rotating enumerator of ResourceDictionary objects that coordinates
|
|
||||
/// with the dispenser object to ensure that no two enumerators are on the same
|
|
||||
/// item. If the dispenser is reset or its collection is changed then the
|
|
||||
/// enumerators are also reset.
|
|
||||
/// </summary>
|
|
||||
/// <param name="predicate">A predicate that returns a value indicating
|
|
||||
/// whether to return an item.</param>
|
|
||||
/// <returns>An enumerator of ResourceDictionaries.</returns>
|
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Returning a usable enumerator instance.")] |
|
||||
public IEnumerator<ResourceDictionary> GetResourceDictionariesWhere(Func<ResourceDictionary, bool> predicate) |
|
||||
{ |
|
||||
ResourceDictionaryEnumerator enumerator = new ResourceDictionaryEnumerator(this, predicate); |
|
||||
|
|
||||
_ignoreResourceDictionaryDispensedByEnumerator = true; |
|
||||
try |
|
||||
{ |
|
||||
foreach (ResourceDictionaryDispensedEventArgs args in _resourceDictionariesDispensed) |
|
||||
{ |
|
||||
enumerator.ResourceDictionaryDispenserResourceDictionaryDispensed(this, args); |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
_ignoreResourceDictionaryDispensedByEnumerator = false; |
|
||||
} |
|
||||
|
|
||||
_resourceDictionaryEnumerators.Add(enumerator); |
|
||||
return enumerator; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is raised when an enumerator dispenses a ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
internal void EnumeratorResourceDictionaryDispensed(object sender, ResourceDictionaryDispensedEventArgs e) |
|
||||
{ |
|
||||
if (!_ignoreResourceDictionaryDispensedByEnumerator) |
|
||||
{ |
|
||||
OnEnumeratorResourceDictionaryDispensed(this, e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Raises the ParentChanged event.
|
|
||||
/// </summary>
|
|
||||
private void OnParentChanged() |
|
||||
{ |
|
||||
foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) |
|
||||
{ |
|
||||
enumerator.ResourceDictionaryDispenserParentChanged(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Raises the EnumeratorResourceDictionaryDispensed event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="source">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void OnEnumeratorResourceDictionaryDispensed(object source, ResourceDictionaryDispensedEventArgs args) |
|
||||
{ |
|
||||
// Remove this item from the list of dispensed styles.
|
|
||||
_resourceDictionariesDispensed.Remove(args); |
|
||||
|
|
||||
// Add this item to the end of the list of dispensed styles.
|
|
||||
_resourceDictionariesDispensed.AddLast(args); |
|
||||
|
|
||||
foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) |
|
||||
{ |
|
||||
enumerator.ResourceDictionaryDispenserResourceDictionaryDispensed(source, args); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method raises the EnumeratorsResetting event.
|
|
||||
/// </summary>
|
|
||||
private void OnResetting() |
|
||||
{ |
|
||||
_resourceDictionariesDispensed.Clear(); |
|
||||
|
|
||||
foreach (ResourceDictionaryEnumerator enumerator in _resourceDictionaryEnumerators) |
|
||||
{ |
|
||||
enumerator.ResourceDictionaryDispenserResetting(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the Parent's ResourceDictionariesChanged event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Parent instance.</param>
|
|
||||
/// <param name="e">Event args.</param>
|
|
||||
private void ParentResourceDictionariesChanged(object sender, EventArgs e) |
|
||||
{ |
|
||||
Reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,232 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// An enumerator that dispenses ResourceDictionaries sequentially by coordinating with
|
|
||||
/// related enumerators. Enumerators are related through an association
|
|
||||
/// with a parent ResourceDictionaryDispenser class.
|
|
||||
/// </summary>
|
|
||||
internal class ResourceDictionaryEnumerator : IEnumerator<ResourceDictionary> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The index of current item in the ResourceDictionaryDispenser's list.
|
|
||||
/// </summary>
|
|
||||
private int? index; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the current ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
private ResourceDictionary CurrentResourceDictionary { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The parent enumerator.
|
|
||||
/// </summary>
|
|
||||
private IEnumerator<ResourceDictionary> _parentEnumerator; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the parent enumerator.
|
|
||||
/// </summary>
|
|
||||
private IEnumerator<ResourceDictionary> ParentEnumerator |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (_parentEnumerator == null && ResourceDictionaryDispenser.Parent != null) |
|
||||
{ |
|
||||
_parentEnumerator = ResourceDictionaryDispenser.Parent.GetResourceDictionariesWhere(Predicate); |
|
||||
} |
|
||||
return _parentEnumerator; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of a ResourceDictionaryEnumerator.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dispenser">The dispenser that dispensed this
|
|
||||
/// ResourceDictionaryEnumerator.</param>
|
|
||||
/// <param name="predicate">A predicate used to determine which
|
|
||||
/// ResourceDictionaries to return.</param>
|
|
||||
public ResourceDictionaryEnumerator(ResourceDictionaryDispenser dispenser, Func<ResourceDictionary, bool> predicate) |
|
||||
{ |
|
||||
ResourceDictionaryDispenser = dispenser; |
|
||||
Predicate = predicate; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the parent has changed.
|
|
||||
/// </summary>
|
|
||||
internal void ResourceDictionaryDispenserParentChanged() |
|
||||
{ |
|
||||
_parentEnumerator = null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the index of the next suitable style in the list.
|
|
||||
/// </summary>
|
|
||||
/// <param name="startIndex">The index at which to start looking.</param>
|
|
||||
/// <returns>The index of the next suitable ResourceDictionary.</returns>
|
|
||||
private int? GetIndexOfNextSuitableResourceDictionary(int startIndex) |
|
||||
{ |
|
||||
if (ResourceDictionaryDispenser.ResourceDictionaries == null || ResourceDictionaryDispenser.ResourceDictionaries.Count == 0) |
|
||||
{ |
|
||||
return new int?(); |
|
||||
} |
|
||||
|
|
||||
if (startIndex >= ResourceDictionaryDispenser.ResourceDictionaries.Count) |
|
||||
{ |
|
||||
startIndex = 0; |
|
||||
} |
|
||||
|
|
||||
int counter = startIndex; |
|
||||
do |
|
||||
{ |
|
||||
if (Predicate(ResourceDictionaryDispenser.ResourceDictionaries[counter])) |
|
||||
{ |
|
||||
return counter; |
|
||||
} |
|
||||
|
|
||||
counter = (counter + 1) % ResourceDictionaryDispenser.ResourceDictionaries.Count; |
|
||||
} |
|
||||
while (startIndex != counter); |
|
||||
|
|
||||
return new int?(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Resets the dispenser.
|
|
||||
/// </summary>
|
|
||||
internal void ResourceDictionaryDispenserResetting() |
|
||||
{ |
|
||||
if (!ShouldRetrieveFromParentEnumerator) |
|
||||
{ |
|
||||
index = new int?(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a predicate that returns a value indicating whether a
|
|
||||
/// ResourceDictionary should be returned by this enumerator.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A value indicating whether a ResourceDictionary can be returned by this
|
|
||||
/// enumerator.</returns>
|
|
||||
private Func<ResourceDictionary, bool> Predicate { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method is invoked when one of the related enumerator's
|
|
||||
/// dispenses. The enumerator checks to see if the item
|
|
||||
/// dispensed would've been the next item it would have returned. If
|
|
||||
/// so it updates it's index to the position after the previously
|
|
||||
/// returned item.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The ResourceDictionaryDispenser.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
internal void ResourceDictionaryDispenserResourceDictionaryDispensed(object sender, ResourceDictionaryDispensedEventArgs e) |
|
||||
{ |
|
||||
if (!ShouldRetrieveFromParentEnumerator && Predicate(e.ResourceDictionary)) |
|
||||
{ |
|
||||
int? nextStyleIndex = GetIndexOfNextSuitableResourceDictionary(index ?? 0); |
|
||||
if ((nextStyleIndex ?? -1) == e.Index) |
|
||||
{ |
|
||||
index = (e.Index + 1) % ResourceDictionaryDispenser.ResourceDictionaries.Count; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Raises the EnumeratorResourceDictionaryDispensed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="args">Information about the ResourceDictionary dispensed.</param>
|
|
||||
protected virtual void OnStyleDispensed(ResourceDictionaryDispensedEventArgs args) |
|
||||
{ |
|
||||
ResourceDictionaryDispenser.EnumeratorResourceDictionaryDispensed(this, args); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the dispenser that dispensed this enumerator.
|
|
||||
/// </summary>
|
|
||||
public ResourceDictionaryDispenser ResourceDictionaryDispenser { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the current ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
public ResourceDictionary Current |
|
||||
{ |
|
||||
get { return CurrentResourceDictionary; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the current ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
object System.Collections.IEnumerator.Current |
|
||||
{ |
|
||||
get { return CurrentResourceDictionary; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Moves to the next ResourceDictionary.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A value indicating whether there are any more suitable
|
|
||||
/// ResourceDictionary.</returns>
|
|
||||
public bool MoveNext() |
|
||||
{ |
|
||||
if (ShouldRetrieveFromParentEnumerator && ParentEnumerator != null) |
|
||||
{ |
|
||||
bool isMore = ParentEnumerator.MoveNext(); |
|
||||
if (isMore) |
|
||||
{ |
|
||||
this.CurrentResourceDictionary = ParentEnumerator.Current; |
|
||||
} |
|
||||
return isMore; |
|
||||
} |
|
||||
|
|
||||
index = GetIndexOfNextSuitableResourceDictionary(index ?? 0); |
|
||||
if (index == null) |
|
||||
{ |
|
||||
CurrentResourceDictionary = null; |
|
||||
Dispose(); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
CurrentResourceDictionary = ResourceDictionaryDispenser.ResourceDictionaries[index.Value]; |
|
||||
OnStyleDispensed(new ResourceDictionaryDispensedEventArgs(index.Value, CurrentResourceDictionary)); |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether a enumerator should return ResourceDictionaries
|
|
||||
/// from its parent enumerator.
|
|
||||
/// </summary>
|
|
||||
private bool ShouldRetrieveFromParentEnumerator |
|
||||
{ |
|
||||
get { return this.ResourceDictionaryDispenser.ResourceDictionaries == null; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Resets the enumerator.
|
|
||||
/// </summary>
|
|
||||
public void Reset() |
|
||||
{ |
|
||||
throw new NotSupportedException(Properties.Resources.ResourceDictionaryEnumerator_CantResetEnumeratorResetDispenserInstead); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stops listening to the dispenser.
|
|
||||
/// </summary>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (_parentEnumerator != null) |
|
||||
{ |
|
||||
_parentEnumerator.Dispose(); |
|
||||
} |
|
||||
|
|
||||
this.ResourceDictionaryDispenser.Unregister(this); |
|
||||
GC.SuppressFinalize(this); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,207 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
#if !DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in X/Y
|
|
||||
/// line format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(AreaDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[StyleTypedProperty(Property = "PathStyle", StyleTargetType = typeof(Path))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] |
|
||||
public partial class AreaSeries : LineAreaBaseSeries<AreaDataPoint>, IAnchoredToOrigin |
|
||||
{ |
|
||||
#region public Geometry Geometry
|
|
||||
/// <summary>
|
|
||||
/// Gets the geometry property.
|
|
||||
/// </summary>
|
|
||||
public Geometry Geometry |
|
||||
{ |
|
||||
get { return GetValue(GeometryProperty) as Geometry; } |
|
||||
private set { SetValue(GeometryProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Geometry dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty GeometryProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Geometry", |
|
||||
typeof(Geometry), |
|
||||
typeof(AreaSeries), |
|
||||
null); |
|
||||
#endregion public Geometry Geometry
|
|
||||
|
|
||||
#region public Style PathStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the style of the Path object that follows the data
|
|
||||
/// points.
|
|
||||
/// </summary>
|
|
||||
public Style PathStyle |
|
||||
{ |
|
||||
get { return GetValue(PathStyleProperty) as Style; } |
|
||||
set { SetValue(PathStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the PathStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PathStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"PathStyle", |
|
||||
typeof(Style), |
|
||||
typeof(AreaSeries), |
|
||||
null); |
|
||||
#endregion public Style PathStyle
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the AreaSeries class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static AreaSeries() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(AreaSeries), new FrameworkPropertyMetadata(typeof(AreaSeries))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the AreaSeries class.
|
|
||||
/// </summary>
|
|
||||
public AreaSeries() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(AreaSeries); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal linear axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.X, |
|
||||
() => |
|
||||
{ |
|
||||
IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
axis = new CategoryAxis(); |
|
||||
} |
|
||||
axis.Orientation = AxisOrientation.X; |
|
||||
return axis; |
|
||||
}, |
|
||||
(axis) => |
|
||||
{ |
|
||||
IRangeAxis rangeAxis = axis as IRangeAxis; |
|
||||
return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.Y; |
|
||||
}, |
|
||||
() => |
|
||||
{ |
|
||||
DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
if (axis == null || (axis as IRangeAxis).Origin == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
axis.ShowGridLines = true; |
|
||||
axis.Orientation = AxisOrientation.Y; |
|
||||
return axis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Series shape object from a collection of Points.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">Collection of Points.</param>
|
|
||||
protected override void UpdateShapeFromPoints(IEnumerable<Point> points) |
|
||||
{ |
|
||||
UnitValue originCoordinate = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin); |
|
||||
UnitValue maximumCoordinate = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum); |
|
||||
if (points.Any() && ValueHelper.CanGraph(originCoordinate.Value) && ValueHelper.CanGraph(maximumCoordinate.Value)) |
|
||||
{ |
|
||||
double originY = Math.Floor(originCoordinate.Value); |
|
||||
PathFigure figure = new PathFigure(); |
|
||||
figure.IsClosed = true; |
|
||||
figure.IsFilled = true; |
|
||||
|
|
||||
double maximum = maximumCoordinate.Value; |
|
||||
Point startPoint; |
|
||||
IEnumerator<Point> pointEnumerator = points.GetEnumerator(); |
|
||||
pointEnumerator.MoveNext(); |
|
||||
startPoint = new Point(pointEnumerator.Current.X, maximum - originY); |
|
||||
figure.StartPoint = startPoint; |
|
||||
|
|
||||
Point lastPoint; |
|
||||
do |
|
||||
{ |
|
||||
lastPoint = pointEnumerator.Current; |
|
||||
figure.Segments.Add(new LineSegment { Point = pointEnumerator.Current }); |
|
||||
} |
|
||||
while (pointEnumerator.MoveNext()); |
|
||||
figure.Segments.Add(new LineSegment { Point = new Point(lastPoint.X, maximum - originY) }); |
|
||||
|
|
||||
if (figure.Segments.Count > 1) |
|
||||
{ |
|
||||
PathGeometry geometry = new PathGeometry(); |
|
||||
geometry.Figures.Add(figure); |
|
||||
Geometry = geometry; |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
Geometry = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Remove value margins from the side of the data points to ensure
|
|
||||
/// that area chart is flush against the edge of the chart.
|
|
||||
/// </summary>
|
|
||||
/// <param name="consumer">The value margin consumer.</param>
|
|
||||
/// <returns>A sequence of value margins.</returns>
|
|
||||
protected override IEnumerable<ValueMargin> GetValueMargins(IValueMarginConsumer consumer) |
|
||||
{ |
|
||||
if (consumer == ActualIndependentAxis) |
|
||||
{ |
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
return base.GetValueMargins(consumer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the axis to which the series is anchored.
|
|
||||
/// </summary>
|
|
||||
IRangeAxis IAnchoredToOrigin.AnchoredAxis |
|
||||
{ |
|
||||
get { return AnchoredAxis; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the axis to which the series is anchored.
|
|
||||
/// </summary>
|
|
||||
protected IRangeAxis AnchoredAxis |
|
||||
{ |
|
||||
get { return ActualDependentRangeAxis; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
@ -1,136 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
|
|
||||
#if !DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in bar format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] |
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BarDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
public partial class BarSeries : ColumnBarBaseSeries<BarDataPoint> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the BarSeries class.
|
|
||||
/// </summary>
|
|
||||
public BarSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal category axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.Y, |
|
||||
() => new CategoryAxis { Orientation = AxisOrientation.Y }, |
|
||||
(axis) => |
|
||||
{ |
|
||||
IRangeAxis rangeAxis = axis as IRangeAxis; |
|
||||
return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.X; |
|
||||
}, |
|
||||
() => |
|
||||
{ |
|
||||
IRangeAxis rangeAxis = CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
rangeAxis.Orientation = AxisOrientation.X; |
|
||||
if (rangeAxis == null || rangeAxis.Origin == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
DisplayAxis axis = rangeAxis as DisplayAxis; |
|
||||
if (axis != null) |
|
||||
{ |
|
||||
axis.ShowGridLines = true; |
|
||||
} |
|
||||
return rangeAxis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates each point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to update.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
if (SeriesHost == null || PlotArea == null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
object category = dataPoint.ActualIndependentValue ?? (this.ActiveDataPoints.IndexOf(dataPoint) + 1); |
|
||||
Range<UnitValue> coordinateRange = GetCategoryRange(category); |
|
||||
|
|
||||
if (!coordinateRange.HasData) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes); |
|
||||
} |
|
||||
|
|
||||
double minimum = (double)coordinateRange.Minimum.Value; |
|
||||
double maximum = (double)coordinateRange.Maximum.Value; |
|
||||
|
|
||||
IEnumerable<BarSeries> barSeries = SeriesHost.Series.OfType<BarSeries>().Where(series => series.ActualIndependentAxis == ActualIndependentAxis); |
|
||||
int numberOfSeries = barSeries.Count(); |
|
||||
double coordinateRangeHeight = (maximum - minimum); |
|
||||
double segmentHeight = coordinateRangeHeight * 0.8; |
|
||||
double barHeight = segmentHeight / numberOfSeries; |
|
||||
int seriesIndex = barSeries.IndexOf(this); |
|
||||
|
|
||||
double dataPointX = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(dataPoint.ActualDependentValue)).Value; |
|
||||
double zeroPointX = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value; |
|
||||
|
|
||||
double offset = seriesIndex * Math.Round(barHeight) + coordinateRangeHeight * 0.1; |
|
||||
double dataPointY = minimum + offset; |
|
||||
|
|
||||
if (GetIsDataPointGrouped(category)) |
|
||||
{ |
|
||||
// Multiple DataPoints share this category; offset and overlap them appropriately
|
|
||||
IGrouping<object, DataPoint> categoryGrouping = GetDataPointGroup(category); |
|
||||
int index = categoryGrouping.IndexOf(dataPoint); |
|
||||
dataPointY += (index * (barHeight * 0.2)) / (categoryGrouping.Count() - 1); |
|
||||
barHeight *= 0.8; |
|
||||
Canvas.SetZIndex(dataPoint, -index); |
|
||||
} |
|
||||
|
|
||||
if (ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(dataPointY) && ValueHelper.CanGraph(zeroPointX)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
double top = Math.Round(dataPointY); |
|
||||
double height = Math.Round(barHeight); |
|
||||
|
|
||||
double left = Math.Round(Math.Min(dataPointX, zeroPointX) - 0.5); |
|
||||
double right = Math.Round(Math.Max(dataPointX, zeroPointX) - 0.5); |
|
||||
double width = right - left + 1; |
|
||||
|
|
||||
Canvas.SetLeft(dataPoint, left); |
|
||||
Canvas.SetTop(dataPoint, top); |
|
||||
dataPoint.Width = width; |
|
||||
dataPoint.Height = height; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
@ -1,415 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows.Data; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in X/Y
|
|
||||
/// line format. A third binding determines the size of the data point.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BubbleDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
public class BubbleSeries : DataPointSingleSeriesWithAxes |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The maximum bubble size as a ratio of the smallest dimension.
|
|
||||
/// </summary>
|
|
||||
private const double MaximumBubbleSizeAsRatioOfSmallestDimension = 0.25; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The binding used to identify the size value.
|
|
||||
/// </summary>
|
|
||||
private Binding _sizeValueBinding; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding to use for identifying the size of the bubble.
|
|
||||
/// </summary>
|
|
||||
public Binding SizeValueBinding |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _sizeValueBinding; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (_sizeValueBinding != value) |
|
||||
{ |
|
||||
_sizeValueBinding = value; |
|
||||
Refresh(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding Path to use for identifying the size of the bubble.
|
|
||||
/// </summary>
|
|
||||
public string SizeValuePath |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return (null != SizeValueBinding) ? SizeValueBinding.Path.Path : null; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (null == value) |
|
||||
{ |
|
||||
SizeValueBinding = null; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
SizeValueBinding = new Binding(value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the range of ActualSize values for the BubbleDataPoints.
|
|
||||
/// </summary>
|
|
||||
private Range<double> _rangeOfActualSizeValues = new Range<double>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the bubble series.
|
|
||||
/// </summary>
|
|
||||
public BubbleSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new instance of bubble data point.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A new instance of bubble data point.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new BubbleDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// ResourceDictionary to use for necessary resources.
|
|
||||
/// </returns>
|
|
||||
protected override IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost() |
|
||||
{ |
|
||||
return GetResourceDictionaryWithTargetType(SeriesHost, typeof(BubbleDataPoint), true); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal linear axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.X, |
|
||||
() => |
|
||||
{ |
|
||||
IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
axis = new CategoryAxis(); |
|
||||
} |
|
||||
axis.Orientation = AxisOrientation.X; |
|
||||
return axis; |
|
||||
}, |
|
||||
(axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, |
|
||||
() => |
|
||||
{ |
|
||||
DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
axis.ShowGridLines = true; |
|
||||
axis.Orientation = AxisOrientation.Y; |
|
||||
return axis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Prepares a bubble data point by binding the size value binding to
|
|
||||
/// the size property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to prepare.</param>
|
|
||||
/// <param name="dataContext">The data context of the data point.
|
|
||||
/// </param>
|
|
||||
protected override void PrepareDataPoint(DataPoint dataPoint, object dataContext) |
|
||||
{ |
|
||||
base.PrepareDataPoint(dataPoint, dataContext); |
|
||||
|
|
||||
BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; |
|
||||
bubbleDataPoint.SetBinding(BubbleDataPoint.SizeProperty, SizeValueBinding ?? DependentValueBinding ?? IndependentValueBinding); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Attaches size change and actual size change event handlers to the
|
|
||||
/// data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point.</param>
|
|
||||
protected override void AttachEventHandlersToDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; |
|
||||
bubbleDataPoint.SizePropertyChanged += BubbleDataPointSizePropertyChanged; |
|
||||
bubbleDataPoint.ActualSizePropertyChanged += BubbleDataPointActualSizePropertyChanged; |
|
||||
base.AttachEventHandlersToDataPoint(dataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Detaches size change and actual size change event handlers from the
|
|
||||
/// data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point.</param>
|
|
||||
protected override void DetachEventHandlersFromDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; |
|
||||
bubbleDataPoint.SizePropertyChanged -= BubbleDataPointSizePropertyChanged; |
|
||||
bubbleDataPoint.ActualSizePropertyChanged -= BubbleDataPointActualSizePropertyChanged; |
|
||||
base.DetachEventHandlersFromDataPoint(dataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates all data points when the actual size property of a data
|
|
||||
/// point changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void BubbleDataPointActualSizePropertyChanged(object sender, RoutedPropertyChangedEventArgs<double> e) |
|
||||
{ |
|
||||
Range<double> newRangeOfActualSizeValues = ActiveDataPoints.OfType<BubbleDataPoint>().Select(d => Math.Abs(d.ActualSize)).GetRange(); |
|
||||
if (newRangeOfActualSizeValues == _rangeOfActualSizeValues) |
|
||||
{ |
|
||||
// No range change - only need to update the current point
|
|
||||
UpdateDataPoint((BubbleDataPoint)sender); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Range has changed - need to update all points
|
|
||||
UpdateDataPoints(ActiveDataPoints); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Animates the value of the ActualSize property to the size property
|
|
||||
/// when it changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private void BubbleDataPointSizePropertyChanged(object sender, RoutedPropertyChangedEventArgs<double> e) |
|
||||
{ |
|
||||
BubbleDataPoint dataPoint = (BubbleDataPoint)sender; |
|
||||
|
|
||||
DependencyPropertyAnimationHelper.BeginAnimation( |
|
||||
dataPoint, |
|
||||
BubbleDataPoint.ActualSizeProperty, |
|
||||
"ActualSize", |
|
||||
e.NewValue, |
|
||||
TransitionDuration, |
|
||||
this.TransitionEasingFunction); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the range of ActualSize values of all active BubbleDataPoints.
|
|
||||
/// </summary>
|
|
||||
protected override void OnBeforeUpdateDataPoints() |
|
||||
{ |
|
||||
_rangeOfActualSizeValues = ActiveDataPoints.OfType<BubbleDataPoint>().Select(d => Math.Abs(d.ActualSize)).GetRange(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Ensure that if any data points are updated, all data points are
|
|
||||
/// updated.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoints">The data points to update.</param>
|
|
||||
protected override void UpdateDataPoints(IEnumerable<DataPoint> dataPoints) |
|
||||
{ |
|
||||
base.UpdateDataPoints(ActiveDataPoints); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point's visual representation.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
double maximumDiameter = Math.Min(PlotAreaSize.Width, PlotAreaSize.Height) * MaximumBubbleSizeAsRatioOfSmallestDimension; |
|
||||
|
|
||||
BubbleDataPoint bubbleDataPoint = (BubbleDataPoint)dataPoint; |
|
||||
|
|
||||
double ratioOfLargestBubble = |
|
||||
(_rangeOfActualSizeValues.HasData && _rangeOfActualSizeValues.Maximum != 0.0 && bubbleDataPoint.ActualSize >= 0.0) ? Math.Abs(bubbleDataPoint.ActualSize) / _rangeOfActualSizeValues.Maximum : 0.0; |
|
||||
|
|
||||
bubbleDataPoint.Width = ratioOfLargestBubble * maximumDiameter; |
|
||||
bubbleDataPoint.Height = ratioOfLargestBubble * maximumDiameter; |
|
||||
|
|
||||
double left = |
|
||||
(ActualIndependentAxis.GetPlotAreaCoordinate(bubbleDataPoint.ActualIndependentValue)).Value |
|
||||
- (bubbleDataPoint.Width / 2.0); |
|
||||
|
|
||||
double top = |
|
||||
(PlotAreaSize.Height |
|
||||
- (bubbleDataPoint.Height / 2.0)) |
|
||||
- ActualDependentRangeAxis.GetPlotAreaCoordinate(bubbleDataPoint.ActualDependentValue).Value; |
|
||||
|
|
||||
if (ValueHelper.CanGraph(left) && ValueHelper.CanGraph(top)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
Canvas.SetLeft(bubbleDataPoint, left); |
|
||||
Canvas.SetTop(bubbleDataPoint, top); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the value margins after all data points are updated.
|
|
||||
/// </summary>
|
|
||||
protected override void OnAfterUpdateDataPoints() |
|
||||
{ |
|
||||
IValueMarginProvider provider = this as IValueMarginProvider; |
|
||||
{ |
|
||||
IValueMarginConsumer consumer = ActualDependentRangeAxis as IValueMarginConsumer; |
|
||||
if (consumer != null) |
|
||||
{ |
|
||||
consumer.ValueMarginsChanged(provider, GetValueMargins(consumer)); |
|
||||
} |
|
||||
} |
|
||||
{ |
|
||||
IValueMarginConsumer consumer = ActualIndependentAxis as IValueMarginConsumer; |
|
||||
if (consumer != null) |
|
||||
{ |
|
||||
consumer.ValueMarginsChanged(provider, GetValueMargins(consumer)); |
|
||||
} |
|
||||
} |
|
||||
base.OnAfterUpdateDataPoints(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the dependent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } |
|
||||
|
|
||||
#region public IRangeAxis DependentRangeAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the dependent range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentRangeAxis", |
|
||||
typeof(IRangeAxis), |
|
||||
typeof(BubbleSeries), |
|
||||
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">BubbleSeries that changed its DependentRangeAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
BubbleSeries source = (BubbleSeries)d; |
|
||||
IRangeAxis newValue = (IRangeAxis)e.NewValue; |
|
||||
source.OnDependentRangeAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) |
|
||||
{ |
|
||||
this.InternalDependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IRangeAxis DependentRangeAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the independent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } |
|
||||
|
|
||||
#region public IAxis IndependentAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets independent range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis IndependentAxis |
|
||||
{ |
|
||||
get { return GetValue(IndependentAxisProperty) as IAxis; } |
|
||||
set { SetValue(IndependentAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IndependentAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentAxis", |
|
||||
typeof(IAxis), |
|
||||
typeof(BubbleSeries), |
|
||||
new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">BubbleSeries that changed its IndependentAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
BubbleSeries source = (BubbleSeries)d; |
|
||||
IAxis newValue = (IAxis)e.NewValue; |
|
||||
source.OnIndependentAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnIndependentAxisPropertyChanged(IAxis newValue) |
|
||||
{ |
|
||||
this.InternalIndependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IAxis IndependentAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The margins required for each value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="consumer">The consumer to return the value margins for.</param>
|
|
||||
/// <returns>A sequence of margins for each value.</returns>
|
|
||||
protected override IEnumerable<ValueMargin> GetValueMargins(IValueMarginConsumer consumer) |
|
||||
{ |
|
||||
IAxis axis = consumer as IAxis; |
|
||||
if (axis != null) |
|
||||
{ |
|
||||
return ActiveDataPoints.Select(dataPoint => |
|
||||
{ |
|
||||
double margin = dataPoint.GetMargin(axis); |
|
||||
return new ValueMargin( |
|
||||
GetActualDataPointAxisValue(dataPoint, axis), |
|
||||
margin, |
|
||||
margin); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,358 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// This series serves as the base class for the column and bar series.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the data point.</typeparam>
|
|
||||
public abstract class ColumnBarBaseSeries<T> : DataPointSingleSeriesWithAxes, IAnchoredToOrigin |
|
||||
where T : DataPoint, new() |
|
||||
{ |
|
||||
#region public IRangeAxis DependentRangeAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the dependent range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] |
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentRangeAxis", |
|
||||
typeof(IRangeAxis), |
|
||||
typeof(ColumnBarBaseSeries<T>), |
|
||||
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">ColumnBarBaseSeries that changed its DependentRangeAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
ColumnBarBaseSeries<T> source = (ColumnBarBaseSeries<T>)d; |
|
||||
IRangeAxis newValue = (IRangeAxis)e.NewValue; |
|
||||
source.OnDependentRangeAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) |
|
||||
{ |
|
||||
InternalDependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IRangeAxis DependentRangeAxis
|
|
||||
|
|
||||
#region public IAxis IndependentAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the independent category axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis IndependentAxis |
|
||||
{ |
|
||||
get { return GetValue(IndependentAxisProperty) as IAxis; } |
|
||||
set { SetValue(IndependentAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] |
|
||||
public static readonly DependencyProperty IndependentAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentAxis", |
|
||||
typeof(IAxis), |
|
||||
typeof(ColumnBarBaseSeries<T>), |
|
||||
new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">ColumnBarBaseSeries that changed its IndependentAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
ColumnBarBaseSeries<T> source = (ColumnBarBaseSeries<T>)d; |
|
||||
IAxis newValue = (IAxis)e.NewValue; |
|
||||
source.OnIndependentAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnIndependentAxisPropertyChanged(IAxis newValue) |
|
||||
{ |
|
||||
InternalIndependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IAxis IndependentAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Keeps a list of DataPoints that share the same category.
|
|
||||
/// </summary>
|
|
||||
private IDictionary<object, IGrouping<object, DataPoint>> _categoriesWithMultipleDataPoints; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the group of data points in a given category.
|
|
||||
/// </summary>
|
|
||||
/// <param name="category">The category for which to return the data
|
|
||||
/// point group.</param>
|
|
||||
/// <returns>The group of data points in a given category.</returns>
|
|
||||
protected IGrouping<object, DataPoint> GetDataPointGroup(object category) |
|
||||
{ |
|
||||
return _categoriesWithMultipleDataPoints[category]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a value indicating whether a data point corresponding to
|
|
||||
/// a category is grouped.
|
|
||||
/// </summary>
|
|
||||
/// <param name="category">The category.</param>
|
|
||||
/// <returns>A value indicating whether a data point corresponding to
|
|
||||
/// a category is grouped.</returns>
|
|
||||
protected bool GetIsDataPointGrouped(object category) |
|
||||
{ |
|
||||
return _categoriesWithMultipleDataPoints.ContainsKey(category); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The length of each data point.
|
|
||||
/// </summary>
|
|
||||
private double? _dataPointlength; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the dependent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the independent axis as a category axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ColumnBarBaseSeries class.
|
|
||||
/// </summary>
|
|
||||
protected ColumnBarBaseSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Method run before DataPoints are updated.
|
|
||||
/// </summary>
|
|
||||
protected override void OnBeforeUpdateDataPoints() |
|
||||
{ |
|
||||
base.OnBeforeUpdateDataPoints(); |
|
||||
|
|
||||
CalculateDataPointLength(); |
|
||||
|
|
||||
// Update the list of DataPoints with the same category
|
|
||||
_categoriesWithMultipleDataPoints = ActiveDataPoints |
|
||||
.Where(point => null != point.IndependentValue) |
|
||||
.OrderBy(point => point.DependentValue) |
|
||||
.GroupBy(point => point.IndependentValue) |
|
||||
.Where(grouping => 1 < grouping.Count()) |
|
||||
.ToDictionary(grouping => grouping.Key); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// ResourceDictionary to use for necessary resources.
|
|
||||
/// </returns>
|
|
||||
protected override IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost() |
|
||||
{ |
|
||||
return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates a data point when its actual dependent value has changed.
|
|
||||
/// </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) |
|
||||
{ |
|
||||
UpdateDataPoint(dataPoint); |
|
||||
base.OnDataPointActualDependentValueChanged(dataPoint, oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Redraws other column series to assure they allocate the right amount
|
|
||||
/// of space for their columns.
|
|
||||
/// </summary>
|
|
||||
/// <param name="seriesHost">The series host to update.</param>
|
|
||||
protected void RedrawOtherSeries(ISeriesHost seriesHost) |
|
||||
{ |
|
||||
Type thisType = typeof(ColumnBarBaseSeries<T>); |
|
||||
|
|
||||
// redraw all other column series to ensure they make space for new one
|
|
||||
foreach (ColumnBarBaseSeries<T> series in seriesHost.Series.Where(series => thisType.IsAssignableFrom(series.GetType())).OfType<ColumnBarBaseSeries<T>>().Where(series => series != this)) |
|
||||
{ |
|
||||
series.UpdateDataPoints(series.ActiveDataPoints); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called after data points have been loaded from the items source.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newDataPoints">New active data points.</param>
|
|
||||
/// <param name="oldDataPoints">Old inactive data points.</param>
|
|
||||
protected override void OnDataPointsChanged(IList<DataPoint> newDataPoints, IList<DataPoint> oldDataPoints) |
|
||||
{ |
|
||||
base.OnDataPointsChanged(newDataPoints, oldDataPoints); |
|
||||
|
|
||||
CalculateDataPointLength(); |
|
||||
|
|
||||
if (this.SeriesHost != null) |
|
||||
{ |
|
||||
RedrawOtherSeries(this.SeriesHost); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Redraw other column series when removed from a series host.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old value of the series host property.</param>
|
|
||||
/// <param name="newValue">The new value of the series host property.</param>
|
|
||||
protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) |
|
||||
{ |
|
||||
base.OnSeriesHostPropertyChanged(oldValue, newValue); |
|
||||
|
|
||||
// If being removed from series host, redraw all column series.
|
|
||||
if (newValue == null || oldValue != null) |
|
||||
{ |
|
||||
RedrawOtherSeries(oldValue); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates the bar data point.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A bar data point.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new T(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the length of the data points.
|
|
||||
/// </summary>
|
|
||||
protected void CalculateDataPointLength() |
|
||||
{ |
|
||||
if (!(ActualIndependentAxis is ICategoryAxis)) |
|
||||
{ |
|
||||
IEnumerable<UnitValue> values = |
|
||||
ActiveDataPoints |
|
||||
.Select(dataPoint => ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue)) |
|
||||
.Where(value => ValueHelper.CanGraph(value.Value)) |
|
||||
.OrderBy(value => value.Value) |
|
||||
.ToList(); |
|
||||
|
|
||||
_dataPointlength = |
|
||||
EnumerableFunctions.Zip( |
|
||||
values, |
|
||||
values.Skip(1), |
|
||||
(left, right) => new Range<double>(left.Value, right.Value)) |
|
||||
.Select(range => range.Maximum - range.Minimum) |
|
||||
.MinOrNullable(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <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 override IEnumerable<ValueMargin> GetValueMargins(IValueMarginConsumer consumer) |
|
||||
{ |
|
||||
double dependentValueMargin = this.ActualHeight / 10; |
|
||||
IAxis axis = consumer as IAxis; |
|
||||
if (axis != null && ActiveDataPoints.Any()) |
|
||||
{ |
|
||||
Func<DataPoint, IComparable> selector = null; |
|
||||
if (axis == InternalActualIndependentAxis) |
|
||||
{ |
|
||||
selector = (dataPoint) => (IComparable)dataPoint.ActualIndependentValue; |
|
||||
|
|
||||
DataPoint minimumPoint = ActiveDataPoints.MinOrNull(selector); |
|
||||
DataPoint maximumPoint = ActiveDataPoints.MaxOrNull(selector); |
|
||||
|
|
||||
double minimumMargin = minimumPoint.GetMargin(axis); |
|
||||
yield return new ValueMargin(selector(minimumPoint), minimumMargin, minimumMargin); |
|
||||
|
|
||||
double maximumMargin = maximumPoint.GetMargin(axis); |
|
||||
yield return new ValueMargin(selector(maximumPoint), maximumMargin, maximumMargin); |
|
||||
} |
|
||||
else if (axis == InternalActualDependentAxis) |
|
||||
{ |
|
||||
selector = (dataPoint) => (IComparable)dataPoint.ActualDependentValue; |
|
||||
|
|
||||
DataPoint minimumPoint = ActiveDataPoints.MinOrNull(selector); |
|
||||
DataPoint maximumPoint = ActiveDataPoints.MaxOrNull(selector); |
|
||||
|
|
||||
yield return new ValueMargin(selector(minimumPoint), dependentValueMargin, dependentValueMargin); |
|
||||
yield return new ValueMargin(selector(maximumPoint), dependentValueMargin, dependentValueMargin); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
yield break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a range in which to render a data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="category">The category to retrieve the range for.
|
|
||||
/// </param>
|
|
||||
/// <returns>The range in which to render a data point.</returns>
|
|
||||
protected Range<UnitValue> GetCategoryRange(object category) |
|
||||
{ |
|
||||
ICategoryAxis categoryAxis = ActualIndependentAxis as CategoryAxis; |
|
||||
if (categoryAxis != null) |
|
||||
{ |
|
||||
return categoryAxis.GetPlotAreaCoordinateRange(category); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
UnitValue unitValue = ActualIndependentAxis.GetPlotAreaCoordinate(category); |
|
||||
if (ValueHelper.CanGraph(unitValue.Value) && _dataPointlength.HasValue) |
|
||||
{ |
|
||||
double halfLength = _dataPointlength.Value / 2.0; |
|
||||
|
|
||||
return new Range<UnitValue>( |
|
||||
new UnitValue(unitValue.Value - halfLength, unitValue.Unit), |
|
||||
new UnitValue(unitValue.Value + halfLength, unitValue.Unit)); |
|
||||
} |
|
||||
|
|
||||
return new Range<UnitValue>(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the axis to which the data is anchored.
|
|
||||
/// </summary>
|
|
||||
IRangeAxis IAnchoredToOrigin.AnchoredAxis |
|
||||
{ |
|
||||
get { return this.ActualDependentRangeAxis; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,137 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
|
|
||||
#if !DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in column format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] |
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ColumnDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
public partial class ColumnSeries : ColumnBarBaseSeries<ColumnDataPoint> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ColumnSeries class.
|
|
||||
/// </summary>
|
|
||||
public ColumnSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal category axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.X, |
|
||||
() => new CategoryAxis { Orientation = AxisOrientation.X }, |
|
||||
(axis) => |
|
||||
{ |
|
||||
IRangeAxis rangeAxis = axis as IRangeAxis; |
|
||||
return rangeAxis != null && rangeAxis.Origin != null && axis.Orientation == AxisOrientation.Y; |
|
||||
}, |
|
||||
() => |
|
||||
{ |
|
||||
IRangeAxis rangeAxis = CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
rangeAxis.Orientation = AxisOrientation.Y; |
|
||||
if (rangeAxis == null || rangeAxis.Origin == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
DisplayAxis axis = rangeAxis as DisplayAxis; |
|
||||
if (axis != null) |
|
||||
{ |
|
||||
axis.ShowGridLines = true; |
|
||||
} |
|
||||
return rangeAxis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates each point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to update.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
if (SeriesHost == null || PlotArea == null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
object category = dataPoint.ActualIndependentValue ?? (this.ActiveDataPoints.IndexOf(dataPoint) + 1); |
|
||||
Range<UnitValue> coordinateRange = GetCategoryRange(category); |
|
||||
|
|
||||
if (!coordinateRange.HasData) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
else if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes); |
|
||||
} |
|
||||
|
|
||||
double minimum = (double)coordinateRange.Minimum.Value; |
|
||||
double maximum = (double)coordinateRange.Maximum.Value; |
|
||||
|
|
||||
double plotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
IEnumerable<ColumnSeries> columnSeries = SeriesHost.Series.OfType<ColumnSeries>().Where(series => series.ActualIndependentAxis == ActualIndependentAxis); |
|
||||
int numberOfSeries = columnSeries.Count(); |
|
||||
double coordinateRangeWidth = (maximum - minimum); |
|
||||
double segmentWidth = coordinateRangeWidth * 0.8; |
|
||||
double columnWidth = segmentWidth / numberOfSeries; |
|
||||
int seriesIndex = columnSeries.IndexOf(this); |
|
||||
|
|
||||
double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(dataPoint.ActualDependentValue)).Value; |
|
||||
double zeroPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value; |
|
||||
|
|
||||
double offset = seriesIndex * Math.Round(columnWidth) + coordinateRangeWidth * 0.1; |
|
||||
double dataPointX = minimum + offset; |
|
||||
|
|
||||
if (GetIsDataPointGrouped(category)) |
|
||||
{ |
|
||||
// Multiple DataPoints share this category; offset and overlap them appropriately
|
|
||||
IGrouping<object, DataPoint> categoryGrouping = GetDataPointGroup(category); |
|
||||
int index = categoryGrouping.IndexOf(dataPoint); |
|
||||
dataPointX += (index * (columnWidth * 0.2)) / (categoryGrouping.Count() - 1); |
|
||||
columnWidth *= 0.8; |
|
||||
Canvas.SetZIndex(dataPoint, -index); |
|
||||
} |
|
||||
|
|
||||
if (ValueHelper.CanGraph(dataPointY) && ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(zeroPointY)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
double left = Math.Round(dataPointX); |
|
||||
double width = Math.Round(columnWidth); |
|
||||
|
|
||||
double top = Math.Round(plotAreaHeight - Math.Max(dataPointY, zeroPointY) + 0.5); |
|
||||
double bottom = Math.Round(plotAreaHeight - Math.Min(dataPointY, zeroPointY) + 0.5); |
|
||||
double height = bottom - top + 1; |
|
||||
|
|
||||
Canvas.SetLeft(dataPoint, left); |
|
||||
Canvas.SetTop(dataPoint, top); |
|
||||
dataPoint.Width = width; |
|
||||
dataPoint.Height = height; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
@ -1,265 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
#if DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
#else
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
#endif
|
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as an area chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the DefinitionSeries hierarchy.
|
|
||||
/// </remarks>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(AreaDataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
[StyleTypedProperty(Property = PathStyleName, StyleTargetType = typeof(Path))] |
|
||||
public class AreaSeries : StackedAreaSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the PathStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string PathStyleName = "PathStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Field storing the single SeriesDefinition used by the series.
|
|
||||
/// </summary>
|
|
||||
private SeriesDefinition _definition; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the AreaSeries class.
|
|
||||
/// </summary>
|
|
||||
public AreaSeries() |
|
||||
{ |
|
||||
SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); |
|
||||
SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); |
|
||||
_definition = new SeriesDefinition(); |
|
||||
_definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataShapeStyleProperty, new Binding(PathStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); |
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
_definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); |
|
||||
#endif
|
|
||||
// For compatibility
|
|
||||
DependentValueBinding = new Binding(); |
|
||||
IndependentValueBinding = new Binding(); |
|
||||
SeriesDefinitions.Add(_definition); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sequence of IndependentValueGroups.
|
|
||||
/// </summary>
|
|
||||
protected override IEnumerable<IndependentValueGroup> IndependentValueGroups |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
// Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable
|
|
||||
return DataItems |
|
||||
.Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.DependentValueBinding; } |
|
||||
set { _definition.DependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get { return _definition.DependentValuePath; } |
|
||||
set { _definition.DependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.IndependentValueBinding; } |
|
||||
set { _definition.IndependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get { return _definition.IndependentValuePath; } |
|
||||
set { _definition.IndependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IRangeAxis to use as the dependent axis of the series.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title of the series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the DataPoints of the series.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the LegendItem of the series.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the Path of the series.
|
|
||||
/// </summary>
|
|
||||
public Style PathStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(PathStyleProperty); } |
|
||||
set { SetValue(PathStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the PathStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PathStyleProperty = |
|
||||
DependencyProperty.Register(PathStyleName, typeof(Style), typeof(AreaSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(AreaSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(AreaSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,227 +0,0 @@ |
|||||
// (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.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
|
|
||||
#if DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
#else
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
#endif
|
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as a bar chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the DefinitionSeries hierarchy.
|
|
||||
/// </remarks>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(BarDataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
public class BarSeries : StackedBarSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Field storing the single SeriesDefinition used by the series.
|
|
||||
/// </summary>
|
|
||||
private SeriesDefinition _definition; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the BarSeries class.
|
|
||||
/// </summary>
|
|
||||
public BarSeries() |
|
||||
{ |
|
||||
SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); |
|
||||
SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); |
|
||||
_definition = new SeriesDefinition(); |
|
||||
_definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); |
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
_definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); |
|
||||
#endif
|
|
||||
// For compatibility
|
|
||||
DependentValueBinding = new Binding(); |
|
||||
IndependentValueBinding = new Binding(); |
|
||||
SeriesDefinitions.Add(_definition); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.DependentValueBinding; } |
|
||||
set { _definition.DependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get { return _definition.DependentValuePath; } |
|
||||
set { _definition.DependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.IndependentValueBinding; } |
|
||||
set { _definition.IndependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get { return _definition.IndependentValuePath; } |
|
||||
set { _definition.IndependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IRangeAxis to use as the dependent axis of the series.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title of the series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the DataPoints of the series.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the LegendItem of the series.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(BarSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(BarSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(BarSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,227 +0,0 @@ |
|||||
// (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.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
|
|
||||
#if DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
#else
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
#endif
|
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as a column chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the DefinitionSeries hierarchy.
|
|
||||
/// </remarks>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ColumnDataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
public class ColumnSeries : StackedColumnSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Field storing the single SeriesDefinition used by the series.
|
|
||||
/// </summary>
|
|
||||
private SeriesDefinition _definition; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ColumnSeries class.
|
|
||||
/// </summary>
|
|
||||
public ColumnSeries() |
|
||||
{ |
|
||||
SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); |
|
||||
SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); |
|
||||
_definition = new SeriesDefinition(); |
|
||||
_definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); |
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
_definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); |
|
||||
#endif
|
|
||||
// For compatibility
|
|
||||
DependentValueBinding = new Binding(); |
|
||||
IndependentValueBinding = new Binding(); |
|
||||
SeriesDefinitions.Add(_definition); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.DependentValueBinding; } |
|
||||
set { _definition.DependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get { return _definition.DependentValuePath; } |
|
||||
set { _definition.DependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.IndependentValueBinding; } |
|
||||
set { _definition.IndependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get { return _definition.IndependentValuePath; } |
|
||||
set { _definition.IndependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IRangeAxis to use as the dependent axis of the series.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title of the series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the DataPoints of the series.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the LegendItem of the series.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(ColumnSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(ColumnSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(ColumnSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,268 +0,0 @@ |
|||||
// (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.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
#if DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
#else
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
#endif
|
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as an line chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the DefinitionSeries hierarchy.
|
|
||||
/// </remarks>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
[StyleTypedProperty(Property = PolylineStyleName, StyleTargetType = typeof(Polyline))] |
|
||||
public class LineSeries : StackedLineSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the PolylineStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string PolylineStyleName = "PolylineStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Field storing the single SeriesDefinition used by the series.
|
|
||||
/// </summary>
|
|
||||
private SeriesDefinition _definition; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LineSeries class.
|
|
||||
/// </summary>
|
|
||||
public LineSeries() |
|
||||
{ |
|
||||
SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); |
|
||||
SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); |
|
||||
_definition = new SeriesDefinition(); |
|
||||
_definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataShapeStyleProperty, new Binding(PolylineStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); |
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
_definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); |
|
||||
#endif
|
|
||||
// For compatibility
|
|
||||
DependentValueBinding = new Binding(); |
|
||||
IndependentValueBinding = new Binding(); |
|
||||
SeriesDefinitions.Add(_definition); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sequence of IndependentValueGroups.
|
|
||||
/// </summary>
|
|
||||
protected override IEnumerable<IndependentValueGroup> IndependentValueGroups |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
// Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable
|
|
||||
return DataItems |
|
||||
.Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.DependentValueBinding; } |
|
||||
set { _definition.DependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get { return _definition.DependentValuePath; } |
|
||||
set { _definition.DependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.IndependentValueBinding; } |
|
||||
set { _definition.IndependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get { return _definition.IndependentValuePath; } |
|
||||
set { _definition.IndependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IRangeAxis to use as the dependent axis of the series.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title of the series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the DataPoints of the series.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the LegendItem of the series.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the Path of the series.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Compatible.LineSeries.#PolylineStyle", Justification = "Matches spelling of same-named framework class.")] |
|
||||
public Style PolylineStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(PolylineStyleProperty); } |
|
||||
set { SetValue(PolylineStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the PolylineStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches spelling of same-named framework class.")] |
|
||||
public static readonly DependencyProperty PolylineStyleProperty = |
|
||||
DependencyProperty.Register(PolylineStyleName, typeof(Style), typeof(LineSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(LineSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(LineSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,260 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
|
|
||||
#if DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
#else
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
#endif
|
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as a scatter chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the DefinitionSeries hierarchy.
|
|
||||
/// </remarks>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ScatterDataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
public class ScatterSeries : StackedLineSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Field storing the single SeriesDefinition used by the series.
|
|
||||
/// </summary>
|
|
||||
private SeriesDefinition _definition; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ScatterSeries class.
|
|
||||
/// </summary>
|
|
||||
public ScatterSeries() |
|
||||
{ |
|
||||
SetBinding(DefinitionSeries.DependentAxisProperty, new Binding("DependentRangeAxis") { Source = this }); |
|
||||
SetBinding(DefinitionSeries.SelectionModeProperty, new Binding("IsSelectionEnabled") { Source = this, Converter = new System.Windows.Controls.DataVisualization.Charting.Compatible.SelectionEnabledToSelectionModeConverter() }); |
|
||||
_definition = new SeriesDefinition(); |
|
||||
_definition.SetBinding(SeriesDefinition.ItemsSourceProperty, new Binding("ItemsSource") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TitleProperty, new Binding("Title") { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.DataPointStyleProperty, new Binding(DataPointStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.LegendItemStyleProperty, new Binding(LegendItemStyleName) { Source = this }); |
|
||||
_definition.SetBinding(SeriesDefinition.TransitionDurationProperty, new Binding("TransitionDuration") { Source = this }); |
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
_definition.SetBinding(SeriesDefinition.TransitionEasingFunctionProperty, new Binding("TransitionEasingFunction") { Source = this }); |
|
||||
#endif
|
|
||||
// For compatibility
|
|
||||
DependentValueBinding = new Binding(); |
|
||||
IndependentValueBinding = new Binding(); |
|
||||
SeriesDefinitions.Add(_definition); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Series-appropriate DataPoint instance.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new ScatterDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the shape for the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="definitionPoints">Locations of the points of each SeriesDefinition in the series.</param>
|
|
||||
protected override void UpdateShape(IList<IEnumerable<Point>> definitionPoints) |
|
||||
{ |
|
||||
// Do not call base class implementation; leave shape empty for an easy way to use StackedLineSeries for ScatterSeries
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sequence of IndependentValueGroups.
|
|
||||
/// </summary>
|
|
||||
protected override IEnumerable<IndependentValueGroup> IndependentValueGroups |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
// Base implementation groups by independent value; when plotting a single series in isolation, that's not desirable
|
|
||||
return DataItems |
|
||||
.Select(di => new IndependentValueGroup(di.ActualIndependentValue, new DataItem[] { di })); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.DependentValueBinding; } |
|
||||
set { _definition.DependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the dependent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get { return _definition.DependentValuePath; } |
|
||||
set { _definition.DependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get { return _definition.IndependentValueBinding; } |
|
||||
set { _definition.IndependentValueBinding = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding path that identifies the independent values of the series.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get { return _definition.IndependentValuePath; } |
|
||||
set { _definition.IndependentValuePath = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IRangeAxis to use as the dependent axis of the series.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return (IRangeAxis)GetValue(DependentRangeAxisProperty); } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register("DependentRangeAxis", typeof(IRangeAxis), typeof(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title of the series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the DataPoints of the series.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Style to use for the LegendItem of the series.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether selection is enabled.
|
|
||||
/// </summary>
|
|
||||
public bool IsSelectionEnabled |
|
||||
{ |
|
||||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|
||||
set { SetValue(IsSelectionEnabledProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IsSelectionEnabled dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IsSelectionEnabledProperty = |
|
||||
DependencyProperty.Register("IsSelectionEnabled", typeof(bool), typeof(ScatterSeries), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(ScatterSeries), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(ScatterSeries), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,54 +0,0 @@ |
|||||
// (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.Globalization; |
|
||||
using System.Windows.Data; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting.Compatible |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Converts from a true/false value indicating whether selection is enabled to a SeriesSelectionMode.
|
|
||||
/// </summary>
|
|
||||
internal class SelectionEnabledToSelectionModeConverter : IValueConverter |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the SelectionEnabledToSelectionModeConverter class.
|
|
||||
/// </summary>
|
|
||||
public SelectionEnabledToSelectionModeConverter() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts a value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value produced by the binding source.</param>
|
|
||||
/// <param name="targetType">The type of the binding target property.</param>
|
|
||||
/// <param name="parameter">The converter parameter to use.</param>
|
|
||||
/// <param name="culture">The culture to use in the converter.</param>
|
|
||||
/// <returns>Converted value.</returns>
|
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
|
||||
{ |
|
||||
SeriesSelectionMode selectionMode = SeriesSelectionMode.None; |
|
||||
if ((value is bool) && (bool)value) |
|
||||
{ |
|
||||
selectionMode = SeriesSelectionMode.Single; |
|
||||
} |
|
||||
return selectionMode; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts a value back.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value produced by the binding source.</param>
|
|
||||
/// <param name="targetType">The type of the binding target property.</param>
|
|
||||
/// <param name="parameter">The converter parameter to use.</param>
|
|
||||
/// <param name="culture">The culture to use in the converter.</param>
|
|
||||
/// <returns>Converted value.</returns>
|
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,665 +0,0 @@ |
|||||
// (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).CastWrapper<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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,352 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Globalization; |
|
||||
using System.Windows.Data; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A dynamic series with axes and only one legend item and style for all
|
|
||||
/// data points.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public abstract class DataPointSingleSeriesWithAxes : DataPointSeriesWithAxes, IRequireGlobalSeriesIndex |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the ActualDataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
protected const string ActualDataPointStyleName = "ActualDataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the single legend item associated with the series.
|
|
||||
/// </summary>
|
|
||||
protected LegendItem LegendItem |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (null == _legendItem) |
|
||||
{ |
|
||||
_legendItem = CreateLegendItem(this); |
|
||||
LegendItems.Add(_legendItem); |
|
||||
} |
|
||||
return _legendItem; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the LegendItem for the series.
|
|
||||
/// </summary>
|
|
||||
private LegendItem _legendItem; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the Palette-dispensed ResourceDictionary for the Series.
|
|
||||
/// </summary>
|
|
||||
protected ResourceDictionary PaletteResources { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether a custom title is in use.
|
|
||||
/// </summary>
|
|
||||
private bool CustomTitleInUse { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DataPointStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected override void OnDataPointStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
// Propagate change
|
|
||||
ActualDataPointStyle = newValue; |
|
||||
base.OnDataPointStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
#region protected Style ActualDataPointStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual style used for the data points.
|
|
||||
/// </summary>
|
|
||||
protected Style ActualDataPointStyle |
|
||||
{ |
|
||||
get { return GetValue(ActualDataPointStyleProperty) as Style; } |
|
||||
set { SetValue(ActualDataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
protected static readonly DependencyProperty ActualDataPointStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
ActualDataPointStyleName, |
|
||||
typeof(Style), |
|
||||
typeof(DataPointSingleSeriesWithAxes), |
|
||||
null); |
|
||||
#endregion protected Style ActualDataPointStyle
|
|
||||
|
|
||||
#region protected Style ActualLegendItemStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the actual style used for the legend item.
|
|
||||
/// </summary>
|
|
||||
protected Style ActualLegendItemStyle |
|
||||
{ |
|
||||
get { return GetValue(ActualLegendItemStyleProperty) as Style; } |
|
||||
set { SetValue(ActualLegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualLegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
protected static readonly DependencyProperty ActualLegendItemStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
ActualLegendItemStyleName, |
|
||||
typeof(Style), |
|
||||
typeof(DataPointSeries), |
|
||||
null); |
|
||||
#endregion protected Style ActualLegendItemStyle
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the LegendItemStyle property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected override void OnLegendItemStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
// Propagate change
|
|
||||
ActualLegendItemStyle = newValue; |
|
||||
base.OnLegendItemStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
#region public int? GlobalSeriesIndex
|
|
||||
/// <summary>
|
|
||||
/// Gets the index of the series in the Parent's series collection.
|
|
||||
/// </summary>
|
|
||||
public int? GlobalSeriesIndex |
|
||||
{ |
|
||||
get { return (int?)GetValue(GlobalSeriesIndexProperty); } |
|
||||
private set { SetValue(GlobalSeriesIndexProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the GlobalSeriesIndex dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty GlobalSeriesIndexProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"GlobalSeriesIndex", |
|
||||
typeof(int?), |
|
||||
typeof(Series), |
|
||||
new PropertyMetadata(new int?(), OnGlobalSeriesIndexPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GlobalSeriesIndexProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Series that changed its Index.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnGlobalSeriesIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
DataPointSingleSeriesWithAxes source = (DataPointSingleSeriesWithAxes)d; |
|
||||
int? oldValue = (int?)e.OldValue; |
|
||||
int? newValue = (int?)e.NewValue; |
|
||||
source.OnGlobalSeriesIndexPropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GlobalSeriesIndexProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "newValue+1", Justification = "Impractical to add as many Series as it would take to overflow.")] |
|
||||
protected virtual void OnGlobalSeriesIndexPropertyChanged(int? oldValue, int? newValue) |
|
||||
{ |
|
||||
if (!CustomTitleInUse && (null == GetBindingExpression(TitleProperty))) |
|
||||
{ |
|
||||
Title = newValue.HasValue ? string.Format(CultureInfo.CurrentCulture, Properties.Resources.Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString, newValue.Value + 1) : null; |
|
||||
// Setting Title will set CustomTitleInUse; reset it now
|
|
||||
CustomTitleInUse = false; |
|
||||
} |
|
||||
} |
|
||||
#endregion public int? GlobalSeriesIndex
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the Title property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value of the Title property.</param>
|
|
||||
/// <param name="newValue">New value of the Title property.</param>
|
|
||||
protected override void OnTitleChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
// Title property is being set, so a custom Title is in use
|
|
||||
CustomTitleInUse = true; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the DataPointSingleSeriesWithAxes class.
|
|
||||
/// </summary>
|
|
||||
protected DataPointSingleSeriesWithAxes() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// ResourceDictionary to use for necessary resources.
|
|
||||
/// </returns>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This property does more work than get functions typically do.")] |
|
||||
protected abstract IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Insert grid containing data point used for legend item into the
|
|
||||
/// plot area.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old plot area.</param>
|
|
||||
/// <param name="newValue">The new plot area.</param>
|
|
||||
protected override void OnPlotAreaChanged(Panel oldValue, Panel newValue) |
|
||||
{ |
|
||||
if (newValue != null) |
|
||||
{ |
|
||||
CreateLegendItemDataPoint(); |
|
||||
} |
|
||||
base.OnPlotAreaChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// When the series host property is set retrieves a style to use for all the
|
|
||||
/// data points.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old series host value.</param>
|
|
||||
/// <param name="newValue">The new series host value.</param>
|
|
||||
protected override void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) |
|
||||
{ |
|
||||
base.OnSeriesHostPropertyChanged(oldValue, newValue); |
|
||||
|
|
||||
if (oldValue != null) |
|
||||
{ |
|
||||
oldValue.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged); |
|
||||
} |
|
||||
|
|
||||
if (newValue != null) |
|
||||
{ |
|
||||
newValue.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged); |
|
||||
|
|
||||
DispensedResourcesChanging(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates the LegendItem Control if conditions are right.
|
|
||||
/// </summary>
|
|
||||
private void CreateLegendItemDataPoint() |
|
||||
{ |
|
||||
DataPoint dataPoint = CreateDataPoint(); |
|
||||
if (null != PlotArea) |
|
||||
{ |
|
||||
// Bounce into the visual tree to get default Style applied
|
|
||||
PlotArea.Children.Add(dataPoint); |
|
||||
PlotArea.Children.Remove(dataPoint); |
|
||||
} |
|
||||
dataPoint.SetBinding(DataPoint.StyleProperty, new Binding(ActualDataPointStyleName) { Source = this }); |
|
||||
// Start DataContext null to avoid Binding warnings in the output window
|
|
||||
LegendItem.DataContext = null; |
|
||||
#if !SILVERLIGHT
|
|
||||
if (null == LegendItem.Parent) |
|
||||
{ |
|
||||
#endif
|
|
||||
LegendItem.Loaded += delegate |
|
||||
{ |
|
||||
// Wait for Loaded to set the DataPoint
|
|
||||
LegendItem.DataContext = dataPoint; |
|
||||
}; |
|
||||
#if !SILVERLIGHT
|
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
LegendItem.DataContext = dataPoint; |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called after data points have been loaded from the items source.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newDataPoints">New active data points.</param>
|
|
||||
/// <param name="oldDataPoints">Old inactive data points.</param>
|
|
||||
protected override void OnDataPointsChanged(IList<DataPoint> newDataPoints, IList<DataPoint> oldDataPoints) |
|
||||
{ |
|
||||
base.OnDataPointsChanged(newDataPoints, oldDataPoints); |
|
||||
|
|
||||
if (null != PlotArea) |
|
||||
{ |
|
||||
// Create the Control for use by LegendItem
|
|
||||
// Add it to the visual tree so that its style will be applied
|
|
||||
if (null != LegendItem.DataContext) |
|
||||
{ |
|
||||
PlotArea.Children.Remove(LegendItem.DataContext as UIElement); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the style of the data point to the single style used for all
|
|
||||
/// data points.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to apply the style to.
|
|
||||
/// </param>
|
|
||||
/// <param name="dataContext">The object associated with the data point.
|
|
||||
/// </param>
|
|
||||
protected override void PrepareDataPoint(DataPoint dataPoint, object dataContext) |
|
||||
{ |
|
||||
dataPoint.SetBinding(DataPoint.StyleProperty, new Binding(ActualDataPointStyleName) { Source = this }); |
|
||||
base.PrepareDataPoint(dataPoint, dataContext); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method updates the global series index property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="globalIndex">The global index of the series.</param>
|
|
||||
public void GlobalSeriesIndexChanged(int? globalIndex) |
|
||||
{ |
|
||||
this.GlobalSeriesIndex = globalIndex; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the SeriesHost's ResourceDictionariesChanged event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">ISeriesHost instance.</param>
|
|
||||
/// <param name="e">Event args.</param>
|
|
||||
private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e) |
|
||||
{ |
|
||||
DispensedResourcesChanging(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Processes the change of the DispensedResources property.
|
|
||||
/// </summary>
|
|
||||
private void DispensedResourcesChanging() |
|
||||
{ |
|
||||
if (null != PaletteResources) |
|
||||
{ |
|
||||
Resources.MergedDictionaries.Remove(PaletteResources); |
|
||||
PaletteResources = null; |
|
||||
} |
|
||||
using (IEnumerator<ResourceDictionary> enumerator = GetResourceDictionaryEnumeratorFromHost()) |
|
||||
{ |
|
||||
if (enumerator.MoveNext()) |
|
||||
{ |
|
||||
PaletteResources = |
|
||||
#if SILVERLIGHT
|
|
||||
enumerator.Current.ShallowCopy(); |
|
||||
#else
|
|
||||
enumerator.Current; |
|
||||
#endif
|
|
||||
Resources.MergedDictionaries.Add(PaletteResources); |
|
||||
} |
|
||||
} |
|
||||
CreateLegendItemDataPoint(); |
|
||||
ActualDataPointStyle = DataPointStyle ?? (Resources[DataPointStyleName] as Style); |
|
||||
ActualLegendItemStyle = LegendItemStyle ?? (Resources[LegendItemStyleName] as Style); |
|
||||
Refresh(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,21 +0,0 @@ |
|||||
// (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); |
|
||||
} |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
// (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.ObjectModel; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a series in a chart.
|
|
||||
/// </summary>
|
|
||||
public interface ISeries : IRequireSeriesHost |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets a list of the legend items associated with the object.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<object> LegendItems { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,39 +0,0 @@ |
|||||
// (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 an item used by a Series in the Legend of a Chart.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class LegendItem : ContentControl |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the owner of the LegendItem.
|
|
||||
/// </summary>
|
|
||||
public object Owner { get; set; } |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the LegendItem class.
|
|
||||
/// </summary>
|
|
||||
static LegendItem() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(LegendItem), new FrameworkPropertyMetadata(typeof(LegendItem))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LegendItem class.
|
|
||||
/// </summary>
|
|
||||
public LegendItem() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(LegendItem); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,306 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Windows.Controls.DataVisualization.Collections; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A base class that contains methods used by both the line and area series.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of data point used by the series.</typeparam>
|
|
||||
public abstract class LineAreaBaseSeries<T> : DataPointSingleSeriesWithAxes |
|
||||
where T : DataPoint, new() |
|
||||
{ |
|
||||
#region public IRangeAxis DependentRangeAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the dependent range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] |
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentRangeAxis", |
|
||||
typeof(IRangeAxis), |
|
||||
typeof(LineAreaBaseSeries<T>), |
|
||||
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">LineAreaBaseSeries that changed its DependentRangeAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
LineAreaBaseSeries<T> source = (LineAreaBaseSeries<T>)d; |
|
||||
IRangeAxis newValue = (IRangeAxis)e.NewValue; |
|
||||
source.OnDependentRangeAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) |
|
||||
{ |
|
||||
this.InternalDependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IRangeAxis DependentRangeAxis
|
|
||||
|
|
||||
#region public IAxis IndependentAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the independent range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis IndependentAxis |
|
||||
{ |
|
||||
get { return GetValue(IndependentAxisProperty) as IAxis; } |
|
||||
set { SetValue(IndependentAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This member is necessary because child classes need to share this dependency property.")] |
|
||||
public static readonly DependencyProperty IndependentAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentAxis", |
|
||||
typeof(IAxis), |
|
||||
typeof(LineAreaBaseSeries<T>), |
|
||||
new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">LineAreaBaseSeries that changed its IndependentAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
LineAreaBaseSeries<T> source = (LineAreaBaseSeries<T>)d; |
|
||||
IAxis newValue = (IAxis)e.NewValue; |
|
||||
source.OnIndependentAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnIndependentAxisPropertyChanged(IAxis newValue) |
|
||||
{ |
|
||||
this.InternalIndependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IAxis IndependentAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets data points collection sorted by independent value.
|
|
||||
/// </summary>
|
|
||||
internal OrderedMultipleDictionary<IComparable, DataPoint> DataPointsByIndependentValue { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the independent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the dependent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LineAreaBaseSeries class.
|
|
||||
/// </summary>
|
|
||||
protected LineAreaBaseSeries() |
|
||||
{ |
|
||||
DataPointsByIndependentValue = |
|
||||
new OrderedMultipleDictionary<IComparable, DataPoint>( |
|
||||
false, |
|
||||
(left, right) => |
|
||||
left.CompareTo(right), |
|
||||
(leftDataPoint, rightDataPoint) => |
|
||||
RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint))); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for determining the line color.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
base.OnApplyTemplate(); |
|
||||
if (null != PlotArea) |
|
||||
{ |
|
||||
Grid grid = new Grid(); |
|
||||
DataPoint dataPoint = CreateDataPoint(); |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
dataPoint.Loaded += delegate |
|
||||
{ |
|
||||
dataPoint.SetStyle(ActualDataPointStyle); |
|
||||
Background = dataPoint.Background; |
|
||||
if (null != PlotArea) |
|
||||
{ |
|
||||
PlotArea.Children.Remove(grid); |
|
||||
} |
|
||||
}; |
|
||||
grid.Children.Add(dataPoint); |
|
||||
PlotArea.Children.Add(grid); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called after data points have been loaded from the items source.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newDataPoints">New active data points.</param>
|
|
||||
/// <param name="oldDataPoints">Old inactive data points.</param>
|
|
||||
protected override void OnDataPointsChanged(IList<DataPoint> newDataPoints, IList<DataPoint> oldDataPoints) |
|
||||
{ |
|
||||
base.OnDataPointsChanged(newDataPoints, oldDataPoints); |
|
||||
|
|
||||
if (ActualIndependentAxis is IRangeAxis) |
|
||||
{ |
|
||||
foreach (DataPoint dataPoint in oldDataPoints) |
|
||||
{ |
|
||||
DataPointsByIndependentValue.Remove((IComparable)dataPoint.IndependentValue, dataPoint); |
|
||||
} |
|
||||
|
|
||||
foreach (DataPoint dataPoint in newDataPoints) |
|
||||
{ |
|
||||
DataPointsByIndependentValue.Add((IComparable)dataPoint.IndependentValue, dataPoint); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method executes after all data points have been updated.
|
|
||||
/// </summary>
|
|
||||
protected override void OnAfterUpdateDataPoints() |
|
||||
{ |
|
||||
if (InternalActualDependentAxis != null && InternalActualIndependentAxis != null) |
|
||||
{ |
|
||||
UpdateShape(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Repositions line data point in the sorted collection if the actual
|
|
||||
/// independent axis is a range axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point that has changed.</param>
|
|
||||
/// <param name="oldValue">The old value.</param>
|
|
||||
/// <param name="newValue">The new value.</param>
|
|
||||
protected override void OnDataPointIndependentValueChanged(DataPoint dataPoint, object oldValue, object newValue) |
|
||||
{ |
|
||||
if (ActualIndependentAxis is IRangeAxis && !oldValue.Equals(newValue)) |
|
||||
{ |
|
||||
bool removed = DataPointsByIndependentValue.Remove((IComparable)oldValue, dataPoint); |
|
||||
if (removed) |
|
||||
{ |
|
||||
DataPointsByIndependentValue.Add((IComparable)newValue, dataPoint); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new line data point.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A line data point.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new T(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// ResourceDictionary to use for necessary resources.
|
|
||||
/// </returns>
|
|
||||
protected override IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost() |
|
||||
{ |
|
||||
return GetResourceDictionaryWithTargetType(SeriesHost, typeof(T), true); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the visual representation of the data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to update.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
if (ValueHelper.CanGraph(maximum)) |
|
||||
{ |
|
||||
double x = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; |
|
||||
double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value; |
|
||||
|
|
||||
if (ValueHelper.CanGraph(x) && ValueHelper.CanGraph(y)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
double coordinateY = Math.Round(maximum - (y + (dataPoint.ActualHeight / 2))); |
|
||||
Canvas.SetTop(dataPoint, coordinateY); |
|
||||
double coordinateX = Math.Round(x - (dataPoint.ActualWidth / 2)); |
|
||||
Canvas.SetLeft(dataPoint, coordinateX); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!UpdatingDataPoints) |
|
||||
{ |
|
||||
UpdateShape(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Series shape object.
|
|
||||
/// </summary>
|
|
||||
protected virtual void UpdateShape() |
|
||||
{ |
|
||||
double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
|
|
||||
Func<DataPoint, Point> createPoint = |
|
||||
dataPoint => |
|
||||
new Point( |
|
||||
ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value, |
|
||||
maximum - ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value); |
|
||||
|
|
||||
IEnumerable<Point> points = Enumerable.Empty<Point>(); |
|
||||
if (ValueHelper.CanGraph(maximum)) |
|
||||
{ |
|
||||
if (ActualIndependentAxis is IRangeAxis) |
|
||||
{ |
|
||||
points = DataPointsByIndependentValue.Select(createPoint); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
points = |
|
||||
ActiveDataPoints |
|
||||
.Select(createPoint) |
|
||||
.OrderBy(point => point.X); |
|
||||
} |
|
||||
} |
|
||||
UpdateShapeFromPoints(points); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Series shape object from a collection of Points.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">Collection of Points.</param>
|
|
||||
protected abstract void UpdateShapeFromPoints(IEnumerable<Point> points); |
|
||||
} |
|
||||
} |
|
||||
@ -1,150 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
#if !DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in X/Y
|
|
||||
/// line format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[StyleTypedProperty(Property = "PolylineStyle", StyleTargetType = typeof(Polyline))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] |
|
||||
public partial class LineSeries : LineAreaBaseSeries<LineDataPoint> |
|
||||
{ |
|
||||
#region public PointCollection Points
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of points that make up the line.
|
|
||||
/// </summary>
|
|
||||
public PointCollection Points |
|
||||
{ |
|
||||
get { return GetValue(PointsProperty) as PointCollection; } |
|
||||
private set { SetValue(PointsProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Points dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PointsProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Points", |
|
||||
typeof(PointCollection), |
|
||||
typeof(LineSeries), |
|
||||
null); |
|
||||
#endregion public PointCollection Points
|
|
||||
|
|
||||
#region public Style PolylineStyle
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the style of the Polyline object that follows the data
|
|
||||
/// points.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] |
|
||||
public Style PolylineStyle |
|
||||
{ |
|
||||
get { return GetValue(PolylineStyleProperty) as Style; } |
|
||||
set { SetValue(PolylineStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the PolylineStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] |
|
||||
public static readonly DependencyProperty PolylineStyleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"PolylineStyle", |
|
||||
typeof(Style), |
|
||||
typeof(LineSeries), |
|
||||
null); |
|
||||
#endregion public Style PolylineStyle
|
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the LineSeries class.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] |
|
||||
static LineSeries() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(LineSeries), new FrameworkPropertyMetadata(typeof(LineSeries))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LineSeries class.
|
|
||||
/// </summary>
|
|
||||
public LineSeries() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(LineSeries); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal linear axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.X, |
|
||||
() => |
|
||||
{ |
|
||||
IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
axis = new CategoryAxis(); |
|
||||
} |
|
||||
axis.Orientation = AxisOrientation.X; |
|
||||
return axis; |
|
||||
}, |
|
||||
(axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, |
|
||||
() => |
|
||||
{ |
|
||||
DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
axis.ShowGridLines = true; |
|
||||
axis.Orientation = AxisOrientation.Y; |
|
||||
return axis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Series shape object from a collection of Points.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">Collection of Points.</param>
|
|
||||
protected override void UpdateShapeFromPoints(IEnumerable<Point> points) |
|
||||
{ |
|
||||
if (points.Any()) |
|
||||
{ |
|
||||
PointCollection pointCollection = new PointCollection(); |
|
||||
foreach (Point point in points) |
|
||||
{ |
|
||||
pointCollection.Add(point); |
|
||||
} |
|
||||
Points = pointCollection; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
Points = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
@ -1,597 +0,0 @@ |
|||||
// (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.Collections.ObjectModel; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in pie
|
|
||||
/// format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(PieDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
public partial class PieSeries : DataPointSeries, IResourceDictionaryDispenser, IRequireGlobalSeriesIndex |
|
||||
{ |
|
||||
#region public Collection<ResourceDictionary> Palette
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a palette of ResourceDictionaries used by the series.
|
|
||||
/// </summary>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Want to allow this to be set from XAML.")] |
|
||||
public Collection<ResourceDictionary> Palette |
|
||||
{ |
|
||||
get { return GetValue(PaletteProperty) as Collection<ResourceDictionary>; } |
|
||||
set { SetValue(PaletteProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Palette dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty PaletteProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"Palette", |
|
||||
typeof(Collection<ResourceDictionary>), |
|
||||
typeof(Series), |
|
||||
new PropertyMetadata(OnPalettePropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// PaletteProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">Parent that changed its Palette.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnPalettePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
PieSeries source = d as PieSeries; |
|
||||
Collection<ResourceDictionary> newValue = e.NewValue as Collection<ResourceDictionary>; |
|
||||
source.OnPalettePropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// PaletteProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnPalettePropertyChanged(Collection<ResourceDictionary> newValue) |
|
||||
{ |
|
||||
ResourceDictionaryDispenser.ResourceDictionaries = newValue; |
|
||||
} |
|
||||
#endregion public Collection<ResourceDictionary> Palette
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The pie data point style enumerator.
|
|
||||
/// </summary>
|
|
||||
private IEnumerator<ResourceDictionary> _resourceDictionaryEnumerator; |
|
||||
|
|
||||
#if !SILVERLIGHT
|
|
||||
/// <summary>
|
|
||||
/// Initializes the static members of the PieSeries class.
|
|
||||
/// </summary>
|
|
||||
static PieSeries() |
|
||||
{ |
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(PieSeries), new FrameworkPropertyMetadata(typeof(PieSeries))); |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the PieSeries class.
|
|
||||
/// </summary>
|
|
||||
public PieSeries() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
this.DefaultStyleKey = typeof(PieSeries); |
|
||||
#endif
|
|
||||
this.ResourceDictionaryDispenser = new ResourceDictionaryDispenser(); |
|
||||
ResourceDictionaryDispenser.ResourceDictionariesChanged += delegate |
|
||||
{ |
|
||||
OnResourceDictionariesChanged(EventArgs.Empty); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Invokes the ResourceDictionariesChanged event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
protected virtual void OnResourceDictionariesChanged(EventArgs e) |
|
||||
{ |
|
||||
// Update with new styles
|
|
||||
Refresh(); |
|
||||
|
|
||||
// Forward event on to listeners
|
|
||||
EventHandler handler = ResourceDictionariesChanged; |
|
||||
if (null != handler) |
|
||||
{ |
|
||||
handler.Invoke(this, e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A dictionary that links data points to their legend items.
|
|
||||
/// </summary>
|
|
||||
private Dictionary<DataPoint, LegendItem> _dataPointLegendItems = new Dictionary<DataPoint, LegendItem>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Accepts a ratio of a full rotation, the x and y length and returns
|
|
||||
/// the 2D point using trigonometric functions.
|
|
||||
/// </summary>
|
|
||||
/// <param name="ratio">The ratio of a full rotation [0..1].</param>
|
|
||||
/// <param name="radiusX">The x radius.</param>
|
|
||||
/// <param name="radiusY">The y radius.</param>
|
|
||||
/// <returns>The corresponding 2D point.</returns>
|
|
||||
private static Point ConvertRatioOfRotationToPoint(double ratio, double radiusX, double radiusY) |
|
||||
{ |
|
||||
double radians = (((ratio * 360) - 90) * (Math.PI / 180)); |
|
||||
return new Point(radiusX * Math.Cos(radians), radiusY * Math.Sin(radians)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a legend item for each data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point added.</param>
|
|
||||
protected override void AddDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
base.AddDataPoint(dataPoint); |
|
||||
PieDataPoint pieDataPoint = (PieDataPoint)dataPoint; |
|
||||
|
|
||||
int index = ActiveDataPoints.IndexOf(dataPoint) + 1; |
|
||||
LegendItem legendItem = CreatePieLegendItem(dataPoint, index); |
|
||||
|
|
||||
// Grab a style enumerator if we don't have one already.
|
|
||||
if (_resourceDictionaryEnumerator == null) |
|
||||
{ |
|
||||
_resourceDictionaryEnumerator = GetResourceDictionaryWithTargetType(this, typeof(PieDataPoint), true); |
|
||||
} |
|
||||
|
|
||||
if (_resourceDictionaryEnumerator.MoveNext()) |
|
||||
{ |
|
||||
ResourceDictionary paletteResources = |
|
||||
#if SILVERLIGHT
|
|
||||
_resourceDictionaryEnumerator.Current.ShallowCopy(); |
|
||||
#else
|
|
||||
_resourceDictionaryEnumerator.Current; |
|
||||
#endif
|
|
||||
pieDataPoint.PaletteResources = paletteResources; |
|
||||
pieDataPoint.Resources.MergedDictionaries.Add(paletteResources); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
pieDataPoint.PaletteResources = null; |
|
||||
} |
|
||||
pieDataPoint.ActualDataPointStyle = DataPointStyle ?? pieDataPoint.Resources[DataPointStyleName] as Style; |
|
||||
pieDataPoint.SetBinding(PieDataPoint.StyleProperty, new Binding(PieDataPoint.ActualDataPointStyleName) { Source = pieDataPoint }); |
|
||||
pieDataPoint.ActualLegendItemStyle = LegendItemStyle ?? (pieDataPoint.Resources[LegendItemStyleName] as Style); |
|
||||
legendItem.SetBinding(LegendItem.StyleProperty, new Binding(ActualLegendItemStyleName) { Source = pieDataPoint }); |
|
||||
|
|
||||
_dataPointLegendItems[dataPoint] = legendItem; |
|
||||
LegendItems.Add(legendItem); |
|
||||
UpdateLegendItemIndexes(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes data point's legend item when the data point is removed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to remove.</param>
|
|
||||
protected override void RemoveDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
base.RemoveDataPoint(dataPoint); |
|
||||
if (dataPoint != null) |
|
||||
{ |
|
||||
LegendItem legendItem = _dataPointLegendItems[dataPoint]; |
|
||||
_dataPointLegendItems.Remove(dataPoint); |
|
||||
|
|
||||
LegendItems.Remove(legendItem); |
|
||||
UpdateLegendItemIndexes(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a data point.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A data point.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new PieDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the active pie data points.
|
|
||||
/// </summary>
|
|
||||
private IEnumerable<PieDataPoint> ActivePieDataPoints |
|
||||
{ |
|
||||
get { return ActiveDataPoints.OfType<PieDataPoint>(); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates all ratios before data points are updated.
|
|
||||
/// </summary>
|
|
||||
protected override void OnBeforeUpdateDataPoints() |
|
||||
{ |
|
||||
UpdateRatios(); |
|
||||
|
|
||||
base.OnBeforeUpdateDataPoints(); |
|
||||
} |
|
||||
|
|
||||
/// <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) |
|
||||
{ |
|
||||
UpdateDataPoints(newDataPoints); |
|
||||
base.OnDataPointsChanged(newDataPoints, oldDataPoints); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the indexes of all legend items when a change is made to the collection.
|
|
||||
/// </summary>
|
|
||||
private void UpdateLegendItemIndexes() |
|
||||
{ |
|
||||
int index = 0; |
|
||||
foreach (DataPoint dataPoint in ActiveDataPoints) |
|
||||
{ |
|
||||
LegendItem legendItem = _dataPointLegendItems[dataPoint]; |
|
||||
legendItem.Content = dataPoint.IndependentValue ?? (index + 1); |
|
||||
index++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the ratios of each data point.
|
|
||||
/// </summary>
|
|
||||
private void UpdateRatios() |
|
||||
{ |
|
||||
double sum = ActivePieDataPoints.Select(pieDataPoint => Math.Abs(ValueHelper.ToDouble(pieDataPoint.DependentValue))).Sum(); |
|
||||
|
|
||||
// Priming the loop by calculating initial value of
|
|
||||
// offset ratio and its corresponding points.
|
|
||||
double offsetRatio = 0; |
|
||||
foreach (PieDataPoint dataPoint in ActivePieDataPoints) |
|
||||
{ |
|
||||
double dependentValue = Math.Abs(ValueHelper.ToDouble(dataPoint.DependentValue)); |
|
||||
double ratio = dependentValue / sum; |
|
||||
if (!ValueHelper.CanGraph(ratio)) |
|
||||
{ |
|
||||
ratio = 0.0; |
|
||||
} |
|
||||
dataPoint.Ratio = ratio; |
|
||||
dataPoint.OffsetRatio = offsetRatio; |
|
||||
offsetRatio += ratio; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates a data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to update.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
PieDataPoint pieDataPoint = (PieDataPoint) dataPoint; |
|
||||
pieDataPoint.Width = ActualWidth; |
|
||||
pieDataPoint.Height = ActualHeight; |
|
||||
UpdatePieDataPointGeometry(pieDataPoint, ActualWidth, ActualHeight); |
|
||||
Canvas.SetLeft(pieDataPoint, 0); |
|
||||
Canvas.SetTop(pieDataPoint, 0); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the PieDataPoint's Geometry property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="pieDataPoint">PieDataPoint instance.</param>
|
|
||||
/// <param name="plotAreaWidth">PlotArea width.</param>
|
|
||||
/// <param name="plotAreaHeight">PlotArea height.</param>
|
|
||||
internal static void UpdatePieDataPointGeometry(PieDataPoint pieDataPoint, double plotAreaWidth, double plotAreaHeight) |
|
||||
{ |
|
||||
double diameter = (plotAreaWidth < plotAreaHeight) ? plotAreaWidth : plotAreaHeight; |
|
||||
diameter *= 0.95; |
|
||||
double plotAreaRadius = diameter / 2; |
|
||||
double maxDistanceFromCenter = 0.0; |
|
||||
double sliceRadius = plotAreaRadius - maxDistanceFromCenter; |
|
||||
|
|
||||
Point translatePoint = new Point(plotAreaWidth / 2, plotAreaHeight / 2); |
|
||||
|
|
||||
if (pieDataPoint.ActualRatio == 1) |
|
||||
{ |
|
||||
foreach (DependencyProperty dependencyProperty in new DependencyProperty[] { PieDataPoint.GeometryProperty, PieDataPoint.GeometrySelectionProperty, PieDataPoint.GeometryHighlightProperty }) |
|
||||
{ |
|
||||
Geometry geometry = |
|
||||
new EllipseGeometry |
|
||||
{ |
|
||||
Center = translatePoint, |
|
||||
RadiusX = sliceRadius, |
|
||||
RadiusY = sliceRadius |
|
||||
}; |
|
||||
pieDataPoint.SetValue(dependencyProperty, geometry); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (pieDataPoint.ActualRatio == 0.0) |
|
||||
{ |
|
||||
pieDataPoint.Geometry = null; |
|
||||
pieDataPoint.GeometryHighlight = null; |
|
||||
pieDataPoint.GeometrySelection = null; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
double ratio = pieDataPoint.ActualRatio; |
|
||||
double offsetRatio = pieDataPoint.ActualOffsetRatio; |
|
||||
double currentRatio = offsetRatio + ratio; |
|
||||
|
|
||||
Point offsetRatioPoint = ConvertRatioOfRotationToPoint(offsetRatio, sliceRadius, sliceRadius); |
|
||||
|
|
||||
Point adjustedOffsetRatioPoint = offsetRatioPoint.Translate(translatePoint); |
|
||||
|
|
||||
// Calculate the last clockwise point in the pie slice
|
|
||||
Point currentRatioPoint = |
|
||||
ConvertRatioOfRotationToPoint(currentRatio, sliceRadius, sliceRadius); |
|
||||
|
|
||||
// Adjust point using center of plot area as origin
|
|
||||
// instead of 0,0
|
|
||||
Point adjustedCurrentRatioPoint = |
|
||||
currentRatioPoint.Translate(translatePoint); |
|
||||
|
|
||||
foreach (DependencyProperty dependencyProperty in new DependencyProperty[] { PieDataPoint.GeometryProperty, PieDataPoint.GeometrySelectionProperty, PieDataPoint.GeometryHighlightProperty }) |
|
||||
{ |
|
||||
// Creating the pie slice geometry object
|
|
||||
PathFigure pathFigure = new PathFigure { IsClosed = true }; |
|
||||
pathFigure.StartPoint = translatePoint; |
|
||||
pathFigure.Segments.Add(new LineSegment { Point = adjustedOffsetRatioPoint }); |
|
||||
bool isLargeArc = (currentRatio - offsetRatio) > 0.5; |
|
||||
pathFigure.Segments.Add( |
|
||||
new ArcSegment |
|
||||
{ |
|
||||
Point = adjustedCurrentRatioPoint, |
|
||||
IsLargeArc = isLargeArc, |
|
||||
Size = new Size(sliceRadius, sliceRadius), |
|
||||
SweepDirection = SweepDirection.Clockwise |
|
||||
}); |
|
||||
|
|
||||
PathGeometry pathGeometry = new PathGeometry(); |
|
||||
pathGeometry.Figures.Add(pathFigure); |
|
||||
pieDataPoint.SetValue(dependencyProperty, pathGeometry); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a legend item from a data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to use to create the legend item.</param>
|
|
||||
/// <param name="index">The 1-based index of the Control.</param>
|
|
||||
/// <returns>The series host legend item.</returns>
|
|
||||
protected virtual LegendItem CreatePieLegendItem(DataPoint dataPoint, int index) |
|
||||
{ |
|
||||
LegendItem legendItem = CreateLegendItem(this); |
|
||||
// Set the Content of the LegendItem
|
|
||||
legendItem.Content = dataPoint.IndependentValue ?? index; |
|
||||
// Create a representative DataPoint for access to styled properties
|
|
||||
DataPoint legendDataPoint = CreateDataPoint(); |
|
||||
legendDataPoint.DataContext = dataPoint.DataContext; |
|
||||
if (null != PlotArea) |
|
||||
{ |
|
||||
// Bounce into the visual tree to get default Style applied
|
|
||||
PlotArea.Children.Add(legendDataPoint); |
|
||||
PlotArea.Children.Remove(legendDataPoint); |
|
||||
} |
|
||||
legendDataPoint.SetBinding(DataPoint.StyleProperty, new Binding(PieDataPoint.ActualDataPointStyleName) { Source = dataPoint }); |
|
||||
legendItem.DataContext = legendDataPoint; |
|
||||
return legendItem; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Attach event handlers to a data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point.</param>
|
|
||||
protected override void AttachEventHandlersToDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
PieDataPoint pieDataPoint = dataPoint as PieDataPoint; |
|
||||
|
|
||||
pieDataPoint.ActualRatioChanged += OnPieDataPointActualRatioChanged; |
|
||||
pieDataPoint.ActualOffsetRatioChanged += OnPieDataPointActualOffsetRatioChanged; |
|
||||
pieDataPoint.RatioChanged += OnPieDataPointRatioChanged; |
|
||||
pieDataPoint.OffsetRatioChanged += OnPieDataPointOffsetRatioChanged; |
|
||||
|
|
||||
base.AttachEventHandlersToDataPoint(dataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Detaches event handlers from a data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point.</param>
|
|
||||
protected override void DetachEventHandlersFromDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
PieDataPoint pieDataPoint = dataPoint as PieDataPoint; |
|
||||
|
|
||||
pieDataPoint.ActualRatioChanged -= OnPieDataPointActualRatioChanged; |
|
||||
pieDataPoint.ActualOffsetRatioChanged -= OnPieDataPointActualOffsetRatioChanged; |
|
||||
pieDataPoint.RatioChanged -= OnPieDataPointRatioChanged; |
|
||||
pieDataPoint.OffsetRatioChanged -= OnPieDataPointOffsetRatioChanged; |
|
||||
|
|
||||
base.DetachEventHandlersFromDataPoint(dataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method updates the global series index property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="globalIndex">The global index of the series.</param>
|
|
||||
public void GlobalSeriesIndexChanged(int? globalIndex) |
|
||||
{ |
|
||||
// Do nothing because we want to use up an index but do nothing
|
|
||||
// with it.
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the dependent value is changed.
|
|
||||
/// </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) |
|
||||
{ |
|
||||
UpdateRatios(); |
|
||||
base.OnDataPointDependentValueChanged(dataPoint, oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the independent value is changed.
|
|
||||
/// </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) |
|
||||
{ |
|
||||
_dataPointLegendItems[dataPoint].Content = newValue; |
|
||||
base.OnDataPointIndependentValueChanged(dataPoint, oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the pie data point's actual ratio is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void OnPieDataPointActualRatioChanged(object sender, RoutedPropertyChangedEventArgs<double> args) |
|
||||
{ |
|
||||
UpdateDataPoint(sender as DataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the pie data point's actual offset ratio
|
|
||||
/// is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void OnPieDataPointActualOffsetRatioChanged(object sender, RoutedPropertyChangedEventArgs<double> args) |
|
||||
{ |
|
||||
UpdateDataPoint(sender as DataPoint); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the pie data point's ratio is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void OnPieDataPointRatioChanged(object sender, RoutedPropertyChangedEventArgs<double> args) |
|
||||
{ |
|
||||
DataPoint dataPoint = sender as DataPoint; |
|
||||
dataPoint.BeginAnimation(PieDataPoint.ActualRatioProperty, "ActualRatio", args.NewValue, TransitionDuration, this.TransitionEasingFunction); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the data point when the pie data point's offset ratio is
|
|
||||
/// changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">The source of the event.</param>
|
|
||||
/// <param name="args">Information about the event.</param>
|
|
||||
private void OnPieDataPointOffsetRatioChanged(object sender, RoutedPropertyChangedEventArgs<double> args) |
|
||||
{ |
|
||||
DataPoint dataPoint = sender as DataPoint; |
|
||||
dataPoint.BeginAnimation(PieDataPoint.ActualOffsetRatioProperty, "ActualOffsetRatio", args.NewValue, TransitionDuration, this.TransitionEasingFunction); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets an object used to dispense styles from the style
|
|
||||
/// palette.
|
|
||||
/// </summary>
|
|
||||
private ResourceDictionaryDispenser ResourceDictionaryDispenser { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Event that is invoked when the ResourceDictionaryDispenser's collection has changed.
|
|
||||
/// </summary>
|
|
||||
public event EventHandler ResourceDictionariesChanged; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a rotating enumerator of ResourceDictionary objects that coordinates
|
|
||||
/// with the dispenser object to ensure that no two enumerators are on the same
|
|
||||
/// item. If the dispenser is reset or its collection is changed then the
|
|
||||
/// enumerators are also reset.
|
|
||||
/// </summary>
|
|
||||
/// <param name="predicate">A predicate that returns a value indicating
|
|
||||
/// whether to return an item.</param>
|
|
||||
/// <returns>An enumerator of ResourceDictionaries.</returns>
|
|
||||
public IEnumerator<ResourceDictionary> GetResourceDictionariesWhere(Func<ResourceDictionary, bool> predicate) |
|
||||
{ |
|
||||
return ResourceDictionaryDispenser.GetResourceDictionariesWhere(predicate); |
|
||||
} |
|
||||
|
|
||||
/// <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) |
|
||||
{ |
|
||||
base.OnSeriesHostPropertyChanged(oldValue, newValue); |
|
||||
|
|
||||
if (null != oldValue) |
|
||||
{ |
|
||||
oldValue.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged); |
|
||||
} |
|
||||
|
|
||||
if (null != newValue) |
|
||||
{ |
|
||||
newValue.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Dispose of the enumerator.
|
|
||||
if (null != _resourceDictionaryEnumerator) |
|
||||
{ |
|
||||
_resourceDictionaryEnumerator.Dispose(); |
|
||||
_resourceDictionaryEnumerator = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
this.ResourceDictionaryDispenser.Parent = newValue; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the SeriesHost's ResourceDictionariesChanged event.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">ISeriesHost instance.</param>
|
|
||||
/// <param name="e">Event args.</param>
|
|
||||
private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e) |
|
||||
{ |
|
||||
Refresh(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DataPointStyleProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected override void OnDataPointStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
// Propagate change
|
|
||||
foreach (PieDataPoint pieDataPoint in ActiveDataPoints) |
|
||||
{ |
|
||||
pieDataPoint.ActualDataPointStyle = newValue ?? (pieDataPoint.Resources[DataPointStyleName] as Style); |
|
||||
} |
|
||||
base.OnDataPointStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the value of the LegendItemStyle property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
protected override void OnLegendItemStylePropertyChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
// Propagate change
|
|
||||
foreach (PieDataPoint pieDataPoint in ActiveDataPoints) |
|
||||
{ |
|
||||
pieDataPoint.ActualLegendItemStyle = newValue ?? (pieDataPoint.Resources[LegendItemStyleName] as Style); |
|
||||
} |
|
||||
base.OnLegendItemStylePropertyChanged(oldValue, newValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,205 +0,0 @@ |
|||||
// (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; |
|
||||
|
|
||||
#if !DEFINITION_SERIES_COMPATIBILITY_MODE
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series to be rendered in X/Y scatter format.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(ScatterDataPoint))] |
|
||||
[StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] |
|
||||
[TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] |
|
||||
public partial class ScatterSeries : DataPointSingleSeriesWithAxes |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ScatterSeries class.
|
|
||||
/// </summary>
|
|
||||
public ScatterSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the dependent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis ActualDependentRangeAxis { get { return this.InternalActualDependentAxis as IRangeAxis; } } |
|
||||
|
|
||||
#region public IRangeAxis DependentRangeAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the dependent range axis.
|
|
||||
/// </summary>
|
|
||||
public IRangeAxis DependentRangeAxis |
|
||||
{ |
|
||||
get { return GetValue(DependentRangeAxisProperty) as IRangeAxis; } |
|
||||
set { SetValue(DependentRangeAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DependentRangeAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DependentRangeAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"DependentRangeAxis", |
|
||||
typeof(IRangeAxis), |
|
||||
typeof(ScatterSeries), |
|
||||
new PropertyMetadata(null, OnDependentRangeAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">ScatterSeries that changed its DependentRangeAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnDependentRangeAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
ScatterSeries source = (ScatterSeries)d; |
|
||||
IRangeAxis newValue = (IRangeAxis)e.NewValue; |
|
||||
source.OnDependentRangeAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// DependentRangeAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnDependentRangeAxisPropertyChanged(IRangeAxis newValue) |
|
||||
{ |
|
||||
this.InternalDependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IRangeAxis DependentRangeAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the independent axis as a range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis ActualIndependentAxis { get { return this.InternalActualIndependentAxis as IAxis; } } |
|
||||
|
|
||||
#region public IAxis IndependentAxis
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the independent range axis.
|
|
||||
/// </summary>
|
|
||||
public IAxis IndependentAxis |
|
||||
{ |
|
||||
get { return GetValue(IndependentAxisProperty) as IAxis; } |
|
||||
set { SetValue(IndependentAxisProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the IndependentAxis dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty IndependentAxisProperty = |
|
||||
DependencyProperty.Register( |
|
||||
"IndependentAxis", |
|
||||
typeof(IAxis), |
|
||||
typeof(ScatterSeries), |
|
||||
new PropertyMetadata(null, OnIndependentAxisPropertyChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="d">ScatterSeries that changed its IndependentAxis.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnIndependentAxisPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
ScatterSeries source = (ScatterSeries)d; |
|
||||
IAxis newValue = (IAxis)e.NewValue; |
|
||||
source.OnIndependentAxisPropertyChanged(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IndependentAxisProperty property changed handler.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnIndependentAxisPropertyChanged(IAxis newValue) |
|
||||
{ |
|
||||
this.InternalIndependentAxis = (IAxis)newValue; |
|
||||
} |
|
||||
#endregion public IAxis IndependentAxis
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquire a horizontal linear axis and a vertical linear axis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstDataPoint">The first data point.</param>
|
|
||||
protected override void GetAxes(DataPoint firstDataPoint) |
|
||||
{ |
|
||||
GetAxes( |
|
||||
firstDataPoint, |
|
||||
(axis) => axis.Orientation == AxisOrientation.X, |
|
||||
() => |
|
||||
{ |
|
||||
IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
axis = new CategoryAxis(); |
|
||||
} |
|
||||
axis.Orientation = AxisOrientation.X; |
|
||||
return axis; |
|
||||
}, |
|
||||
(axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, |
|
||||
() => |
|
||||
{ |
|
||||
DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); |
|
||||
if (axis == null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); |
|
||||
} |
|
||||
axis.ShowGridLines = true; |
|
||||
axis.Orientation = AxisOrientation.Y; |
|
||||
return axis; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new scatter data point.
|
|
||||
/// </summary>
|
|
||||
/// <returns>A scatter data point.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new ScatterDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the custom ResourceDictionary to use for necessary resources.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// ResourceDictionary to use for necessary resources.
|
|
||||
/// </returns>
|
|
||||
protected override IEnumerator<ResourceDictionary> GetResourceDictionaryEnumeratorFromHost() |
|
||||
{ |
|
||||
return GetResourceDictionaryWithTargetType(SeriesHost, typeof(ScatterDataPoint), true); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// This method updates a single data point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">The data point to update.</param>
|
|
||||
protected override void UpdateDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
double PlotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
double dataPointX = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value; |
|
||||
double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value; |
|
||||
|
|
||||
if (ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(dataPointY)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
// Set the Position
|
|
||||
Canvas.SetLeft( |
|
||||
dataPoint, |
|
||||
Math.Round(dataPointX - (dataPoint.ActualWidth / 2))); |
|
||||
Canvas.SetTop( |
|
||||
dataPoint, |
|
||||
Math.Round(PlotAreaHeight - (dataPointY + (dataPoint.ActualHeight / 2)))); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#endif
|
|
||||
@ -1,113 +0,0 @@ |
|||||
// (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.ObjectModel; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a control that contains a data series.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public abstract partial class Series : Control, ISeries, IRequireSeriesHost |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The name of the Title property.
|
|
||||
/// </summary>
|
|
||||
protected const string TitleName = "Title"; |
|
||||
|
|
||||
#region public ISeriesHost SeriesHost
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the parent instance the Series belongs to.
|
|
||||
/// </summary>
|
|
||||
public ISeriesHost SeriesHost |
|
||||
{ |
|
||||
get { return _seriesHost; } |
|
||||
set |
|
||||
{ |
|
||||
ISeriesHost oldValue = _seriesHost; |
|
||||
_seriesHost = value; |
|
||||
if (oldValue != _seriesHost) |
|
||||
{ |
|
||||
OnSeriesHostPropertyChanged(oldValue, _seriesHost); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the Parent instance the Series belongs to.
|
|
||||
/// </summary>
|
|
||||
private ISeriesHost _seriesHost; |
|
||||
|
|
||||
/// <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 virtual void OnSeriesHostPropertyChanged(ISeriesHost oldValue, ISeriesHost newValue) |
|
||||
{ |
|
||||
if (newValue != null && oldValue != null) |
|
||||
{ |
|
||||
throw new InvalidOperationException(Properties.Resources.Series_SeriesHost_SeriesHostPropertyNotNull); |
|
||||
} |
|
||||
} |
|
||||
#endregion public ISeriesHost SeriesHost
|
|
||||
|
|
||||
#region public ObservableCollection<object> LegendItems
|
|
||||
/// <summary>
|
|
||||
/// Gets the legend items to be added to the legend.
|
|
||||
/// </summary>
|
|
||||
public ObservableCollection<object> LegendItems { get; private set; } |
|
||||
#endregion public ObservableCollection<object> LegendItems
|
|
||||
|
|
||||
#region public object Title
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the title content of the Series.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register( |
|
||||
TitleName, |
|
||||
typeof(object), |
|
||||
typeof(Series), |
|
||||
new PropertyMetadata(OnTitleChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// TitleProperty property changed callback.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">Series for which the Title changed.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private static void OnTitleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((Series)o).OnTitleChanged(e.OldValue, e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the Title property changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">The old value of the Title property.</param>
|
|
||||
/// <param name="newValue">The new value of the Title property.</param>
|
|
||||
protected virtual void OnTitleChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
} |
|
||||
#endregion public object Title
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Series class.
|
|
||||
/// </summary>
|
|
||||
protected Series() |
|
||||
{ |
|
||||
LegendItems = new NoResetObservableCollection<object>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,635 +0,0 @@ |
|||||
// (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.ObjectModel; |
|
||||
using System.Collections.Specialized; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Globalization; |
|
||||
using System.Windows.Controls.Primitives; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media.Animation; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines the attributes of a series that is to be rendered by the DefinitionSeries class.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
[StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(DataPoint))] |
|
||||
[StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] |
|
||||
[StyleTypedProperty(Property = DataShapeStyleName, StyleTargetType = typeof(Shape))] |
|
||||
public class SeriesDefinition : FrameworkElement, ISeries, IRequireGlobalSeriesIndex |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Name of the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataPointStyleName = "DataPointStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string LegendItemStyleName = "LegendItemStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Name of the DataShapeStyle property.
|
|
||||
/// </summary>
|
|
||||
private const string DataShapeStyleName = "DataShapeStyle"; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides the store for the ISeries.LegendItems property.
|
|
||||
/// </summary>
|
|
||||
private readonly ObservableCollection<object> _legendItems = new ObservableCollection<object>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents the single LegendItem corresponding to the SeriesDefinition.
|
|
||||
/// </summary>
|
|
||||
private readonly LegendItem _legendItem; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Keeps a reference to the WeakEventListener used to prevent leaks of collections assigned to the ItemsSource property.
|
|
||||
/// </summary>
|
|
||||
private WeakEventListener<SeriesDefinition, object, NotifyCollectionChangedEventArgs> _weakEventListener; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the index of the series definition.
|
|
||||
/// </summary>
|
|
||||
internal int Index { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the SeriesDefinition class.
|
|
||||
/// </summary>
|
|
||||
public SeriesDefinition() |
|
||||
{ |
|
||||
_legendItem = new LegendItem { Owner = this }; |
|
||||
_legendItem.SetBinding(LegendItem.ContentProperty, new Binding("ActualTitle") { Source = this }); |
|
||||
_legendItem.SetBinding(LegendItem.StyleProperty, new Binding("ActualLegendItemStyle") { Source = this }); |
|
||||
_legendItems.Add(_legendItem); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a sequence that provides the content of the series.
|
|
||||
/// </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(SeriesDefinition), new PropertyMetadata(OnItemsSourceChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the ItemsSource dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">DependencyObject that changed.</param>
|
|
||||
/// <param name="e">Event data for the DependencyPropertyChangedEvent.</param>
|
|
||||
private static void OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((SeriesDefinition)o).OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the ItemsSource property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
private void OnItemsSourceChanged(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<SeriesDefinition, object, NotifyCollectionChangedEventArgs>(this); |
|
||||
_weakEventListener.OnEventAction = (instance, source, eventArgs) => instance.ItemsSourceCollectionChanged(source, eventArgs); |
|
||||
_weakEventListener.OnDetachAction = (weakEventListener) => newValueINotifyCollectionChanged.CollectionChanged -= weakEventListener.OnEvent; |
|
||||
newValueINotifyCollectionChanged.CollectionChanged += _weakEventListener.OnEvent; |
|
||||
} |
|
||||
|
|
||||
if (null != ParentDefinitionSeries) |
|
||||
{ |
|
||||
ParentDefinitionSeries.SeriesDefinitionItemsSourceChanged(this, oldValue, newValue); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the CollectionChanged event for the ItemsSource property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">Event arguments..</param>
|
|
||||
private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (null != ParentDefinitionSeries) |
|
||||
{ |
|
||||
ParentDefinitionSeries.SeriesDefinitionItemsSourceCollectionChanged(this, e.Action, e.OldItems, e.OldStartingIndex, e.NewItems, e.NewStartingIndex); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the automatic title of the series definition.
|
|
||||
/// </summary>
|
|
||||
private object AutomaticTitle |
|
||||
{ |
|
||||
get { return _automaticTitle; } |
|
||||
set |
|
||||
{ |
|
||||
_automaticTitle = value; |
|
||||
ActualTitle = Title ?? _automaticTitle; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the automatic title of the series definition.
|
|
||||
/// </summary>
|
|
||||
private object _automaticTitle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Title of the series definition.
|
|
||||
/// </summary>
|
|
||||
public object Title |
|
||||
{ |
|
||||
get { return (object)GetValue(TitleProperty); } |
|
||||
set { SetValue(TitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TitleProperty = |
|
||||
DependencyProperty.Register("Title", typeof(object), typeof(SeriesDefinition), new PropertyMetadata(OnTitleChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the Title dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">DependencyObject that changed.</param>
|
|
||||
/// <param name="e">Event data for the DependencyPropertyChangedEvent.</param>
|
|
||||
private static void OnTitleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((SeriesDefinition)o).OnTitleChanged((object)e.OldValue, (object)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the Title property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] |
|
||||
private void OnTitleChanged(object oldValue, object newValue) |
|
||||
{ |
|
||||
ActualTitle = newValue ?? _automaticTitle; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the rendered Title of the series definition.
|
|
||||
/// </summary>
|
|
||||
public object ActualTitle |
|
||||
{ |
|
||||
get { return (object)GetValue(ActualTitleProperty); } |
|
||||
protected set { SetValue(ActualTitleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualTitle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualTitleProperty = |
|
||||
DependencyProperty.Register("ActualTitle", typeof(object), typeof(SeriesDefinition), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the DataPoint Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
internal Style PaletteDataPointStyle |
|
||||
{ |
|
||||
get { return _paletteDataPointStyle; } |
|
||||
set |
|
||||
{ |
|
||||
_paletteDataPointStyle = value; |
|
||||
ActualDataPointStyle = DataPointStyle ?? _paletteDataPointStyle; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the DataPoint Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
private Style _paletteDataPointStyle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the DataPoint Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style DataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataPointStyleProperty); } |
|
||||
set { SetValue(DataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataPointStyleProperty = |
|
||||
DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataPointStyleChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the DataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">DependencyObject that changed.</param>
|
|
||||
/// <param name="e">Event data for the DependencyPropertyChangedEvent.</param>
|
|
||||
private static void OnDataPointStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((SeriesDefinition)o).OnDataPointStyleChanged((Style)e.OldValue, (Style)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the DataPointStyle property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] |
|
||||
private void OnDataPointStyleChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
ActualDataPointStyle = newValue ?? _paletteDataPointStyle; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the rendered DataPoint Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style ActualDataPointStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(ActualDataPointStyleProperty); } |
|
||||
protected set { SetValue(ActualDataPointStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualDataPointStyleProperty = |
|
||||
DependencyProperty.Register("ActualDataPointStyle", typeof(Style), typeof(SeriesDefinition), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the LegendItem Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
internal Style PaletteLegendItemStyle |
|
||||
{ |
|
||||
get { return _paletteLegendItemStyle; } |
|
||||
set |
|
||||
{ |
|
||||
_paletteLegendItemStyle = value; |
|
||||
ActualLegendItemStyle = LegendItemStyle ?? _paletteLegendItemStyle; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the LegendItem Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
private Style _paletteLegendItemStyle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the LegendItem Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style LegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(LegendItemStyleProperty); } |
|
||||
set { SetValue(LegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty LegendItemStyleProperty = |
|
||||
DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnLegendItemStyleChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the LegendItemStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">DependencyObject that changed.</param>
|
|
||||
/// <param name="e">Event data for the DependencyPropertyChangedEvent.</param>
|
|
||||
private static void OnLegendItemStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((SeriesDefinition)o).OnLegendItemStyleChanged((Style)e.OldValue, (Style)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the LegendItemStyle property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] |
|
||||
private void OnLegendItemStyleChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
ActualLegendItemStyle = newValue ?? _paletteLegendItemStyle; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the rendered LegendItem Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style ActualLegendItemStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(ActualLegendItemStyleProperty); } |
|
||||
protected set { SetValue(ActualLegendItemStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDataPointStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualLegendItemStyleProperty = |
|
||||
DependencyProperty.Register("ActualLegendItemStyle", typeof(Style), typeof(SeriesDefinition), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the DataShape Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
internal Style PaletteDataShapeStyle |
|
||||
{ |
|
||||
get { return _paletteDataShapeStyle; } |
|
||||
set |
|
||||
{ |
|
||||
_paletteDataShapeStyle = value; |
|
||||
ActualDataShapeStyle = DataShapeStyle ?? _paletteDataShapeStyle; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the DataShape Style from the SeriesHost's Palette.
|
|
||||
/// </summary>
|
|
||||
private Style _paletteDataShapeStyle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the DataShape Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style DataShapeStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(DataShapeStyleProperty); } |
|
||||
set { SetValue(DataShapeStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the DataShapeStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty DataShapeStyleProperty = |
|
||||
DependencyProperty.Register(DataShapeStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataShapeStyleChanged)); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the DataShapeStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">DependencyObject that changed.</param>
|
|
||||
/// <param name="e">Event data for the DependencyPropertyChangedEvent.</param>
|
|
||||
private static void OnDataShapeStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
((SeriesDefinition)o).OnDataShapeStyleChanged((Style)e.OldValue, (Style)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the DataShapeStyle property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldValue">Old value.</param>
|
|
||||
/// <param name="newValue">New value.</param>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")] |
|
||||
private void OnDataShapeStyleChanged(Style oldValue, Style newValue) |
|
||||
{ |
|
||||
ActualDataShapeStyle = newValue ?? _paletteDataShapeStyle; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the rendered DataShape Style for the series definition.
|
|
||||
/// </summary>
|
|
||||
public Style ActualDataShapeStyle |
|
||||
{ |
|
||||
get { return (Style)GetValue(ActualDataShapeStyleProperty); } |
|
||||
protected set { SetValue(ActualDataShapeStyleProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ActualDataShapeStyle dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ActualDataShapeStyleProperty = |
|
||||
DependencyProperty.Register("ActualDataShapeStyle", typeof(Style), typeof(SeriesDefinition), null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding to use for identifying the dependent value.
|
|
||||
/// </summary>
|
|
||||
public Binding DependentValueBinding |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _dependentValueBinding; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (value != _dependentValueBinding) |
|
||||
{ |
|
||||
_dependentValueBinding = value; |
|
||||
Reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The binding used to identify the dependent value binding.
|
|
||||
/// </summary>
|
|
||||
private Binding _dependentValueBinding; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding Path to use for identifying the dependent value.
|
|
||||
/// </summary>
|
|
||||
public string DependentValuePath |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return (null != DependentValueBinding) ? DependentValueBinding.Path.Path : null; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (null == value) |
|
||||
{ |
|
||||
DependentValueBinding = null; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
DependentValueBinding = new Binding(value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding to use for identifying the independent value.
|
|
||||
/// </summary>
|
|
||||
public Binding IndependentValueBinding |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _independentValueBinding; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (_independentValueBinding != value) |
|
||||
{ |
|
||||
_independentValueBinding = value; |
|
||||
Reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The binding used to identify the independent value binding.
|
|
||||
/// </summary>
|
|
||||
private Binding _independentValueBinding; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Binding Path to use for identifying the independent value.
|
|
||||
/// </summary>
|
|
||||
public string IndependentValuePath |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return (null != IndependentValueBinding) ? IndependentValueBinding.Path.Path : null; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
if (null == value) |
|
||||
{ |
|
||||
IndependentValueBinding = null; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
IndependentValueBinding = new Binding(value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Resets the display of the series definition.
|
|
||||
/// </summary>
|
|
||||
private void Reset() |
|
||||
{ |
|
||||
if (null != ParentDefinitionSeries) |
|
||||
{ |
|
||||
ParentDefinitionSeries.SeriesDefinitionItemsSourceChanged(this, ItemsSource, ItemsSource); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the SeriesHost as a DefinitionSeries instance.
|
|
||||
/// </summary>
|
|
||||
private DefinitionSeries ParentDefinitionSeries |
|
||||
{ |
|
||||
get { return (DefinitionSeries)((ISeries)this).SeriesHost; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the collection of legend items for the series definition.
|
|
||||
/// </summary>
|
|
||||
ObservableCollection<object> ISeries.LegendItems |
|
||||
{ |
|
||||
get { return _legendItems; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the SeriesHost for the series definition.
|
|
||||
/// </summary>
|
|
||||
ISeriesHost IRequireSeriesHost.SeriesHost |
|
||||
{ |
|
||||
get { return _seriesHost; } |
|
||||
set |
|
||||
{ |
|
||||
_seriesHost = value; |
|
||||
if (!(_seriesHost is DefinitionSeries) && (null != value)) |
|
||||
{ |
|
||||
throw new NotSupportedException(Properties.Resources.SeriesDefinition_SeriesHost_InvalidParent); |
|
||||
} |
|
||||
|
|
||||
if (null != _seriesHost) |
|
||||
{ |
|
||||
DataPoint legendItemDataPoint = ((DefinitionSeries)_seriesHost).InternalCreateDataPoint(); |
|
||||
#if SILVERLIGHT
|
|
||||
// Apply default style (hard)
|
|
||||
ContentPresenter container = new ContentPresenter { Content = legendItemDataPoint, Width = 1, Height = 1 }; |
|
||||
Popup popup = new Popup { Child = container }; |
|
||||
container.SizeChanged += delegate |
|
||||
{ |
|
||||
popup.Child = null; |
|
||||
popup.IsOpen = false; |
|
||||
}; |
|
||||
popup.IsOpen = true; |
|
||||
#else
|
|
||||
// Apply default style (easy)
|
|
||||
ContentControl contentControl = new ContentControl(); |
|
||||
contentControl.Content = legendItemDataPoint; |
|
||||
contentControl.Content = null; |
|
||||
#endif
|
|
||||
legendItemDataPoint.SetBinding(DataPoint.StyleProperty, new Binding("ActualDataPointStyle") { Source = this }); |
|
||||
_legendItem.DataContext = legendItemDataPoint; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the SeriesHost for the series definition.
|
|
||||
/// </summary>
|
|
||||
private ISeriesHost _seriesHost; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the global series index of the series definition.
|
|
||||
/// </summary>
|
|
||||
/// <param name="globalIndex">New index.</param>
|
|
||||
void IRequireGlobalSeriesIndex.GlobalSeriesIndexChanged(int? globalIndex) |
|
||||
{ |
|
||||
if (globalIndex.HasValue) |
|
||||
{ |
|
||||
AutomaticTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString, globalIndex + 1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the TimeSpan to use for the duration of data transitions.
|
|
||||
/// </summary>
|
|
||||
public TimeSpan TransitionDuration |
|
||||
{ |
|
||||
get { return (TimeSpan)GetValue(TransitionDurationProperty); } |
|
||||
set { SetValue(TransitionDurationProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionDuration dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionDurationProperty = |
|
||||
DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(SeriesDefinition), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); |
|
||||
|
|
||||
#if !NO_EASING_FUNCTIONS
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the IEasingFunction to use for data transitions.
|
|
||||
/// </summary>
|
|
||||
public IEasingFunction TransitionEasingFunction |
|
||||
{ |
|
||||
get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } |
|
||||
set { SetValue(TransitionEasingFunctionProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransitionEasingFunctionProperty = |
|
||||
DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(SeriesDefinition), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); |
|
||||
#else
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a placeholder for the TransitionEasingFunction dependency property.
|
|
||||
/// </summary>
|
|
||||
internal IEasingFunction TransitionEasingFunction { get; set; } |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,28 +0,0 @@ |
|||||
// (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 the selection behavior for a series.
|
|
||||
/// </summary>
|
|
||||
public enum SeriesSelectionMode |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Selection is disabled.
|
|
||||
/// </summary>
|
|
||||
None, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The user can select only one item at a time.
|
|
||||
/// </summary>
|
|
||||
Single, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The user can select multiple items without holding down a modifier key.
|
|
||||
/// </summary>
|
|
||||
Multiple, |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a 100% stacked area chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class Stacked100AreaSeries : StackedAreaSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Stacked100AreaSeries class.
|
|
||||
/// </summary>
|
|
||||
public Stacked100AreaSeries() |
|
||||
{ |
|
||||
IsStacked100 = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a 100% stacked bar chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class Stacked100BarSeries : StackedBarSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Stacked100BarSeries class.
|
|
||||
/// </summary>
|
|
||||
public Stacked100BarSeries() |
|
||||
{ |
|
||||
IsStacked100 = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a 100% stacked column chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class Stacked100ColumnSeries : StackedColumnSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Stacked100ColumnSeries class.
|
|
||||
/// </summary>
|
|
||||
public Stacked100ColumnSeries() |
|
||||
{ |
|
||||
IsStacked100 = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a 100% stacked line chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class Stacked100LineSeries : StackedLineSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the Stacked100LineSeries class.
|
|
||||
/// </summary>
|
|
||||
public Stacked100LineSeries() |
|
||||
{ |
|
||||
IsStacked100 = true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,354 +0,0 @@ |
|||||
// (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.Specialized; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Data; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control base class for displaying values as a stacked area/line chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public abstract class StackedAreaLineSeries : DefinitionSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the Shapes corresponding to each SeriesDefinition.
|
|
||||
/// </summary>
|
|
||||
protected Dictionary<SeriesDefinition, Shape> SeriesDefinitionShapes { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedAreaLineSeries class.
|
|
||||
/// </summary>
|
|
||||
protected StackedAreaLineSeries() |
|
||||
{ |
|
||||
SeriesDefinitionShapes = new Dictionary<SeriesDefinition, Shape>(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Builds the visual tree for the control when a new template is applied.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
SynchronizeSeriesDefinitionShapes(SeriesDefinitions, null); |
|
||||
base.OnApplyTemplate(); |
|
||||
SynchronizeSeriesDefinitionShapes(null, SeriesDefinitions); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called when the SeriesDefinitions collection changes.
|
|
||||
/// </summary>
|
|
||||
/// <param name="action">Type of change.</param>
|
|
||||
/// <param name="oldItems">Sequence of old items.</param>
|
|
||||
/// <param name="oldStartingIndex">Starting index of old items.</param>
|
|
||||
/// <param name="newItems">Sequence of new items.</param>
|
|
||||
/// <param name="newStartingIndex">Starting index of new items.</param>
|
|
||||
protected override void SeriesDefinitionsCollectionChanged(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex) |
|
||||
{ |
|
||||
base.SeriesDefinitionsCollectionChanged(action, oldItems, oldStartingIndex, newItems, newStartingIndex); |
|
||||
if (null != oldItems) |
|
||||
{ |
|
||||
SynchronizeSeriesDefinitionShapes(oldItems.CastWrapper<SeriesDefinition>(), null); |
|
||||
foreach (SeriesDefinition oldDefinition in oldItems.CastWrapper<SeriesDefinition>()) |
|
||||
{ |
|
||||
SeriesDefinitionShapes.Remove(oldDefinition); |
|
||||
} |
|
||||
} |
|
||||
if (null != newItems) |
|
||||
{ |
|
||||
foreach (SeriesDefinition newDefinition in newItems.CastWrapper<SeriesDefinition>()) |
|
||||
{ |
|
||||
Shape dataShape = CreateDataShape(); |
|
||||
dataShape.SetBinding(Shape.StyleProperty, new Binding("ActualDataShapeStyle") { Source = newDefinition }); |
|
||||
SeriesDefinitionShapes[newDefinition] = dataShape; |
|
||||
} |
|
||||
SynchronizeSeriesDefinitionShapes(null, newItems.CastWrapper<SeriesDefinition>()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquires a dependent axis suitable for use with the data values of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Axis instance.</returns>
|
|
||||
protected override IAxis AcquireDependentAxis() |
|
||||
{ |
|
||||
IAxis dependentAxis = SeriesHost.Axes |
|
||||
.Where(a => (a.Orientation == AxisOrientation.Y) && (a is IRangeAxis) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualDependentValue))) |
|
||||
.FirstOrDefault(); |
|
||||
if (null == dependentAxis) |
|
||||
{ |
|
||||
LinearAxis linearAxis = new LinearAxis { Orientation = AxisOrientation.Y, ShowGridLines = true }; |
|
||||
if (IsStacked100) |
|
||||
{ |
|
||||
Style style = new Style(typeof(AxisLabel)); |
|
||||
style.Setters.Add(new Setter(AxisLabel.StringFormatProperty, "{0}%")); |
|
||||
linearAxis.AxisLabelStyle = style; |
|
||||
} |
|
||||
dependentAxis = linearAxis; |
|
||||
} |
|
||||
return dependentAxis; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquires an independent axis suitable for use with the data values of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Axis instance.</returns>
|
|
||||
protected override IAxis AcquireIndependentAxis() |
|
||||
{ |
|
||||
IAxis independentAxis = SeriesHost.Axes |
|
||||
.Where(a => (a.Orientation == AxisOrientation.X) && ((a is IRangeAxis) || (a is ICategoryAxis)) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualIndependentValue))) |
|
||||
.FirstOrDefault(); |
|
||||
if (null == independentAxis) |
|
||||
{ |
|
||||
object probeValue = DataItems.Any() ? DataItems.First().ActualIndependentValue : null; |
|
||||
double convertedDouble; |
|
||||
DateTime convertedDateTime; |
|
||||
if ((null != probeValue) && ValueHelper.TryConvert(probeValue, out convertedDouble)) |
|
||||
{ |
|
||||
independentAxis = new LinearAxis(); |
|
||||
} |
|
||||
else if ((null != probeValue) && ValueHelper.TryConvert(probeValue, out convertedDateTime)) |
|
||||
{ |
|
||||
independentAxis = new DateTimeAxis(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
independentAxis = new CategoryAxis(); |
|
||||
} |
|
||||
independentAxis.Orientation = AxisOrientation.X; |
|
||||
} |
|
||||
return independentAxis; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Prepares a DataPoint for use.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataPoint">DataPoint instance.</param>
|
|
||||
protected override void PrepareDataPoint(DataPoint dataPoint) |
|
||||
{ |
|
||||
base.PrepareDataPoint(dataPoint); |
|
||||
dataPoint.SizeChanged += new SizeChangedEventHandler(DataPointSizeChanged); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles the SizeChanged event of a DataPoint to update the value margins for the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sender">Event source.</param>
|
|
||||
/// <param name="e">Event arguments.</param>
|
|
||||
private void DataPointSizeChanged(object sender, SizeChangedEventArgs e) |
|
||||
{ |
|
||||
DataPoint dataPoint = (DataPoint)sender; |
|
||||
DataItem dataItem = DataItemFromDataPoint(dataPoint); |
|
||||
|
|
||||
// Update placement
|
|
||||
double newWidth = e.NewSize.Width; |
|
||||
double newHeight = e.NewSize.Height; |
|
||||
Canvas.SetLeft(dataItem.Container, Math.Round(dataItem.CenterPoint.X - (newWidth / 2))); |
|
||||
Canvas.SetTop(dataItem.Container, Math.Round(dataItem.CenterPoint.Y - (newHeight / 2))); |
|
||||
|
|
||||
// Update value margins
|
|
||||
double heightMargin = newHeight * (3.0 / 4.0); |
|
||||
NotifyValueMarginsChanged(ActualDependentAxis, new ValueMargin[] { new ValueMargin(dataItem.ActualStackedDependentValue, heightMargin, heightMargin) }); |
|
||||
double widthMargin = newWidth * (3.0 / 4.0); |
|
||||
NotifyValueMarginsChanged(ActualIndependentAxis, new ValueMargin[] { new ValueMargin(dataPoint.ActualIndependentValue, widthMargin, widthMargin) }); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a series-appropriate Shape for connecting the points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Shape instance.</returns>
|
|
||||
protected abstract Shape CreateDataShape(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Synchronizes the SeriesDefinitionShapes dictionary with the contents of the SeriesArea Panel.
|
|
||||
/// </summary>
|
|
||||
/// <param name="oldDefinitions">SeriesDefinition being removed.</param>
|
|
||||
/// <param name="newDefinitions">SeriesDefinition being added.</param>
|
|
||||
private void SynchronizeSeriesDefinitionShapes(IEnumerable<SeriesDefinition> oldDefinitions, IEnumerable<SeriesDefinition> newDefinitions) |
|
||||
{ |
|
||||
if (null != SeriesArea) |
|
||||
{ |
|
||||
if (null != oldDefinitions) |
|
||||
{ |
|
||||
foreach (SeriesDefinition oldDefinition in oldDefinitions) |
|
||||
{ |
|
||||
SeriesArea.Children.Remove(SeriesDefinitionShapes[oldDefinition]); |
|
||||
} |
|
||||
} |
|
||||
if (null != newDefinitions) |
|
||||
{ |
|
||||
foreach (SeriesDefinition newDefinition in newDefinitions.OrderBy(sd => sd.Index)) |
|
||||
{ |
|
||||
SeriesArea.Children.Insert(newDefinition.Index, SeriesDefinitionShapes[newDefinition]); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the range for the data points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rangeConsumer">Consumer of the range.</param>
|
|
||||
/// <returns>Range of values.</returns>
|
|
||||
protected override Range<IComparable> IRangeProviderGetRange(IRangeConsumer rangeConsumer) |
|
||||
{ |
|
||||
if (rangeConsumer == ActualDependentAxis) |
|
||||
{ |
|
||||
IEnumerable<Range<double>> dependentValueRangesByIndependentValue = IndependentValueDependentValues |
|
||||
.Select(g => g.Where(d => ValueHelper.CanGraph(d))) |
|
||||
.Select(g => g.Scan(0.0, (s, t) => s + t).Skip(1).GetRange()) |
|
||||
.DefaultIfEmpty(new Range<double>(0, 0)) |
|
||||
.ToArray(); |
|
||||
double minimum = dependentValueRangesByIndependentValue.Min(r => r.Minimum); |
|
||||
double maximum = dependentValueRangesByIndependentValue.Max(r => r.Maximum); |
|
||||
|
|
||||
if (IsStacked100) |
|
||||
{ |
|
||||
minimum = Math.Min(minimum, 0); |
|
||||
maximum = Math.Max(maximum, 0); |
|
||||
} |
|
||||
|
|
||||
return new Range<IComparable>(minimum, maximum); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.IRangeProviderGetRange(rangeConsumer); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value margins for the data points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="valueMarginConsumer">Consumer of the value margins.</param>
|
|
||||
/// <returns>Sequence of value margins.</returns>
|
|
||||
protected override IEnumerable<ValueMargin> IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) |
|
||||
{ |
|
||||
if (IsStacked100 && (valueMarginConsumer == ActualDependentAxis)) |
|
||||
{ |
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
else if ((valueMarginConsumer == ActualDependentAxis) || (valueMarginConsumer == ActualIndependentAxis)) |
|
||||
{ |
|
||||
Range<IComparable> range = IRangeProviderGetRange((IRangeConsumer)valueMarginConsumer); |
|
||||
double margin = DataItems |
|
||||
.Select(di => |
|
||||
{ |
|
||||
return (null != di.DataPoint) ? |
|
||||
(valueMarginConsumer == ActualDependentAxis) ? di.DataPoint.ActualHeight : di.DataPoint.ActualWidth : |
|
||||
0; |
|
||||
}) |
|
||||
.Average() * (3.0 / 4.0); |
|
||||
return new ValueMargin[] |
|
||||
{ |
|
||||
new ValueMargin(range.Minimum, margin, margin), |
|
||||
new ValueMargin(range.Maximum, margin, margin), |
|
||||
}; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the placement of the DataItems (data points) of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataItems">DataItems in need of an update.</param>
|
|
||||
protected override void UpdateDataItemPlacement(IEnumerable<DefinitionSeries.DataItem> dataItems) |
|
||||
{ |
|
||||
if ((null != ActualDependentAxis) && (null != ActualIndependentAxis)) |
|
||||
{ |
|
||||
double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
double lineTopBuffer = 1; |
|
||||
List<Point>[] points = new List<Point>[SeriesDefinitions.Count]; |
|
||||
for (int i = 0; i < points.Length; i++) |
|
||||
{ |
|
||||
points[i] = new List<Point>(); |
|
||||
} |
|
||||
foreach (IndependentValueGroup group in IndependentValueGroupsOrderedByIndependentValue) |
|
||||
{ |
|
||||
double sum = IsStacked100 ? |
|
||||
group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : |
|
||||
1; |
|
||||
if (0 == sum) |
|
||||
{ |
|
||||
sum = 1; |
|
||||
} |
|
||||
double x = ActualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue).Value; |
|
||||
if (ValueHelper.CanGraph(x)) |
|
||||
{ |
|
||||
double lastValue = 0; |
|
||||
Point lastPoint = new Point(x, Math.Max(plotAreaMaximumDependentCoordinate - ActualDependentRangeAxis.GetPlotAreaCoordinate(lastValue).Value, lineTopBuffer)); |
|
||||
int i = -1; |
|
||||
SeriesDefinition lastDefinition = null; |
|
||||
foreach (DataItem dataItem in group.DataItems) |
|
||||
{ |
|
||||
if (lastDefinition != dataItem.SeriesDefinition) |
|
||||
{ |
|
||||
i++; |
|
||||
} |
|
||||
|
|
||||
while (dataItem.SeriesDefinition != SeriesDefinitions[i]) |
|
||||
{ |
|
||||
points[i].Add(lastPoint); |
|
||||
i++; |
|
||||
} |
|
||||
|
|
||||
DataPoint dataPoint = dataItem.DataPoint; |
|
||||
double value = IsStacked100 ? |
|
||||
(ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue) * (100 / sum)) : |
|
||||
ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue); |
|
||||
if (ValueHelper.CanGraph(value)) |
|
||||
{ |
|
||||
value += lastValue; |
|
||||
dataItem.ActualStackedDependentValue = value; |
|
||||
double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(value).Value; |
|
||||
lastValue = value; |
|
||||
lastPoint.Y = Math.Max(plotAreaMaximumDependentCoordinate - y, lineTopBuffer); |
|
||||
points[i].Add(lastPoint); |
|
||||
|
|
||||
dataItem.CenterPoint = new Point(x, plotAreaMaximumDependentCoordinate - y); |
|
||||
double left = dataItem.CenterPoint.X - (dataPoint.ActualWidth / 2); |
|
||||
double top = dataItem.CenterPoint.Y - (dataPoint.ActualHeight / 2); |
|
||||
|
|
||||
Canvas.SetLeft(dataItem.Container, Math.Round(left)); |
|
||||
Canvas.SetTop(dataItem.Container, Math.Round(top)); |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
points[i].Add(lastPoint); |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
|
|
||||
lastDefinition = dataItem.SeriesDefinition; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
UpdateShape(points); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Shape for the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="definitionPoints">Locations of the points of each SeriesDefinition in the series.</param>
|
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nesting is convenient way to represent data.")] |
|
||||
protected abstract void UpdateShape(IList<IEnumerable<Point>> definitionPoints); |
|
||||
} |
|
||||
} |
|
||||
@ -1,143 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as a stacked area chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class StackedAreaSeries : StackedAreaLineSeries, IAnchoredToOrigin |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedAreaSeries class.
|
|
||||
/// </summary>
|
|
||||
public StackedAreaSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Series-appropriate DataPoint instance.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new AreaDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a series-appropriate Shape for connecting the points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Shape instance.</returns>
|
|
||||
protected override Shape CreateDataShape() |
|
||||
{ |
|
||||
return new Polygon(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the Shape for the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="definitionPoints">Locations of the points of each SeriesDefinition in the series.</param>
|
|
||||
protected override void UpdateShape(IList<IEnumerable<Point>> definitionPoints) |
|
||||
{ |
|
||||
for (int i = SeriesDefinitions.Count - 1; 0 < i; i--) |
|
||||
{ |
|
||||
PointCollection pointCollection = new PointCollection(); |
|
||||
IEnumerable<Point> topPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i].OrderBy(p => p.X) : definitionPoints[i]; |
|
||||
foreach (Point p in topPoints) |
|
||||
{ |
|
||||
pointCollection.Add(p); |
|
||||
} |
|
||||
IEnumerable<Point> bottomPoints = (ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i - 1].OrderByDescending(p => p.X) : definitionPoints[i - 1].Reverse(); |
|
||||
foreach (Point p in bottomPoints) |
|
||||
{ |
|
||||
pointCollection.Add(p); |
|
||||
} |
|
||||
SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[i]], pointCollection); |
|
||||
} |
|
||||
if (1 <= SeriesDefinitions.Count) |
|
||||
{ |
|
||||
double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
IComparable zeroValue = ActualDependentRangeAxis.Origin ?? 0.0; |
|
||||
if (zeroValue.CompareTo(ActualDependentRangeAxis.Range.Minimum) < 0) |
|
||||
{ |
|
||||
zeroValue = ActualDependentRangeAxis.Range.Minimum; |
|
||||
} |
|
||||
if (0 < zeroValue.CompareTo(ActualDependentRangeAxis.Range.Maximum)) |
|
||||
{ |
|
||||
zeroValue = ActualDependentRangeAxis.Range.Maximum; |
|
||||
} |
|
||||
double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(zeroValue).Value; |
|
||||
PointCollection pointCollection = new PointCollection(); |
|
||||
Point[] topPoints = ((ActualIndependentAxis is ICategoryAxis) ? definitionPoints[0].OrderBy(p => p.X) : definitionPoints[0]).ToArray(); |
|
||||
foreach (Point p in topPoints) |
|
||||
{ |
|
||||
pointCollection.Add(p); |
|
||||
} |
|
||||
if (0 < topPoints.Length) |
|
||||
{ |
|
||||
Point firstPoint = topPoints[0]; |
|
||||
Point lastPoint = topPoints[topPoints.Length - 1]; |
|
||||
pointCollection.Add(new Point(lastPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); |
|
||||
pointCollection.Add(new Point(firstPoint.X, plotAreaMaximumDependentCoordinate - zeroCoordinate)); |
|
||||
} |
|
||||
SetPolygonPointsProperty((Polygon)SeriesDefinitionShapes[SeriesDefinitions[0]], pointCollection); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the Points property of a Polygon to the specified PointCollection.
|
|
||||
/// </summary>
|
|
||||
/// <param name="polygon">Polygon to set the Points property of.</param>
|
|
||||
/// <param name="pointCollection">Specified PointCollection.</param>
|
|
||||
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Silverlight implementation is not static.")] |
|
||||
protected void SetPolygonPointsProperty(Polygon polygon, PointCollection pointCollection) |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
// Changing .Points during an Arrange pass can create a layout cycle on Silverlight
|
|
||||
if (!polygon.Points.SequenceEqual(pointCollection)) |
|
||||
{ |
|
||||
#endif
|
|
||||
polygon.Points = pointCollection; |
|
||||
#if SILVERLIGHT
|
|
||||
// In rare cases, Silverlight doesn't update the line visual to match the new points;
|
|
||||
// calling InvalidateArrange works around that problem.
|
|
||||
polygon.InvalidateArrange(); |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value margins for the data points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="valueMarginConsumer">Consumer of the value margins.</param>
|
|
||||
/// <returns>Sequence of value margins.</returns>
|
|
||||
protected override IEnumerable<ValueMargin> IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) |
|
||||
{ |
|
||||
if (valueMarginConsumer == ActualIndependentAxis) |
|
||||
{ |
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the anchored axis for the series.
|
|
||||
/// </summary>
|
|
||||
IRangeAxis IAnchoredToOrigin.AnchoredAxis |
|
||||
{ |
|
||||
get { return ActualDependentRangeAxis; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,371 +0,0 @@ |
|||||
// (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.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control base class for displaying values as a stacked bar/column chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public abstract class StackedBarColumnSeries : DefinitionSeries, IAnchoredToOrigin |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the orientation of the dependent axis.
|
|
||||
/// </summary>
|
|
||||
protected AxisOrientation DependentAxisOrientation { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the orientation of the independent axis.
|
|
||||
/// </summary>
|
|
||||
protected AxisOrientation IndependentAxisOrientation { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedBarColumnSeries class.
|
|
||||
/// </summary>
|
|
||||
protected StackedBarColumnSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquires a dependent axis suitable for use with the data values of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Axis instance.</returns>
|
|
||||
protected override IAxis AcquireDependentAxis() |
|
||||
{ |
|
||||
IAxis dependentAxis = SeriesHost.Axes |
|
||||
.Where(a => (a.Orientation == DependentAxisOrientation) && (a is IRangeAxis) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualDependentValue))) |
|
||||
.FirstOrDefault(); |
|
||||
if (null == dependentAxis) |
|
||||
{ |
|
||||
LinearAxis linearAxis = new LinearAxis { Orientation = DependentAxisOrientation, ShowGridLines = true }; |
|
||||
if (IsStacked100) |
|
||||
{ |
|
||||
Style style = new Style(typeof(AxisLabel)); |
|
||||
style.Setters.Add(new Setter(AxisLabel.StringFormatProperty, "{0}%")); |
|
||||
linearAxis.AxisLabelStyle = style; |
|
||||
} |
|
||||
dependentAxis = linearAxis; |
|
||||
} |
|
||||
return dependentAxis; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Acquires an independent axis suitable for use with the data values of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Axis instance.</returns>
|
|
||||
protected override IAxis AcquireIndependentAxis() |
|
||||
{ |
|
||||
IAxis independentAxis = SeriesHost.Axes |
|
||||
.Where(a => (a.Orientation == IndependentAxisOrientation) && ((a is ICategoryAxis) || (a is IRangeAxis)) && DataItems.Any() && (a.CanPlot(DataItems.First().ActualIndependentValue))) |
|
||||
.FirstOrDefault(); |
|
||||
if (null == independentAxis) |
|
||||
{ |
|
||||
independentAxis = new CategoryAxis { Orientation = IndependentAxisOrientation }; |
|
||||
} |
|
||||
return independentAxis; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the range for the data points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rangeConsumer">Consumer of the range.</param>
|
|
||||
/// <returns>Range of values.</returns>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")] |
|
||||
protected override Range<IComparable> IRangeProviderGetRange(IRangeConsumer rangeConsumer) |
|
||||
{ |
|
||||
if (rangeConsumer == ActualDependentAxis) |
|
||||
{ |
|
||||
var dependentValuesByIndependentValue = IndependentValueDependentValues.Select(e => e.ToArray()).ToArray(); |
|
||||
|
|
||||
var mostNegative = dependentValuesByIndependentValue |
|
||||
.Select(g => g.Where(v => v < 0) |
|
||||
.Sum()) |
|
||||
.Where(v => v < 0) |
|
||||
.ToArray(); |
|
||||
var leastNegative = dependentValuesByIndependentValue |
|
||||
.Select(g => g.Where(v => v <= 0) |
|
||||
.DefaultIfEmpty(1.0) |
|
||||
.First()) |
|
||||
.Where(v => v <= 0) |
|
||||
.ToArray(); |
|
||||
var mostPositive = dependentValuesByIndependentValue |
|
||||
.Select(g => g.Where(v => 0 < v) |
|
||||
.Sum()) |
|
||||
.Where(v => 0 < v) |
|
||||
.ToArray(); |
|
||||
var leastPositive = dependentValuesByIndependentValue |
|
||||
.Select(g => g.Where(v => 0 <= v) |
|
||||
.DefaultIfEmpty(-1.0) |
|
||||
.First()) |
|
||||
.Where(v => 0 <= v) |
|
||||
.ToArray(); |
|
||||
|
|
||||
// Compute minimum
|
|
||||
double minimum = 0; |
|
||||
if (mostNegative.Any()) |
|
||||
{ |
|
||||
minimum = mostNegative.Min(); |
|
||||
} |
|
||||
else if (leastPositive.Any()) |
|
||||
{ |
|
||||
minimum = leastPositive.Min(); |
|
||||
} |
|
||||
|
|
||||
// Compute maximum
|
|
||||
double maximum = 0; |
|
||||
if (mostPositive.Any()) |
|
||||
{ |
|
||||
maximum = mostPositive.Max(); |
|
||||
} |
|
||||
else if (leastNegative.Any()) |
|
||||
{ |
|
||||
maximum = leastNegative.Max(); |
|
||||
} |
|
||||
|
|
||||
if (IsStacked100) |
|
||||
{ |
|
||||
minimum = Math.Min(minimum, 0); |
|
||||
maximum = Math.Max(maximum, 0); |
|
||||
} |
|
||||
|
|
||||
return new Range<IComparable>(minimum, maximum); |
|
||||
} |
|
||||
else if (rangeConsumer == ActualIndependentAxis) |
|
||||
{ |
|
||||
// Using a non-ICategoryAxis for the independent axis
|
|
||||
// Need to specifically adjust for slot size of bars/columns so they don't overlap
|
|
||||
// Note: Calculation for slotSize is not perfect, but it's quick, close, and errs on the safe side
|
|
||||
Range<IComparable> range = base.IRangeProviderGetRange(rangeConsumer); |
|
||||
int count = Math.Max(IndependentValueGroups.Count(), 1); |
|
||||
if (ActualIndependentAxis.CanPlot(0.0)) |
|
||||
{ |
|
||||
double minimum = ValueHelper.ToDouble(range.Minimum); |
|
||||
double maximum = ValueHelper.ToDouble(range.Maximum); |
|
||||
double slotSize = (maximum - minimum) / count; |
|
||||
return new Range<IComparable>(minimum - slotSize, maximum + slotSize); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
DateTime minimum = ValueHelper.ToDateTime(range.Minimum); |
|
||||
DateTime maximum = ValueHelper.ToDateTime(range.Maximum); |
|
||||
TimeSpan slotSize = TimeSpan.FromTicks((maximum - minimum).Ticks / count); |
|
||||
return new Range<IComparable>(minimum - slotSize, maximum + slotSize); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.IRangeProviderGetRange(rangeConsumer); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the value margins for the data points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="valueMarginConsumer">Consumer of the value margins.</param>
|
|
||||
/// <returns>Sequence of value margins.</returns>
|
|
||||
protected override IEnumerable<ValueMargin> IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer) |
|
||||
{ |
|
||||
if (valueMarginConsumer == ActualDependentAxis) |
|
||||
{ |
|
||||
if (IsStacked100) |
|
||||
{ |
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
Range<IComparable> range = IRangeProviderGetRange((IRangeConsumer)ActualDependentAxis); |
|
||||
double margin = ((AxisOrientation.Y == ActualDependentAxis.Orientation) ? ActualHeight : ActualWidth) / 10; |
|
||||
return new ValueMargin[] |
|
||||
{ |
|
||||
new ValueMargin(range.Minimum, margin, margin), |
|
||||
new ValueMargin(range.Maximum, margin, margin), |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
else if (valueMarginConsumer == ActualIndependentAxis) |
|
||||
{ |
|
||||
// Using a non-ICategoryAxis for the independent axis
|
|
||||
// Relevant space already accounted for by IRangeProviderGetRange
|
|
||||
return Enumerable.Empty<ValueMargin>(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.IValueMarginProviderGetValueMargins(valueMarginConsumer); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the placement of the DataItems (data points) of the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="dataItems">DataItems in need of an update.</param>
|
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")] |
|
||||
protected override void UpdateDataItemPlacement(IEnumerable<DefinitionSeries.DataItem> dataItems) |
|
||||
{ |
|
||||
IAxis actualIndependentAxis = ActualIndependentAxis; |
|
||||
if ((null != ActualDependentAxis) && (null != actualIndependentAxis)) |
|
||||
{ |
|
||||
double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; |
|
||||
double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin ?? 0.0).Value; |
|
||||
ICategoryAxis actualIndependentCategoryAxis = actualIndependentAxis as ICategoryAxis; |
|
||||
double nonCategoryAxisRangeMargin = (null != actualIndependentCategoryAxis) ? 0 : GetMarginForNonCategoryAxis(actualIndependentAxis); |
|
||||
foreach (IndependentValueGroup group in IndependentValueGroups) |
|
||||
{ |
|
||||
Range<UnitValue> categoryRange = new Range<UnitValue>(); |
|
||||
if (null != actualIndependentCategoryAxis) |
|
||||
{ |
|
||||
categoryRange = actualIndependentCategoryAxis.GetPlotAreaCoordinateRange(group.IndependentValue); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
UnitValue independentValueCoordinate = actualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue); |
|
||||
if (ValueHelper.CanGraph(independentValueCoordinate.Value)) |
|
||||
{ |
|
||||
categoryRange = new Range<UnitValue>(new UnitValue(independentValueCoordinate.Value - nonCategoryAxisRangeMargin, independentValueCoordinate.Unit), new UnitValue(independentValueCoordinate.Value + nonCategoryAxisRangeMargin, independentValueCoordinate.Unit)); |
|
||||
} |
|
||||
} |
|
||||
if (categoryRange.HasData) |
|
||||
{ |
|
||||
double categoryMinimumCoordinate = categoryRange.Minimum.Value; |
|
||||
double categoryMaximumCoordinate = categoryRange.Maximum.Value; |
|
||||
double padding = 0.1 * (categoryMaximumCoordinate - categoryMinimumCoordinate); |
|
||||
categoryMinimumCoordinate += padding; |
|
||||
categoryMaximumCoordinate -= padding; |
|
||||
|
|
||||
double sum = IsStacked100 ? |
|
||||
group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : |
|
||||
1; |
|
||||
if (0 == sum) |
|
||||
{ |
|
||||
sum = 1; |
|
||||
} |
|
||||
double ceiling = 0; |
|
||||
double floor = 0; |
|
||||
foreach (DataItem dataItem in group.DataItems) |
|
||||
{ |
|
||||
DataPoint dataPoint = dataItem.DataPoint; |
|
||||
double value = IsStacked100 ? (ValueHelper.ToDouble(dataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataPoint.ActualDependentValue); |
|
||||
if (ValueHelper.CanGraph(value)) |
|
||||
{ |
|
||||
double valueCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(value).Value; |
|
||||
double fillerCoordinate = (0 <= value) ? ceiling : floor; |
|
||||
|
|
||||
double topCoordinate = 0, leftCoordinate = 0, height = 0, width = 0, deltaCoordinate = 0; |
|
||||
if (AxisOrientation.Y == ActualDependentAxis.Orientation) |
|
||||
{ |
|
||||
topCoordinate = plotAreaMaximumDependentCoordinate - Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); |
|
||||
double bottomCoordinate = plotAreaMaximumDependentCoordinate - Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); |
|
||||
deltaCoordinate = bottomCoordinate - topCoordinate; |
|
||||
height = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; |
|
||||
leftCoordinate = categoryMinimumCoordinate; |
|
||||
width = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
leftCoordinate = Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); |
|
||||
double rightCoordinate = Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); |
|
||||
deltaCoordinate = rightCoordinate - leftCoordinate; |
|
||||
width = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; |
|
||||
topCoordinate = categoryMinimumCoordinate; |
|
||||
height = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; |
|
||||
} |
|
||||
|
|
||||
double roundedTopCoordinate = Math.Round(topCoordinate); |
|
||||
Canvas.SetTop(dataItem.Container, roundedTopCoordinate); |
|
||||
dataPoint.Height = Math.Round(topCoordinate + height - roundedTopCoordinate); |
|
||||
double roundedLeftCoordinate = Math.Round(leftCoordinate); |
|
||||
Canvas.SetLeft(dataItem.Container, roundedLeftCoordinate); |
|
||||
dataPoint.Width = Math.Round(leftCoordinate + width - roundedLeftCoordinate); |
|
||||
dataPoint.Visibility = Visibility.Visible; |
|
||||
|
|
||||
if (0 <= value) |
|
||||
{ |
|
||||
ceiling += deltaCoordinate; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
floor -= deltaCoordinate; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) |
|
||||
{ |
|
||||
dataPoint.Visibility = Visibility.Collapsed; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the margin to use for an independent axis that does not implement ICategoryAxis.
|
|
||||
/// </summary>
|
|
||||
/// <param name="axis">Axis to get the margin for.</param>
|
|
||||
/// <returns>Margin for axis.</returns>
|
|
||||
private double GetMarginForNonCategoryAxis(IAxis axis) |
|
||||
{ |
|
||||
Debug.Assert(!(axis is ICategoryAxis), "This method is unnecessary for ICategoryAxis."); |
|
||||
|
|
||||
// Find the smallest distance between two independent value plot area coordinates
|
|
||||
double smallestDistance = double.MaxValue; |
|
||||
double lastCoordinate = double.NaN; |
|
||||
foreach (double coordinate in |
|
||||
IndependentValueGroupsOrderedByIndependentValue |
|
||||
.Select(g => axis.GetPlotAreaCoordinate(g.IndependentValue).Value) |
|
||||
.Where(v => ValueHelper.CanGraph(v))) |
|
||||
{ |
|
||||
if (!double.IsNaN(lastCoordinate)) |
|
||||
{ |
|
||||
double distance = coordinate - lastCoordinate; |
|
||||
if (distance < smallestDistance) |
|
||||
{ |
|
||||
smallestDistance = distance; |
|
||||
} |
|
||||
} |
|
||||
lastCoordinate = coordinate; |
|
||||
} |
|
||||
// Return the margin
|
|
||||
if (double.MaxValue == smallestDistance) |
|
||||
{ |
|
||||
// No smallest distance because <= 1 independent values to plot
|
|
||||
FrameworkElement element = axis as FrameworkElement; |
|
||||
if (null != element) |
|
||||
{ |
|
||||
// Use width of provided axis so single column scenario looks good
|
|
||||
return element.GetMargin(axis); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// No information to work with; no idea what margin to return
|
|
||||
throw new NotSupportedException(); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Found the smallest distance; margin is half of that
|
|
||||
return smallestDistance / 2; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the anchored axis for the series.
|
|
||||
/// </summary>
|
|
||||
IRangeAxis IAnchoredToOrigin.AnchoredAxis |
|
||||
{ |
|
||||
get { return ActualDependentRangeAxis; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a stacked bar chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class StackedBarSeries : StackedBarColumnSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedBarSeries class.
|
|
||||
/// </summary>
|
|
||||
public StackedBarSeries() |
|
||||
{ |
|
||||
DependentAxisOrientation = AxisOrientation.X; |
|
||||
IndependentAxisOrientation = AxisOrientation.Y; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Series-appropriate DataPoint instance.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new BarDataPoint(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Control that displays values as a stacked column chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class StackedColumnSeries : StackedBarColumnSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedColumnSeries class.
|
|
||||
/// </summary>
|
|
||||
public StackedColumnSeries() |
|
||||
{ |
|
||||
DependentAxisOrientation = AxisOrientation.Y; |
|
||||
IndependentAxisOrientation = AxisOrientation.X; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Series-appropriate DataPoint instance.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new ColumnDataPoint(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,86 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Windows.Media; |
|
||||
using System.Windows.Shapes; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Charting |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that displays values as a stacked line chart visualization.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public class StackedLineSeries : StackedAreaLineSeries |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the StackedLineSeries class.
|
|
||||
/// </summary>
|
|
||||
public StackedLineSeries() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a DataPoint for the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Series-appropriate DataPoint instance.</returns>
|
|
||||
protected override DataPoint CreateDataPoint() |
|
||||
{ |
|
||||
return new LineDataPoint(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a series-appropriate Shape for connecting the points of the series.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Shape instance.</returns>
|
|
||||
protected override Shape CreateDataShape() |
|
||||
{ |
|
||||
return new Polyline { Fill = null }; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates the shape for the series.
|
|
||||
/// </summary>
|
|
||||
/// <param name="definitionPoints">Locations of the points of each SeriesDefinition in the series.</param>
|
|
||||
protected override void UpdateShape(IList<IEnumerable<Point>> definitionPoints) |
|
||||
{ |
|
||||
for (int i = 0; i < SeriesDefinitions.Count; i++) |
|
||||
{ |
|
||||
PointCollection pointCollection = new PointCollection(); |
|
||||
foreach (Point p in ((ActualIndependentAxis is ICategoryAxis) ? definitionPoints[i].OrderBy(p => p.X) : definitionPoints[i])) |
|
||||
{ |
|
||||
pointCollection.Add(p); |
|
||||
} |
|
||||
SetPolylinePointsProperty((Polyline)SeriesDefinitionShapes[SeriesDefinitions[i]], pointCollection); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the Points property of a Polyline to the specified PointCollection.
|
|
||||
/// </summary>
|
|
||||
/// <param name="polyline">Polyline to set the Points property of.</param>
|
|
||||
/// <param name="pointCollection">Specified PointCollection.</param>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches spelling of same-named framework class.")] |
|
||||
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "polyline", Justification = "Matches spelling of same-named framework class.")] |
|
||||
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Silverlight implementation is not static.")] |
|
||||
protected void SetPolylinePointsProperty(Polyline polyline, PointCollection pointCollection) |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
// Changing .Points during an Arrange pass can create a layout cycle on Silverlight
|
|
||||
if (!polyline.Points.SequenceEqual(pointCollection)) |
|
||||
{ |
|
||||
#endif
|
|
||||
polyline.Points = pointCollection; |
|
||||
#if SILVERLIGHT
|
|
||||
// In rare cases, Silverlight doesn't update the line visual to match the new points;
|
|
||||
// calling InvalidateArrange works around that problem.
|
|
||||
polyline.InvalidateArrange(); |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,41 +0,0 @@ |
|||||
// (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 class used to calculate axis range.
|
|
||||
/// </summary>
|
|
||||
internal class ValueMarginCoordinateAndOverlap |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the value margin object.
|
|
||||
/// </summary>
|
|
||||
public ValueMargin ValueMargin { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the coordinate.
|
|
||||
/// </summary>
|
|
||||
public double Coordinate { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the left overlap.
|
|
||||
/// </summary>
|
|
||||
public double LeftOverlap { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the right overlap.
|
|
||||
/// </summary>
|
|
||||
public double RightOverlap { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the ValueMarginCoordinateAndOverlap
|
|
||||
/// class.
|
|
||||
/// </summary>
|
|
||||
public ValueMarginCoordinateAndOverlap() |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,815 +0,0 @@ |
|||||
// (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.
|
|
||||
|
|
||||
// Uncomment this to enable the following debugging aids:
|
|
||||
// LeftLeaningRedBlackTree.HtmlFragment
|
|
||||
// LeftLeaningRedBlackTree.Node.HtmlFragment
|
|
||||
// LeftLeaningRedBlackTree.AssertInvariants
|
|
||||
// #define DEBUGGING
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization.Collections |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Implements a left-leaning red-black tree.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Based on the research paper "Left-leaning Red-Black Trees"
|
|
||||
/// by Robert Sedgewick. More information available at:
|
|
||||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf
|
|
||||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf
|
|
||||
/// </remarks>
|
|
||||
/// <typeparam name="TKey">Type of keys.</typeparam>
|
|
||||
/// <typeparam name="TValue">Type of values.</typeparam>
|
|
||||
internal class LeftLeaningRedBlackTree<TKey, TValue> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Stores the key comparison function.
|
|
||||
/// </summary>
|
|
||||
private Comparison<TKey> _keyComparison; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the value comparison function.
|
|
||||
/// </summary>
|
|
||||
private Comparison<TValue> _valueComparison; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the root node of the tree.
|
|
||||
/// </summary>
|
|
||||
private Node _rootNode; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a node of the tree.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Using fields instead of properties drops execution time by about 40%.
|
|
||||
/// </remarks>
|
|
||||
[DebuggerDisplay("Key={Key}, Value={Value}, Siblings={Siblings}")] |
|
||||
private class Node |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the node's key.
|
|
||||
/// </summary>
|
|
||||
public TKey Key; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the node's value.
|
|
||||
/// </summary>
|
|
||||
public TValue Value; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the left node.
|
|
||||
/// </summary>
|
|
||||
public Node Left; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the right node.
|
|
||||
/// </summary>
|
|
||||
public Node Right; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the color of the node.
|
|
||||
/// </summary>
|
|
||||
public bool IsBlack; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the number of "siblings" (nodes with the same key/value).
|
|
||||
/// </summary>
|
|
||||
public int Siblings; |
|
||||
|
|
||||
#if DEBUGGING
|
|
||||
/// <summary>
|
|
||||
/// Gets an HTML fragment representing the node and its children.
|
|
||||
/// </summary>
|
|
||||
public string HtmlFragment |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return |
|
||||
"<table border='1'>" + |
|
||||
"<tr>" + |
|
||||
"<td colspan='2' align='center' bgcolor='" + (IsBlack ? "gray" : "red") + "'>" + Key + ", " + Value + " [" + Siblings + "]</td>" + |
|
||||
"</tr>" + |
|
||||
"<tr>" + |
|
||||
"<td valign='top'>" + (null != Left ? Left.HtmlFragment : "[null]") + "</td>" + |
|
||||
"<td valign='top'>" + (null != Right ? Right.HtmlFragment : "[null]") + "</td>" + |
|
||||
"</tr>" + |
|
||||
"</table>"; |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing a normal dictionary.
|
|
||||
/// </summary>
|
|
||||
/// <param name="keyComparison">The key comparison function.</param>
|
|
||||
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison) |
|
||||
{ |
|
||||
if (null == keyComparison) |
|
||||
{ |
|
||||
throw new ArgumentNullException("keyComparison"); |
|
||||
} |
|
||||
_keyComparison = keyComparison; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing an ordered multi-dictionary.
|
|
||||
/// </summary>
|
|
||||
/// <param name="keyComparison">The key comparison function.</param>
|
|
||||
/// <param name="valueComparison">The value comparison function.</param>
|
|
||||
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison, Comparison<TValue> valueComparison) |
|
||||
: this(keyComparison) |
|
||||
{ |
|
||||
if (null == valueComparison) |
|
||||
{ |
|
||||
throw new ArgumentNullException("valueComparison"); |
|
||||
} |
|
||||
_valueComparison = valueComparison; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the tree is acting as an ordered multi-dictionary.
|
|
||||
/// </summary>
|
|
||||
private bool IsMultiDictionary |
|
||||
{ |
|
||||
get { return null != _valueComparison; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds a key/value pair to the tree.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Key to add.</param>
|
|
||||
/// <param name="value">Value to add.</param>
|
|
||||
public void Add(TKey key, TValue value) |
|
||||
{ |
|
||||
_rootNode = Add(_rootNode, key, value); |
|
||||
_rootNode.IsBlack = true; |
|
||||
#if DEBUGGING
|
|
||||
AssertInvariants(); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes a key (and its associated value) from a normal (non-multi) dictionary.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Key to remove.</param>
|
|
||||
/// <returns>True if key present and removed.</returns>
|
|
||||
public bool Remove(TKey key) |
|
||||
{ |
|
||||
if (IsMultiDictionary) |
|
||||
{ |
|
||||
throw new InvalidOperationException("Remove is only supported when acting as a normal (non-multi) dictionary."); |
|
||||
} |
|
||||
return Remove(key, default(TValue)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes a key/value pair from the tree.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Key to remove.</param>
|
|
||||
/// <param name="value">Value to remove.</param>
|
|
||||
/// <returns>True if key/value present and removed.</returns>
|
|
||||
public bool Remove(TKey key, TValue value) |
|
||||
{ |
|
||||
int initialCount = Count; |
|
||||
if (null != _rootNode) |
|
||||
{ |
|
||||
_rootNode = Remove(_rootNode, key, value); |
|
||||
if (null != _rootNode) |
|
||||
{ |
|
||||
_rootNode.IsBlack = true; |
|
||||
} |
|
||||
} |
|
||||
#if DEBUGGING
|
|
||||
AssertInvariants(); |
|
||||
#endif
|
|
||||
return initialCount != Count; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes all nodes in the tree.
|
|
||||
/// </summary>
|
|
||||
public void Clear() |
|
||||
{ |
|
||||
_rootNode = null; |
|
||||
Count = 0; |
|
||||
#if DEBUGGING
|
|
||||
AssertInvariants(); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sorted list of keys in the tree.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Sorted list of keys.</returns>
|
|
||||
public IEnumerable<TKey> GetKeys() |
|
||||
{ |
|
||||
TKey lastKey = default(TKey); |
|
||||
bool lastKeyValid = false; |
|
||||
return Traverse( |
|
||||
_rootNode, |
|
||||
n => !lastKeyValid || !object.Equals(lastKey, n.Key), |
|
||||
n => |
|
||||
{ |
|
||||
lastKey = n.Key; |
|
||||
lastKeyValid = true; |
|
||||
return lastKey; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the value associated with the specified key in a normal (non-multi) dictionary.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Specified key.</param>
|
|
||||
/// <returns>Value associated with the specified key.</returns>
|
|
||||
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "GetValueForKey", Justification = "Method name.")] |
|
||||
public TValue GetValueForKey(TKey key) |
|
||||
{ |
|
||||
if (IsMultiDictionary) |
|
||||
{ |
|
||||
throw new InvalidOperationException("GetValueForKey is only supported when acting as a normal (non-multi) dictionary."); |
|
||||
} |
|
||||
Node node = GetNodeForKey(key); |
|
||||
if (null != node) |
|
||||
{ |
|
||||
return node.Value; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new KeyNotFoundException(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sequence of the values associated with the specified key.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Specified key.</param>
|
|
||||
/// <returns>Sequence of values.</returns>
|
|
||||
public IEnumerable<TValue> GetValuesForKey(TKey key) |
|
||||
{ |
|
||||
return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a sequence of all the values in the tree.
|
|
||||
/// </summary>
|
|
||||
/// <returns>Sequence of all values.</returns>
|
|
||||
public IEnumerable<TValue> GetValuesForAllKeys() |
|
||||
{ |
|
||||
return Traverse(_rootNode, n => true, n => n.Value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the count of key/value pairs in the tree.
|
|
||||
/// </summary>
|
|
||||
public int Count { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the minimum key in the tree.
|
|
||||
/// </summary>
|
|
||||
public TKey MinimumKey |
|
||||
{ |
|
||||
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum key in the tree.
|
|
||||
/// </summary>
|
|
||||
public TKey MaximumKey |
|
||||
{ |
|
||||
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the minimum key's minimum value.
|
|
||||
/// </summary>
|
|
||||
public TValue MinimumValue |
|
||||
{ |
|
||||
get { return GetExtreme(_rootNode, n => n.Left, n => n.Value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum key's maximum value.
|
|
||||
/// </summary>
|
|
||||
public TValue MaximumValue |
|
||||
{ |
|
||||
get { return GetExtreme(_rootNode, n => n.Right, n => n.Value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns true if the specified node is red.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <returns>True if specified node is red.</returns>
|
|
||||
private static bool IsRed(Node node) |
|
||||
{ |
|
||||
if (null == node) |
|
||||
{ |
|
||||
// "Virtual" leaf nodes are always black
|
|
||||
return false; |
|
||||
} |
|
||||
return !node.IsBlack; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds the specified key/value pair below the specified root node.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <param name="key">Key to add.</param>
|
|
||||
/// <param name="value">Value to add.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private Node Add(Node node, TKey key, TValue value) |
|
||||
{ |
|
||||
if (null == node) |
|
||||
{ |
|
||||
// Insert new node
|
|
||||
Count++; |
|
||||
return new Node { Key = key, Value = value }; |
|
||||
} |
|
||||
|
|
||||
if (IsRed(node.Left) && IsRed(node.Right)) |
|
||||
{ |
|
||||
// Split node with two red children
|
|
||||
FlipColor(node); |
|
||||
} |
|
||||
|
|
||||
// Find right place for new node
|
|
||||
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); |
|
||||
if (comparisonResult < 0) |
|
||||
{ |
|
||||
node.Left = Add(node.Left, key, value); |
|
||||
} |
|
||||
else if (0 < comparisonResult) |
|
||||
{ |
|
||||
node.Right = Add(node.Right, key, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (IsMultiDictionary) |
|
||||
{ |
|
||||
// Store the presence of a "duplicate" node
|
|
||||
node.Siblings++; |
|
||||
Count++; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Replace the value of the existing node
|
|
||||
node.Value = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (IsRed(node.Right)) |
|
||||
{ |
|
||||
// Rotate to prevent red node on right
|
|
||||
node = RotateLeft(node); |
|
||||
} |
|
||||
|
|
||||
if (IsRed(node.Left) && IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
// Rotate to prevent consecutive red nodes
|
|
||||
node = RotateRight(node); |
|
||||
} |
|
||||
|
|
||||
return node; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes the specified key/value pair from below the specified node.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <param name="key">Key to remove.</param>
|
|
||||
/// <param name="value">Value to remove.</param>
|
|
||||
/// <returns>True if key/value present and removed.</returns>
|
|
||||
private Node Remove(Node node, TKey key, TValue value) |
|
||||
{ |
|
||||
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); |
|
||||
if (comparisonResult < 0) |
|
||||
{ |
|
||||
// * Continue search if left is present
|
|
||||
if (null != node.Left) |
|
||||
{ |
|
||||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
// Move a red node over
|
|
||||
node = MoveRedLeft(node); |
|
||||
} |
|
||||
|
|
||||
// Remove from left
|
|
||||
node.Left = Remove(node.Left, key, value); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (IsRed(node.Left)) |
|
||||
{ |
|
||||
// Flip a 3 node or unbalance a 4 node
|
|
||||
node = RotateRight(node); |
|
||||
} |
|
||||
if ((0 == KeyAndValueComparison(key, value, node.Key, node.Value)) && (null == node.Right)) |
|
||||
{ |
|
||||
// Remove leaf node
|
|
||||
Debug.Assert(null == node.Left, "About to remove an extra node."); |
|
||||
Count--; |
|
||||
if (0 < node.Siblings) |
|
||||
{ |
|
||||
// Record the removal of the "duplicate" node
|
|
||||
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); |
|
||||
node.Siblings--; |
|
||||
return node; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Leaf node is gone
|
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
// * Continue search if right is present
|
|
||||
if (null != node.Right) |
|
||||
{ |
|
||||
if (!IsRed(node.Right) && !IsRed(node.Right.Left)) |
|
||||
{ |
|
||||
// Move a red node over
|
|
||||
node = MoveRedRight(node); |
|
||||
} |
|
||||
if (0 == KeyAndValueComparison(key, value, node.Key, node.Value)) |
|
||||
{ |
|
||||
// Remove leaf node
|
|
||||
Count--; |
|
||||
if (0 < node.Siblings) |
|
||||
{ |
|
||||
// Record the removal of the "duplicate" node
|
|
||||
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); |
|
||||
node.Siblings--; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Find the smallest node on the right, swap, and remove it
|
|
||||
Node m = GetExtreme(node.Right, n => n.Left, n => n); |
|
||||
node.Key = m.Key; |
|
||||
node.Value = m.Value; |
|
||||
node.Siblings = m.Siblings; |
|
||||
node.Right = DeleteMinimum(node.Right); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Remove from right
|
|
||||
node.Right = Remove(node.Right, key, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Maintain invariants
|
|
||||
return FixUp(node); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Flip the colors of the specified node and its direct children.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
private static void FlipColor(Node node) |
|
||||
{ |
|
||||
node.IsBlack = !node.IsBlack; |
|
||||
node.Left.IsBlack = !node.Left.IsBlack; |
|
||||
node.Right.IsBlack = !node.Right.IsBlack; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rotate the specified node "left".
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private static Node RotateLeft(Node node) |
|
||||
{ |
|
||||
Node x = node.Right; |
|
||||
node.Right = x.Left; |
|
||||
x.Left = node; |
|
||||
x.IsBlack = node.IsBlack; |
|
||||
node.IsBlack = false; |
|
||||
return x; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rotate the specified node "right".
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private static Node RotateRight(Node node) |
|
||||
{ |
|
||||
Node x = node.Left; |
|
||||
node.Left = x.Right; |
|
||||
x.Right = node; |
|
||||
x.IsBlack = node.IsBlack; |
|
||||
node.IsBlack = false; |
|
||||
return x; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Moves a red node from the right child to the left child.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Parent node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private static Node MoveRedLeft(Node node) |
|
||||
{ |
|
||||
FlipColor(node); |
|
||||
if (IsRed(node.Right.Left)) |
|
||||
{ |
|
||||
node.Right = RotateRight(node.Right); |
|
||||
node = RotateLeft(node); |
|
||||
FlipColor(node); |
|
||||
|
|
||||
// * Avoid creating right-leaning nodes
|
|
||||
if (IsRed(node.Right.Right)) |
|
||||
{ |
|
||||
node.Right = RotateLeft(node.Right); |
|
||||
} |
|
||||
} |
|
||||
return node; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Moves a red node from the left child to the right child.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Parent node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private static Node MoveRedRight(Node node) |
|
||||
{ |
|
||||
FlipColor(node); |
|
||||
if (IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
node = RotateRight(node); |
|
||||
FlipColor(node); |
|
||||
} |
|
||||
return node; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Deletes the minimum node under the specified node.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private Node DeleteMinimum(Node node) |
|
||||
{ |
|
||||
if (null == node.Left) |
|
||||
{ |
|
||||
// Nothing to do
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
// Move red node left
|
|
||||
node = MoveRedLeft(node); |
|
||||
} |
|
||||
|
|
||||
// Recursively delete
|
|
||||
node.Left = DeleteMinimum(node.Left); |
|
||||
|
|
||||
// Maintain invariants
|
|
||||
return FixUp(node); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Maintains invariants by adjusting the specified nodes children.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node">Specified node.</param>
|
|
||||
/// <returns>New root node.</returns>
|
|
||||
private static Node FixUp(Node node) |
|
||||
{ |
|
||||
if (IsRed(node.Right)) |
|
||||
{ |
|
||||
// Avoid right-leaning node
|
|
||||
node = RotateLeft(node); |
|
||||
} |
|
||||
|
|
||||
if (IsRed(node.Left) && IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
// Balance 4-node
|
|
||||
node = RotateRight(node); |
|
||||
} |
|
||||
|
|
||||
if (IsRed(node.Left) && IsRed(node.Right)) |
|
||||
{ |
|
||||
// Push red up
|
|
||||
FlipColor(node); |
|
||||
} |
|
||||
|
|
||||
// * Avoid leaving behind right-leaning nodes
|
|
||||
if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) |
|
||||
{ |
|
||||
node.Left = RotateLeft(node.Left); |
|
||||
if (IsRed(node.Left)) |
|
||||
{ |
|
||||
// Balance 4-node
|
|
||||
node = RotateRight(node); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return node; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the (first) node corresponding to the specified key.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">Key to search for.</param>
|
|
||||
/// <returns>Corresponding node or null if none found.</returns>
|
|
||||
private Node GetNodeForKey(TKey key) |
|
||||
{ |
|
||||
// Initialize
|
|
||||
Node node = _rootNode; |
|
||||
while (null != node) |
|
||||
{ |
|
||||
// Compare keys and go left/right
|
|
||||
int comparisonResult = _keyComparison(key, node.Key); |
|
||||
if (comparisonResult < 0) |
|
||||
{ |
|
||||
node = node.Left; |
|
||||
} |
|
||||
else if (0 < comparisonResult) |
|
||||
{ |
|
||||
node = node.Right; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Match; return node
|
|
||||
return node; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// No match found
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an extreme (ex: minimum/maximum) value.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">Type of value.</typeparam>
|
|
||||
/// <param name="node">Node to start from.</param>
|
|
||||
/// <param name="successor">Successor function.</param>
|
|
||||
/// <param name="selector">Selector function.</param>
|
|
||||
/// <returns>Extreme value.</returns>
|
|
||||
private static T GetExtreme<T>(Node node, Func<Node, Node> successor, Func<Node, T> selector) |
|
||||
{ |
|
||||
// Initialize
|
|
||||
T extreme = default(T); |
|
||||
Node current = node; |
|
||||
while (null != current) |
|
||||
{ |
|
||||
// Go to extreme
|
|
||||
extreme = selector(current); |
|
||||
current = successor(current); |
|
||||
} |
|
||||
return extreme; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">Type of elements.</typeparam>
|
|
||||
/// <param name="node">Starting node.</param>
|
|
||||
/// <param name="condition">Condition method.</param>
|
|
||||
/// <param name="selector">Selector method.</param>
|
|
||||
/// <returns>Sequence of selected nodes.</returns>
|
|
||||
private IEnumerable<T> Traverse<T>(Node node, Func<Node, bool> condition, Func<Node, T> selector) |
|
||||
{ |
|
||||
// Create a stack to avoid recursion
|
|
||||
Stack<Node> stack = new Stack<Node>(); |
|
||||
Node current = node; |
|
||||
while (null != current) |
|
||||
{ |
|
||||
if (null != current.Left) |
|
||||
{ |
|
||||
// Save current state and go left
|
|
||||
stack.Push(current); |
|
||||
current = current.Left; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
do |
|
||||
{ |
|
||||
for (int i = 0; i <= current.Siblings; i++) |
|
||||
{ |
|
||||
// Select current node if relevant
|
|
||||
if (condition(current)) |
|
||||
{ |
|
||||
yield return selector(current); |
|
||||
} |
|
||||
} |
|
||||
// Go right - or up if nothing to the right
|
|
||||
current = current.Right; |
|
||||
} |
|
||||
while ((null == current) && |
|
||||
(0 < stack.Count) && |
|
||||
(null != (current = stack.Pop()))); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares the specified keys (primary) and values (secondary).
|
|
||||
/// </summary>
|
|
||||
/// <param name="leftKey">The left key.</param>
|
|
||||
/// <param name="leftValue">The left value.</param>
|
|
||||
/// <param name="rightKey">The right key.</param>
|
|
||||
/// <param name="rightValue">The right value.</param>
|
|
||||
/// <returns>CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right.</returns>
|
|
||||
private int KeyAndValueComparison(TKey leftKey, TValue leftValue, TKey rightKey, TValue rightValue) |
|
||||
{ |
|
||||
// Compare keys
|
|
||||
int comparisonResult = _keyComparison(leftKey, rightKey); |
|
||||
if ((0 == comparisonResult) && (null != _valueComparison)) |
|
||||
{ |
|
||||
// Keys match; compare values
|
|
||||
comparisonResult = _valueComparison(leftValue, rightValue); |
|
||||
} |
|
||||
return comparisonResult; |
|
||||
} |
|
||||
|
|
||||
#if DEBUGGING
|
|
||||
/// <summary>
|
|
||||
/// Asserts that tree invariants are not violated.
|
|
||||
/// </summary>
|
|
||||
private void AssertInvariants() |
|
||||
{ |
|
||||
// Root is black
|
|
||||
Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); |
|
||||
// Every path contains the same number of black nodes
|
|
||||
Dictionary<Node, Node> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.Node, LeftLeaningRedBlackTree<TKey, TValue>.Node>(); |
|
||||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) |
|
||||
{ |
|
||||
if (null != node.Left) |
|
||||
{ |
|
||||
parents[node.Left] = node; |
|
||||
} |
|
||||
if (null != node.Right) |
|
||||
{ |
|
||||
parents[node.Right] = node; |
|
||||
} |
|
||||
} |
|
||||
if (null != _rootNode) |
|
||||
{ |
|
||||
parents[_rootNode] = null; |
|
||||
} |
|
||||
int treeCount = -1; |
|
||||
foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) |
|
||||
{ |
|
||||
int pathCount = 0; |
|
||||
Node current = node; |
|
||||
while (null != current) |
|
||||
{ |
|
||||
if (current.IsBlack) |
|
||||
{ |
|
||||
pathCount++; |
|
||||
} |
|
||||
current = parents[current]; |
|
||||
} |
|
||||
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); |
|
||||
treeCount = pathCount; |
|
||||
} |
|
||||
// Verify node properties...
|
|
||||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) |
|
||||
{ |
|
||||
// Left node is less
|
|
||||
if (null != node.Left) |
|
||||
{ |
|
||||
Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); |
|
||||
} |
|
||||
// Right node is greater
|
|
||||
if (null != node.Right) |
|
||||
{ |
|
||||
Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); |
|
||||
} |
|
||||
// Both children of a red node are black
|
|
||||
Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); |
|
||||
// Always left-leaning
|
|
||||
Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); |
|
||||
// No consecutive reds (subset of previous rule)
|
|
||||
//Debug.Assert(!(IsRed(node) && IsRed(node.Left)));
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an HTML fragment representing the tree.
|
|
||||
/// </summary>
|
|
||||
public string HtmlDocument |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return |
|
||||
"<html>" + |
|
||||
"<body>" + |
|
||||
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") + |
|
||||
"</body>" + |
|
||||
"</html>"; |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
@ -1,101 +0,0 @@ |
|||||
// (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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,86 +0,0 @@ |
|||||
// (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(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,192 +0,0 @@ |
|||||
// (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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace System.Windows.Controls |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides a custom implementation of DesignerProperties.GetIsInDesignMode
|
|
||||
/// to work around an issue.
|
|
||||
/// </summary>
|
|
||||
internal static class DesignerProperties |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Returns whether the control is in design mode (running under Blend
|
|
||||
/// or Visual Studio).
|
|
||||
/// </summary>
|
|
||||
/// <param name="element">The element from which the property value is
|
|
||||
/// read.</param>
|
|
||||
/// <returns>True if in design mode.</returns>
|
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "element", Justification = "Matching declaration of System.ComponentModel.DesignerProperties.GetIsInDesignMode (which has a bug and is not reliable).")] |
|
||||
public static bool GetIsInDesignMode(DependencyObject element) |
|
||||
{ |
|
||||
if (!_isInDesignMode.HasValue) |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
_isInDesignMode = |
|
||||
(null == Application.Current) || |
|
||||
Application.Current.GetType() == typeof(Application); |
|
||||
#else
|
|
||||
_isInDesignMode = System.ComponentModel.DesignerProperties.GetIsInDesignMode(element); |
|
||||
#endif
|
|
||||
} |
|
||||
return _isInDesignMode.Value; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Stores the computed InDesignMode value.
|
|
||||
/// </summary>
|
|
||||
private static bool? _isInDesignMode; |
|
||||
} |
|
||||
} |
|
||||
@ -1,307 +0,0 @@ |
|||||
// (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; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// This class contains general purpose functions to manipulate the generic
|
|
||||
/// IEnumerable type.
|
|
||||
/// </summary>
|
|
||||
internal static class EnumerableFunctions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Attempts to cast IEnumerable to a list in order to retrieve a count
|
|
||||
/// in order one. It attempts to cast fail the sequence is enumerated.
|
|
||||
/// </summary>
|
|
||||
/// <param name="that">The sequence.</param>
|
|
||||
/// <returns>The number of elements in the sequence.</returns>
|
|
||||
public static int FastCount(this IEnumerable that) |
|
||||
{ |
|
||||
IList list = that as IList; |
|
||||
if (list != null) |
|
||||
{ |
|
||||
return list.Count; |
|
||||
} |
|
||||
return that.CastWrapper<object>().Count(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the minimum value in the stream based on the result of a
|
|
||||
/// project function.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The stream type.</typeparam>
|
|
||||
/// <param name="that">The stream.</param>
|
|
||||
/// <param name="projectionFunction">The function that transforms the
|
|
||||
/// item.</param>
|
|
||||
/// <returns>The minimum value or null.</returns>
|
|
||||
public static T MinOrNull<T>(this IEnumerable<T> that, Func<T, IComparable> projectionFunction) |
|
||||
where T : class |
|
||||
{ |
|
||||
IComparable result = null; |
|
||||
T minimum = default(T); |
|
||||
if (!that.Any()) |
|
||||
{ |
|
||||
return minimum; |
|
||||
} |
|
||||
|
|
||||
minimum = that.First(); |
|
||||
result = projectionFunction(minimum); |
|
||||
foreach (T item in that.Skip(1)) |
|
||||
{ |
|
||||
IComparable currentResult = projectionFunction(item); |
|
||||
if (result.CompareTo(currentResult) > 0) |
|
||||
{ |
|
||||
result = currentResult; |
|
||||
minimum = item; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return minimum; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the sum of all values in the sequence or the default value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="that">The stream.</param>
|
|
||||
/// <returns>The sum of all values or the default value.</returns>
|
|
||||
public static double SumOrDefault(this IEnumerable<double> that) |
|
||||
{ |
|
||||
if (!that.Any()) |
|
||||
{ |
|
||||
return 0.0; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return that.Sum(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the maximum value in the stream based on the result of a
|
|
||||
/// project function.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The stream type.</typeparam>
|
|
||||
/// <param name="that">The stream.</param>
|
|
||||
/// <param name="projectionFunction">The function that transforms the
|
|
||||
/// item.</param>
|
|
||||
/// <returns>The maximum value or null.</returns>
|
|
||||
public static T MaxOrNull<T>(this IEnumerable<T> that, Func<T, IComparable> projectionFunction) |
|
||||
where T : class |
|
||||
{ |
|
||||
IComparable result = null; |
|
||||
T maximum = default(T); |
|
||||
if (!that.Any()) |
|
||||
{ |
|
||||
return maximum; |
|
||||
} |
|
||||
|
|
||||
maximum = that.First(); |
|
||||
result = projectionFunction(maximum); |
|
||||
foreach (T item in that.Skip(1)) |
|
||||
{ |
|
||||
IComparable currentResult = projectionFunction(item); |
|
||||
if (result.CompareTo(currentResult) < 0) |
|
||||
{ |
|
||||
result = currentResult; |
|
||||
maximum = item; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return maximum; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Accepts two sequences and applies a function to the corresponding
|
|
||||
/// values in the two sequences.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T0">The type of the first sequence.</typeparam>
|
|
||||
/// <typeparam name="T1">The type of the second sequence.</typeparam>
|
|
||||
/// <typeparam name="R">The return type of the function.</typeparam>
|
|
||||
/// <param name="enumerable0">The first sequence.</param>
|
|
||||
/// <param name="enumerable1">The second sequence.</param>
|
|
||||
/// <param name="func">The function to apply to the corresponding values
|
|
||||
/// from the two sequences.</param>
|
|
||||
/// <returns>A sequence of transformed values from both sequences.</returns>
|
|
||||
public static IEnumerable<R> Zip<T0, T1, R>(IEnumerable<T0> enumerable0, IEnumerable<T1> enumerable1, Func<T0, T1, R> func) |
|
||||
{ |
|
||||
IEnumerator<T0> enumerator0 = enumerable0.GetEnumerator(); |
|
||||
IEnumerator<T1> enumerator1 = enumerable1.GetEnumerator(); |
|
||||
while (enumerator0.MoveNext() && enumerator1.MoveNext()) |
|
||||
{ |
|
||||
yield return func(enumerator0.Current, enumerator1.Current); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a sequence of values by accepting an initial value, an
|
|
||||
/// iteration function, and apply the iteration function recursively.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the sequence.</typeparam>
|
|
||||
/// <param name="value">The initial value.</param>
|
|
||||
/// <param name="nextFunction">The function to apply to the value.
|
|
||||
/// </param>
|
|
||||
/// <returns>A sequence of the iterated values.</returns>
|
|
||||
public static IEnumerable<T> Iterate<T>(T value, Func<T, T> nextFunction) |
|
||||
{ |
|
||||
yield return value; |
|
||||
while (true) |
|
||||
{ |
|
||||
value = nextFunction(value); |
|
||||
yield return value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the index of an item in a sequence.
|
|
||||
/// </summary>
|
|
||||
/// <param name="that">The sequence.</param>
|
|
||||
/// <param name="value">The item to search for.</param>
|
|
||||
/// <returns>The index of the item or -1 if not found.</returns>
|
|
||||
public static int IndexOf(this IEnumerable that, object value) |
|
||||
{ |
|
||||
int index = 0; |
|
||||
foreach (object item in that) |
|
||||
{ |
|
||||
if (object.ReferenceEquals(value, item) || value.Equals(item)) |
|
||||
{ |
|
||||
return index; |
|
||||
} |
|
||||
index++; |
|
||||
} |
|
||||
return -1; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Executes an action for each item and a sequence, passing in the
|
|
||||
/// index of that item to the action procedure.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the sequence.</typeparam>
|
|
||||
/// <param name="that">The sequence.</param>
|
|
||||
/// <param name="action">A function that accepts a sequence item and its
|
|
||||
/// index in the sequence.</param>
|
|
||||
public static void ForEachWithIndex<T>(this IEnumerable<T> that, Action<T, int> action) |
|
||||
{ |
|
||||
int index = 0; |
|
||||
foreach (T item in that) |
|
||||
{ |
|
||||
action(item, index); |
|
||||
index++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the maximum value or null if sequence is empty.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the sequence.</typeparam>
|
|
||||
/// <param name="that">The sequence to retrieve the maximum value from.
|
|
||||
/// </param>
|
|
||||
/// <returns>The maximum value or null.</returns>
|
|
||||
public static T? MaxOrNullable<T>(this IEnumerable<T> that) |
|
||||
where T : struct, IComparable |
|
||||
{ |
|
||||
if (!that.Any()) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
return that.Max(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the minimum value or null if sequence is empty.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the sequence.</typeparam>
|
|
||||
/// <param name="that">The sequence to retrieve the minimum value from.
|
|
||||
/// </param>
|
|
||||
/// <returns>The minimum value or null.</returns>
|
|
||||
public static T? MinOrNullable<T>(this IEnumerable<T> that) |
|
||||
where T : struct, IComparable |
|
||||
{ |
|
||||
if (!that.Any()) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
return that.Min(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Attempts to retrieve an element at an index by testing whether a
|
|
||||
/// sequence is randomly accessible. If not, performance degrades to a
|
|
||||
/// linear search.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The type of the elements in the sequence.</typeparam>
|
|
||||
/// <param name="that">The sequence.</param>
|
|
||||
/// <param name="index">The index of the element in the sequence.</param>
|
|
||||
/// <returns>The element at the given index.</returns>
|
|
||||
public static T FastElementAt<T>(this IEnumerable that, int index) |
|
||||
{ |
|
||||
{ |
|
||||
IList<T> list = that as IList<T>; |
|
||||
if (list != null) |
|
||||
{ |
|
||||
return list[index]; |
|
||||
} |
|
||||
} |
|
||||
{ |
|
||||
IList list = that as IList; |
|
||||
if (list != null) |
|
||||
{ |
|
||||
return (T) list[index]; |
|
||||
} |
|
||||
} |
|
||||
return that.CastWrapper<T>().ElementAt(index); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Applies an accumulator function over a sequence and returns each intermediate result.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">Type of elements in source sequence.</typeparam>
|
|
||||
/// <typeparam name="S">Type of elements in result sequence.</typeparam>
|
|
||||
/// <param name="that">Sequence to scan.</param>
|
|
||||
/// <param name="seed">Initial accumulator value.</param>
|
|
||||
/// <param name="accumulator">Function used to generate the result sequence.</param>
|
|
||||
/// <returns>Sequence of intermediate results.</returns>
|
|
||||
public static IEnumerable<S> Scan<T, S>(this IEnumerable<T> that, S seed, Func<S, T, S> accumulator) |
|
||||
{ |
|
||||
S value = seed; |
|
||||
yield return seed; |
|
||||
foreach (T t in that) |
|
||||
{ |
|
||||
value = accumulator(value, t); |
|
||||
yield return value; |
|
||||
} |
|
||||
yield break; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts the elements of an System.Collections.IEnumerable to the specified type.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// A wrapper for the Enumerable.Cast(T) method that works around a limitation on some platforms.
|
|
||||
/// </remarks>
|
|
||||
/// <typeparam name="TResult">The type to convert the elements of source to.</typeparam>
|
|
||||
/// <param name="source">The System.Collections.IEnumerable that contains the elements to be converted.</param>
|
|
||||
/// <returns>
|
|
||||
/// An System.Collections.Generic.IEnumerable(T) that contains each element of the source sequence converted to the specified type.
|
|
||||
/// </returns>
|
|
||||
public static IEnumerable<TResult> CastWrapper<TResult>(this IEnumerable source) |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
// Certain flavors of this platform have a bug which causes Cast<T> to raise an exception incorrectly.
|
|
||||
// Work around that by using the more general OfType<T> method instead for no loss of functionality.
|
|
||||
return source.OfType<TResult>(); |
|
||||
#else
|
|
||||
// No issues on this platform - call directly through to Cast<T>
|
|
||||
return source.Cast<TResult>(); |
|
||||
#endif
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,60 +0,0 @@ |
|||||
// (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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,131 +0,0 @@ |
|||||
// (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.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Unofficial release.")] |
|
||||
|
|
||||
[assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Headered", Justification = "Part of HeaderedItemsControl.")] |
|
||||
[assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Polyline", Justification = "Matches System.Windows.Shapes.Polyline.")] |
|
||||
[assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Intelli", Justification = "Part of IntelliSense.")] |
|
||||
[assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "Silverlight", Justification = "Product name.")] |
|
||||
[assembly: SuppressMessage("General", "SWC1001:XmlDocumentationCommentShouldBeSpelledCorrectly", MessageId = "tuple's", Justification = "Consistent with MSDN documentation for .NET 4.")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.ActualMaximum")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.ActualMinimum")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.GetPlotAreaCoordinate(System.IComparable)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.RangeChanged(System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider,System.Windows.Controls.DataVisualization.Range`1<System.IComparable>)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.GetPlotAreaCoordinateValueRange(System.Double)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.ICategoryAxisInformationProvider.GetCategories(System.Windows.Controls.DataVisualization.Charting.ICategoryAxis)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider.GetActualRange(System.Windows.Controls.DataVisualization.Charting.IRangeAxis)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider.GetDesiredRange(System.Windows.Controls.DataVisualization.Charting.IRangeAxis)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Axis.#System.Windows.Controls.DataVisualization.Charting.IAxis.Register(System.Object)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.Axis.#System.Windows.Controls.DataVisualization.Charting.IAxis.Unregister(System.Object)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeAxis.Range")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IAxisListener.AxisInvalidated(System.Windows.Controls.DataVisualization.Charting.IAxis)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.#System.Windows.Controls.DataVisualization.Charting.IRangeProvider.GetRange(System.Windows.Controls.DataVisualization.Charting.IRangeConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.ICollection.SyncRoot")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.Generic.ICollection`1<!0>.IsReadOnly")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.ListBaseCollection`1.#System.Collections.IList.IsFixedSize")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.ListBaseCollection`1.#System.Collections.IList.IsReadOnly")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.CollectionBase`1.#System.Collections.ICollection.IsSynchronized")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.ColumnBarBaseSeries`1.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IRangeConsumer.RangeChanged(System.Windows.Controls.DataVisualization.Charting.IRangeProvider,System.Windows.Controls.DataVisualization.Range`1<System.IComparable>)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.RangeAxis.#System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer.ValueMarginsChanged(System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider,System.Collections.Generic.IEnumerable`1<System.Windows.Controls.DataVisualization.Charting.ValueMargin>)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IAxisListener.AxisInvalidated(System.Windows.Controls.DataVisualization.Charting.IAxis)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IRangeProvider.GetRange(System.Windows.Controls.DataVisualization.Charting.IRangeConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.GetResourceDictionariesWhere(System.Func`2<System.Windows.ResourceDictionary,System.Boolean>)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.ResourceDictionariesChanged")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.IResourceDictionaryDispenser.ResourceDictionariesChanged")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.Axes")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.BackgroundElements")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.ForegroundElements")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.ISeriesHost.Series")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider.GetValueMargins(System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireGlobalSeriesIndex.GlobalSeriesIndexChanged(System.Nullable`1<System.Int32>)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost.SeriesHost")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost.SeriesHost")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesDefinition.#System.Windows.Controls.DataVisualization.Charting.ISeries.LegendItems")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedAreaSeries.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedBarColumnSeries.#System.Windows.Controls.DataVisualization.Charting.IAnchoredToOrigin.AnchoredAxis")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.StackedBarColumnSeries.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.DefinitionSeries.#System.Windows.Controls.DataVisualization.Charting.IDataProvider.GetData(System.Windows.Controls.DataVisualization.Charting.IDataConsumer)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.PieSeries.#.cctor()")] |
|
||||
[assembly: SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Scope = "member", Target = "System.Windows.Controls.DataVisualization.Charting.SeriesHostAxesCollection.#.ctor(System.Windows.Controls.DataVisualization.Charting.ISeriesHost)")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "XamlGeneratedNamespace")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.DataVisualization.Charting.Primitives")] |
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Windows.Controls.DataVisualization.Charting.Compatible")] |
|
||||
|
|
||||
// Suppress "Silverlight-only class or assembly" warning for all DataVisualization classes because there is currently no corresponding WPF implementation.
|
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AnimationSequence")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Axis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisLabel")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisLocation")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.AxisOrientation")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BarDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BarSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BubbleDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.BubbleSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.CategoryAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.CategorySortOrder")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Chart")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointSingleSeriesWithAxes")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DataPointState")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeAxisLabel")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DateTimeIntervalType")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxisGridLinesElementProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ICategoryAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ICategoryAxisInformationProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeAxisInformationProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRequireGlobalSeriesIndex")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ISeriesHost")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LegendItem")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LineDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LineSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.LinearAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.NumericAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.NumericAxisLabel")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PieDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PieSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.RangeAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ScatterDataPoint")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ScatterSeries")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Series")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.IStyleDispenser")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Legend")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Range`1")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.StringFormatConverter")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.ResourceDictionaryCollection")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Title")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ColumnBarBaseSeries`1")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.DisplayAxis")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAnnotationProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IAxisListener")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IDataConsumer")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IDataProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeConsumer")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRangeProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IRequireSeriesHost")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IScrollService")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IValueMarginConsumer")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.IValueMarginProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.PointOrientedPanel")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.RadialPanel")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.ValueMargin")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.IScrollServiceInformationProvider")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Unit")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.UnitValue")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.ListBaseCollection`1")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.CollectionBase`1")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Primitives.Edge")] |
|
||||
[assembly: SuppressMessage("Compatibility", "SWC4000:GeneralWPFCompatibilityRule", MessageId = "System.Windows.Controls.DataVisualization.Charting.Primitives.EdgePanel")] |
|
||||
@ -1,54 +0,0 @@ |
|||||
// (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.Linq; |
|
||||
using System.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A set of extension methods for the Grid container.
|
|
||||
/// </summary>
|
|
||||
internal static class GridExtensions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Mirrors the grid either horizontally or vertically.
|
|
||||
/// </summary>
|
|
||||
/// <param name="grid">The grid to mirror.</param>
|
|
||||
/// <param name="orientation">The orientation to mirror the grid along.
|
|
||||
/// </param>
|
|
||||
public static void Mirror(this Grid grid, Orientation orientation) |
|
||||
{ |
|
||||
if (orientation == Orientation.Horizontal) |
|
||||
{ |
|
||||
IList<RowDefinition> rows = grid.RowDefinitions.Reverse().ToList(); |
|
||||
grid.RowDefinitions.Clear(); |
|
||||
foreach (FrameworkElement child in grid.Children.OfType<FrameworkElement>()) |
|
||||
{ |
|
||||
Grid.SetRow(child, (rows.Count - 1) - Grid.GetRow(child)); |
|
||||
} |
|
||||
foreach (RowDefinition row in rows) |
|
||||
{ |
|
||||
grid.RowDefinitions.Add(row); |
|
||||
} |
|
||||
} |
|
||||
else if (orientation == Orientation.Vertical) |
|
||||
{ |
|
||||
IList<ColumnDefinition> columns = grid.ColumnDefinitions.Reverse().ToList(); |
|
||||
grid.ColumnDefinitions.Clear(); |
|
||||
foreach (FrameworkElement child in grid.Children.OfType<FrameworkElement>()) |
|
||||
{ |
|
||||
Grid.SetColumn(child, (columns.Count - 1) - Grid.GetColumn(child)); |
|
||||
} |
|
||||
foreach (ColumnDefinition column in columns) |
|
||||
{ |
|
||||
grid.ColumnDefinitions.Add(column); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
// (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>
|
|
||||
/// Represents a service that dispenses ResourceDictionaries.
|
|
||||
/// </summary>
|
|
||||
/// <QualityBand>Preview</QualityBand>
|
|
||||
public interface IResourceDictionaryDispenser |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Returns a rotating enumerator of ResourceDictionaries coordinated with
|
|
||||
/// the style dispenser object to ensure that no two enumerators are
|
|
||||
/// currently on the same one if possible. If the dispenser is reset or
|
|
||||
/// its collection is changed then the enumerators will also be reset.
|
|
||||
/// </summary>
|
|
||||
/// <param name="predicate">A predicate that returns a value
|
|
||||
/// indicating whether to return a ResourceDictionary.</param>
|
|
||||
/// <returns>An enumerator of ResourceDictionaries.</returns>
|
|
||||
IEnumerator<ResourceDictionary> GetResourceDictionariesWhere(Func<ResourceDictionary, bool> predicate); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Event that is invoked when the StyleDispenser's ResourceDictionaries have changed.
|
|
||||
/// </summary>
|
|
||||
event EventHandler ResourceDictionariesChanged; |
|
||||
} |
|
||||
} |
|
||||
@ -1,653 +0,0 @@ |
|||||
// (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.Windows; |
|
||||
using System.Windows.Controls; |
|
||||
using System.Windows.Markup; |
|
||||
using System.Windows.Media; |
|
||||
#if !SILVERLIGHT
|
|
||||
using System.IO; |
|
||||
using System.Text; |
|
||||
#endif
|
|
||||
|
|
||||
namespace System.Windows.Controls.DataVisualization |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Control that implements support for transformations as if applied by
|
|
||||
/// LayoutTransform (which does not exist in Silverlight).
|
|
||||
/// </summary>
|
|
||||
[ContentProperty("Child")] |
|
||||
internal class LayoutTransformControl : Control |
|
||||
{ |
|
||||
#region public FrameworkElement Child
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the single child of the LayoutTransformControl.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Corresponds to Windows Presentation Foundation's Decorator.Child
|
|
||||
/// property.
|
|
||||
/// </remarks>
|
|
||||
public FrameworkElement Child |
|
||||
{ |
|
||||
get { return (FrameworkElement)GetValue(ContentProperty); } |
|
||||
set { SetValue(ContentProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the ContentProperty.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( |
|
||||
"Child", typeof(FrameworkElement), typeof(LayoutTransformControl), new PropertyMetadata(ChildChanged)); |
|
||||
#endregion public FrameworkElement Child
|
|
||||
|
|
||||
#region public Transform Transform
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the Transform of the LayoutTransformControl.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Corresponds to UIElement.RenderTransform.
|
|
||||
/// </remarks>
|
|
||||
public Transform Transform |
|
||||
{ |
|
||||
get { return (Transform)GetValue(TransformProperty); } |
|
||||
set { SetValue(TransformProperty, value); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Identifies the TransformProperty dependency property.
|
|
||||
/// </summary>
|
|
||||
public static readonly DependencyProperty TransformProperty = DependencyProperty.Register( |
|
||||
"Transform", typeof(Transform), typeof(LayoutTransformControl), new PropertyMetadata(TransformChanged)); |
|
||||
#endregion
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Value used to work around double arithmetic rounding issues in
|
|
||||
/// Silverlight.
|
|
||||
/// </summary>
|
|
||||
private const double AcceptableDelta = 0.0001; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Value used to work around double arithmetic rounding issues in
|
|
||||
/// Silverlight.
|
|
||||
/// </summary>
|
|
||||
private const int DecimalsAfterRound = 4; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Host panel for Child element.
|
|
||||
/// </summary>
|
|
||||
private Panel _layoutRoot; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// RenderTransform/MatrixTransform applied to layout root.
|
|
||||
/// </summary>
|
|
||||
private MatrixTransform _matrixTransform; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Transformation matrix corresponding to matrix transform.
|
|
||||
/// </summary>
|
|
||||
private Matrix _transformation; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Actual DesiredSize of Child element.
|
|
||||
/// </summary>
|
|
||||
private Size _childActualSize = Size.Empty; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the LayoutTransformControl class.
|
|
||||
/// </summary>
|
|
||||
public LayoutTransformControl() |
|
||||
{ |
|
||||
// Can't tab to LayoutTransformControl
|
|
||||
IsTabStop = false; |
|
||||
#if SILVERLIGHT
|
|
||||
// Disable layout rounding because its rounding of values confuses
|
|
||||
// things.
|
|
||||
UseLayoutRounding = false; |
|
||||
#endif
|
|
||||
// Hard coded template is never meant to be changed and avoids the
|
|
||||
// need for generic.xaml.
|
|
||||
string templateXaml = |
|
||||
@"<ControlTemplate " + |
|
||||
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " + |
|
||||
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>" + |
|
||||
"<Grid x:Name='LayoutRoot' Background='{TemplateBinding Background}'>" + |
|
||||
"<Grid.RenderTransform>" + |
|
||||
"<MatrixTransform x:Name='MatrixTransform'/>" + |
|
||||
"</Grid.RenderTransform>" + |
|
||||
"</Grid>" + |
|
||||
"</ControlTemplate>"; |
|
||||
#if SILVERLIGHT
|
|
||||
Template = (ControlTemplate)XamlReader.Load(templateXaml); |
|
||||
#else
|
|
||||
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(templateXaml))) |
|
||||
{ |
|
||||
Template = (ControlTemplate)XamlReader.Load(stream); |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Called whenever the control's template changes.
|
|
||||
/// </summary>
|
|
||||
public override void OnApplyTemplate() |
|
||||
{ |
|
||||
// Save existing content and remove it from the visual tree
|
|
||||
FrameworkElement savedContent = Child; |
|
||||
Child = null; |
|
||||
// Apply new template
|
|
||||
base.OnApplyTemplate(); |
|
||||
// Find template parts
|
|
||||
_layoutRoot = GetTemplateChild("LayoutRoot") as Panel; |
|
||||
_matrixTransform = GetTemplateChild("MatrixTransform") as MatrixTransform; |
|
||||
// Restore saved content
|
|
||||
Child = savedContent; |
|
||||
// Apply the current transform
|
|
||||
TransformUpdated(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handle changes to the child dependency property.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private static void ChildChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
// Casts are safe because Silverlight is enforcing the types
|
|
||||
((LayoutTransformControl)o).OnChildChanged((FrameworkElement)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Updates content when the child property is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newContent">The new child.</param>
|
|
||||
private void OnChildChanged(FrameworkElement newContent) |
|
||||
{ |
|
||||
if (null != _layoutRoot) |
|
||||
{ |
|
||||
// Clear current child
|
|
||||
_layoutRoot.Children.Clear(); |
|
||||
if (null != newContent) |
|
||||
{ |
|
||||
// Add the new child to the tree
|
|
||||
_layoutRoot.Children.Add(newContent); |
|
||||
} |
|
||||
// New child means re-layout is necessary
|
|
||||
InvalidateMeasure(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Handles changes to the Transform DependencyProperty.
|
|
||||
/// </summary>
|
|
||||
/// <param name="o">The source of the event.</param>
|
|
||||
/// <param name="e">Information about the event.</param>
|
|
||||
private static void TransformChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
|
||||
{ |
|
||||
// Casts are safe because Silverlight is enforcing the types
|
|
||||
((LayoutTransformControl)o).OnTransformChanged((Transform)e.NewValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Processes the transform when the transform is changed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="newValue">The transform to process.</param>
|
|
||||
private void OnTransformChanged(Transform newValue) |
|
||||
{ |
|
||||
ProcessTransform(newValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Notifies the LayoutTransformControl that some aspect of its
|
|
||||
/// Transform property has changed.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Call this to update the LayoutTransform in cases where
|
|
||||
/// LayoutTransformControl wouldn't otherwise know to do so.
|
|
||||
/// </remarks>
|
|
||||
public void TransformUpdated() |
|
||||
{ |
|
||||
ProcessTransform(Transform); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Processes the current transform to determine the corresponding
|
|
||||
/// matrix.
|
|
||||
/// </summary>
|
|
||||
/// <param name="transform">The transform to use to determine the
|
|
||||
/// matrix.</param>
|
|
||||
private void ProcessTransform(Transform transform) |
|
||||
{ |
|
||||
// Get the transform matrix and apply it
|
|
||||
_transformation = RoundMatrix(GetTransformMatrix(transform), DecimalsAfterRound); |
|
||||
if (null != _matrixTransform) |
|
||||
{ |
|
||||
_matrixTransform.Matrix = _transformation; |
|
||||
} |
|
||||
// New transform means re-layout is necessary
|
|
||||
InvalidateMeasure(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Walks the Transform and returns the corresponding matrix.
|
|
||||
/// </summary>
|
|
||||
/// <param name="transform">The transform to create a matrix for.
|
|
||||
/// </param>
|
|
||||
/// <returns>The matrix calculated from the transform.</returns>
|
|
||||
private Matrix GetTransformMatrix(Transform transform) |
|
||||
{ |
|
||||
if (null != transform) |
|
||||
{ |
|
||||
// WPF equivalent of this entire method:
|
|
||||
// return transform.Value;
|
|
||||
|
|
||||
// Process the TransformGroup
|
|
||||
TransformGroup transformGroup = transform as TransformGroup; |
|
||||
if (null != transformGroup) |
|
||||
{ |
|
||||
Matrix groupMatrix = Matrix.Identity; |
|
||||
foreach (Transform child in transformGroup.Children) |
|
||||
{ |
|
||||
groupMatrix = MatrixMultiply(groupMatrix, GetTransformMatrix(child)); |
|
||||
} |
|
||||
return groupMatrix; |
|
||||
} |
|
||||
|
|
||||
// Process the RotateTransform
|
|
||||
RotateTransform rotateTransform = transform as RotateTransform; |
|
||||
if (null != rotateTransform) |
|
||||
{ |
|
||||
double angle = rotateTransform.Angle; |
|
||||
double angleRadians = (2 * Math.PI * angle) / 360; |
|
||||
double sine = Math.Sin(angleRadians); |
|
||||
double cosine = Math.Cos(angleRadians); |
|
||||
return new Matrix(cosine, sine, -sine, cosine, 0, 0); |
|
||||
} |
|
||||
|
|
||||
// Process the ScaleTransform
|
|
||||
ScaleTransform scaleTransform = transform as ScaleTransform; |
|
||||
if (null != scaleTransform) |
|
||||
{ |
|
||||
double scaleX = scaleTransform.ScaleX; |
|
||||
double scaleY = scaleTransform.ScaleY; |
|
||||
return new Matrix(scaleX, 0, 0, scaleY, 0, 0); |
|
||||
} |
|
||||
|
|
||||
// Process the SkewTransform
|
|
||||
SkewTransform skewTransform = transform as SkewTransform; |
|
||||
if (null != skewTransform) |
|
||||
{ |
|
||||
double angleX = skewTransform.AngleX; |
|
||||
double angleY = skewTransform.AngleY; |
|
||||
double angleXRadians = (2 * Math.PI * angleX) / 360; |
|
||||
double angleYRadians = (2 * Math.PI * angleY) / 360; |
|
||||
return new Matrix(1, angleYRadians, angleXRadians, 1, 0, 0); |
|
||||
} |
|
||||
|
|
||||
// Process the MatrixTransform
|
|
||||
MatrixTransform matrixTransform = transform as MatrixTransform; |
|
||||
if (null != matrixTransform) |
|
||||
{ |
|
||||
return matrixTransform.Matrix; |
|
||||
} |
|
||||
|
|
||||
// TranslateTransform has no effect in LayoutTransform
|
|
||||
} |
|
||||
|
|
||||
// Fall back to no-op transformation
|
|
||||
return Matrix.Identity; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides the behavior for the "Measure" pass of layout.
|
|
||||
/// </summary>
|
|
||||
/// <param name="availableSize">The available size that this element can
|
|
||||
/// give to child elements. Infinity can be specified as a value to
|
|
||||
/// indicate that the element will size to whatever content is
|
|
||||
/// available.</param>
|
|
||||
/// <returns>The size that this element determines it needs during
|
|
||||
/// layout, based on its calculations of child element sizes.</returns>
|
|
||||
protected override Size MeasureOverride(Size availableSize) |
|
||||
{ |
|
||||
FrameworkElement child = Child; |
|
||||
if ((null == _layoutRoot) || (null == child)) |
|
||||
{ |
|
||||
// No content, no size
|
|
||||
return Size.Empty; |
|
||||
} |
|
||||
|
|
||||
Size measureSize; |
|
||||
if (_childActualSize == Size.Empty) |
|
||||
{ |
|
||||
// Determine the largest size after the transformation
|
|
||||
measureSize = ComputeLargestTransformedSize(availableSize); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Previous measure/arrange pass determined that
|
|
||||
// Child.DesiredSize was larger than believed.
|
|
||||
measureSize = _childActualSize; |
|
||||
} |
|
||||
|
|
||||
// Perform a mesaure on the _layoutRoot (containing Child)
|
|
||||
_layoutRoot.Measure(measureSize); |
|
||||
|
|
||||
// WPF equivalent of _childActualSize technique (much simpler, but
|
|
||||
// doesn't work on Silverlight 2). If the child is going to render
|
|
||||
// larger than the available size, re-measure according to that size.
|
|
||||
////child.Arrange(new Rect());
|
|
||||
////if (child.RenderSize != child.DesiredSize)
|
|
||||
////{
|
|
||||
//// _layoutRoot.Measure(child.RenderSize);
|
|
||||
////}
|
|
||||
|
|
||||
// Transform DesiredSize to find its width/height
|
|
||||
Rect transformedDesiredRect = RectTransform(new Rect(0, 0, _layoutRoot.DesiredSize.Width, _layoutRoot.DesiredSize.Height), _transformation); |
|
||||
Size transformedDesiredSize = new Size(transformedDesiredRect.Width, transformedDesiredRect.Height); |
|
||||
|
|
||||
// Return result to allocate enough space for the transformation
|
|
||||
return transformedDesiredSize; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides the behavior for the "Arrange" pass of layout.
|
|
||||
/// </summary>
|
|
||||
/// <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>
|
|
||||
/// <remarks>
|
|
||||
/// Using the WPF paramater name finalSize instead of Silverlight's
|
|
||||
/// finalSize for clarity.
|
|
||||
/// </remarks>
|
|
||||
protected override Size ArrangeOverride(Size finalSize) |
|
||||
{ |
|
||||
FrameworkElement child = Child; |
|
||||
if ((null == _layoutRoot) || (null == child)) |
|
||||
{ |
|
||||
// No child, use whatever was given
|
|
||||
return finalSize; |
|
||||
} |
|
||||
|
|
||||
// Determine the largest available size after the transformation
|
|
||||
Size finalSizeTransformed = ComputeLargestTransformedSize(finalSize); |
|
||||
if (IsSizeSmaller(finalSizeTransformed, _layoutRoot.DesiredSize)) |
|
||||
{ |
|
||||
// Some elements do not like being given less space than they asked for (ex: TextBlock)
|
|
||||
// Bump the working size up to do the right thing by them
|
|
||||
finalSizeTransformed = _layoutRoot.DesiredSize; |
|
||||
} |
|
||||
|
|
||||
// Transform the working size to find its width/height
|
|
||||
Rect transformedRect = RectTransform(new Rect(0, 0, finalSizeTransformed.Width, finalSizeTransformed.Height), _transformation); |
|
||||
// Create the Arrange rect to center the transformed content
|
|
||||
Rect finalRect = new Rect( |
|
||||
-transformedRect.Left + ((finalSize.Width - transformedRect.Width) / 2), |
|
||||
-transformedRect.Top + ((finalSize.Height - transformedRect.Height) / 2), |
|
||||
finalSizeTransformed.Width, |
|
||||
finalSizeTransformed.Height); |
|
||||
|
|
||||
// Perform an Arrange on _layoutRoot (containing Child)
|
|
||||
_layoutRoot.Arrange(finalRect); |
|
||||
|
|
||||
// This is the first opportunity under Silverlight to find out the Child's true DesiredSize
|
|
||||
if (IsSizeSmaller(finalSizeTransformed, child.RenderSize) && (Size.Empty == _childActualSize)) |
|
||||
{ |
|
||||
// Unfortunately, all the work so far is invalid because the wrong DesiredSize was used
|
|
||||
// Make a note of the actual DesiredSize
|
|
||||
_childActualSize = new Size(child.ActualWidth, child.ActualHeight); |
|
||||
|
|
||||
// Force a new measure/arrange pass
|
|
||||
InvalidateMeasure(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Clear the "need to measure/arrange again" flag
|
|
||||
_childActualSize = Size.Empty; |
|
||||
} |
|
||||
|
|
||||
// Return result to perform the transformation
|
|
||||
return finalSize; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes the largest usable size after applying the transformation
|
|
||||
/// to the specified bounds.
|
|
||||
/// </summary>
|
|
||||
/// <param name="arrangeBounds">The size to arrange within.</param>
|
|
||||
/// <returns>The size required.</returns>
|
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Closely corresponds to WPF's FrameworkElement.FindMaximalAreaLocalSpaceRect.")] |
|
||||
private Size ComputeLargestTransformedSize(Size arrangeBounds) |
|
||||
{ |
|
||||
// Computed largest transformed size
|
|
||||
Size computedSize = Size.Empty; |
|
||||
|
|
||||
// Detect infinite bounds and constrain the scenario
|
|
||||
bool infiniteWidth = double.IsInfinity(arrangeBounds.Width); |
|
||||
if (infiniteWidth) |
|
||||
{ |
|
||||
arrangeBounds.Width = arrangeBounds.Height; |
|
||||
} |
|
||||
bool infiniteHeight = double.IsInfinity(arrangeBounds.Height); |
|
||||
if (infiniteHeight) |
|
||||
{ |
|
||||
arrangeBounds.Height = arrangeBounds.Width; |
|
||||
} |
|
||||
|
|
||||
// Capture the matrix parameters
|
|
||||
double a = _transformation.M11; |
|
||||
double b = _transformation.M12; |
|
||||
double c = _transformation.M21; |
|
||||
double d = _transformation.M22; |
|
||||
|
|
||||
// Compute maximum possible transformed width/height based on starting width/height
|
|
||||
// These constraints define two lines in the positive x/y quadrant
|
|
||||
double maxWidthFromWidth = Math.Abs(arrangeBounds.Width / a); |
|
||||
double maxHeightFromWidth = Math.Abs(arrangeBounds.Width / c); |
|
||||
double maxWidthFromHeight = Math.Abs(arrangeBounds.Height / b); |
|
||||
double maxHeightFromHeight = Math.Abs(arrangeBounds.Height / d); |
|
||||
|
|
||||
// The transformed width/height that maximize the area under each segment is its midpoint
|
|
||||
// At most one of the two midpoints will satisfy both constraints
|
|
||||
double idealWidthFromWidth = maxWidthFromWidth / 2; |
|
||||
double idealHeightFromWidth = maxHeightFromWidth / 2; |
|
||||
double idealWidthFromHeight = maxWidthFromHeight / 2; |
|
||||
double idealHeightFromHeight = maxHeightFromHeight / 2; |
|
||||
|
|
||||
// Compute slope of both constraint lines
|
|
||||
double slopeFromWidth = -(maxHeightFromWidth / maxWidthFromWidth); |
|
||||
double slopeFromHeight = -(maxHeightFromHeight / maxWidthFromHeight); |
|
||||
|
|
||||
if ((0 == arrangeBounds.Width) || (0 == arrangeBounds.Height)) |
|
||||
{ |
|
||||
// Check for empty bounds
|
|
||||
computedSize = new Size(0, 0); |
|
||||
} |
|
||||
else if (infiniteWidth && infiniteHeight) |
|
||||
{ |
|
||||
// Check for completely unbound scenario
|
|
||||
computedSize = new Size(double.PositiveInfinity, double.PositiveInfinity); |
|
||||
} |
|
||||
else if (!MatrixHasInverse(_transformation)) |
|
||||
{ |
|
||||
// Check for singular matrix
|
|
||||
computedSize = new Size(0, 0); |
|
||||
} |
|
||||
else if ((0 == b) || (0 == c)) |
|
||||
{ |
|
||||
// Check for 0/180 degree special cases
|
|
||||
double maxHeight = (infiniteHeight ? double.PositiveInfinity : maxHeightFromHeight); |
|
||||
double maxWidth = (infiniteWidth ? double.PositiveInfinity : maxWidthFromWidth); |
|
||||
|
|
||||
if ((0 == b) && (0 == c)) |
|
||||
{ |
|
||||
// No constraints
|
|
||||
computedSize = new Size(maxWidth, maxHeight); |
|
||||
} |
|
||||
else if (0 == b) |
|
||||
{ |
|
||||
// Constrained by width
|
|
||||
double computedHeight = Math.Min(idealHeightFromWidth, maxHeight); |
|
||||
computedSize = new Size( |
|
||||
maxWidth - Math.Abs((c * computedHeight) / a), |
|
||||
computedHeight); |
|
||||
} |
|
||||
else if (0 == c) |
|
||||
{ |
|
||||
// Constrained by height
|
|
||||
double computedWidth = Math.Min(idealWidthFromHeight, maxWidth); |
|
||||
computedSize = new Size( |
|
||||
computedWidth, |
|
||||
maxHeight - Math.Abs((b * computedWidth) / d)); |
|
||||
} |
|
||||
} |
|
||||
else if ((0 == a) || (0 == d)) |
|
||||
{ |
|
||||
// Check for 90/270 degree special cases
|
|
||||
|
|
||||
double maxWidth = (infiniteHeight ? double.PositiveInfinity : maxWidthFromHeight); |
|
||||
double maxHeight = (infiniteWidth ? double.PositiveInfinity : maxHeightFromWidth); |
|
||||
|
|
||||
if ((0 == a) && (0 == d)) |
|
||||
{ |
|
||||
// No constraints
|
|
||||
computedSize = new Size(maxWidth, maxHeight); |
|
||||
} |
|
||||
else if (0 == a) |
|
||||
{ |
|
||||
// Constrained by width
|
|
||||
double computedHeight = Math.Min(idealHeightFromHeight, maxHeight); |
|
||||
computedSize = new Size( |
|
||||
maxWidth - Math.Abs((d * computedHeight) / b), |
|
||||
computedHeight); |
|
||||
} |
|
||||
else if (0 == d) |
|
||||
{ |
|
||||
// Constrained by height.
|
|
||||
double computedWidth = Math.Min(idealWidthFromWidth, maxWidth); |
|
||||
computedSize = new Size( |
|
||||
computedWidth, |
|
||||
maxHeight - Math.Abs((a * computedWidth) / c)); |
|
||||
} |
|
||||
} |
|
||||
else if (idealHeightFromWidth <= ((slopeFromHeight * idealWidthFromWidth) + maxHeightFromHeight)) |
|
||||
{ |
|
||||
// Check the width midpoint for viability (by being below the
|
|
||||
// height constraint line).
|
|
||||
computedSize = new Size(idealWidthFromWidth, idealHeightFromWidth); |
|
||||
} |
|
||||
else if (idealHeightFromHeight <= ((slopeFromWidth * idealWidthFromHeight) + maxHeightFromWidth)) |
|
||||
{ |
|
||||
// Check the height midpoint for viability (by being below the
|
|
||||
// width constraint line).
|
|
||||
computedSize = new Size(idealWidthFromHeight, idealHeightFromHeight); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Neither midpoint is viable; use the intersection of the two
|
|
||||
// constraint lines instead.
|
|
||||
|
|
||||
// Compute width by setting heights equal (m1*x+c1=m2*x+c2).
|
|
||||
double computedWidth = (maxHeightFromHeight - maxHeightFromWidth) / (slopeFromWidth - slopeFromHeight); |
|
||||
// Compute height from width constraint line (y=m*x+c; using
|
|
||||
// height would give same result).
|
|
||||
computedSize = new Size( |
|
||||
computedWidth, |
|
||||
(slopeFromWidth * computedWidth) + maxHeightFromWidth); |
|
||||
} |
|
||||
|
|
||||
return computedSize; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Return true if Size a is smaller than Size b in either dimension.
|
|
||||
/// </summary>
|
|
||||
/// <param name="a">The left size.</param>
|
|
||||
/// <param name="b">The right size.</param>
|
|
||||
/// <returns>A value indicating whether the left size is smaller than
|
|
||||
/// the right.</returns>
|
|
||||
private static bool IsSizeSmaller(Size a, Size b) |
|
||||
{ |
|
||||
// WPF equivalent of following code:
|
|
||||
// return ((a.Width < b.Width) || (a.Height < b.Height));
|
|
||||
return ((a.Width + AcceptableDelta < b.Width) || (a.Height + AcceptableDelta < b.Height)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rounds the non-offset elements of a matrix to avoid issues due to
|
|
||||
/// floating point imprecision.
|
|
||||
/// </summary>
|
|
||||
/// <param name="matrix">The matrix to round.</param>
|
|
||||
/// <param name="decimalsAfterRound">The number of decimals after the
|
|
||||
/// round.</param>
|
|
||||
/// <returns>The rounded matrix.</returns>
|
|
||||
private static Matrix RoundMatrix(Matrix matrix, int decimalsAfterRound) |
|
||||
{ |
|
||||
return new Matrix( |
|
||||
Math.Round(matrix.M11, decimalsAfterRound), |
|
||||
Math.Round(matrix.M12, decimalsAfterRound), |
|
||||
Math.Round(matrix.M21, decimalsAfterRound), |
|
||||
Math.Round(matrix.M22, decimalsAfterRound), |
|
||||
matrix.OffsetX, |
|
||||
matrix.OffsetY); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Implement Windows Presentation Foundation's Rect.Transform on
|
|
||||
/// Silverlight.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rectangle">The rectangle to transform.</param>
|
|
||||
/// <param name="matrix">The matrix to use to transform the rectangle.
|
|
||||
/// </param>
|
|
||||
/// <returns>The transformed rectangle.</returns>
|
|
||||
private static Rect RectTransform(Rect rectangle, Matrix matrix) |
|
||||
{ |
|
||||
// WPF equivalent of following code:
|
|
||||
// var rectTransformed = Rect.Transform(rect, matrix);
|
|
||||
Point leftTop = matrix.Transform(new Point(rectangle.Left, rectangle.Top)); |
|
||||
Point rightTop = matrix.Transform(new Point(rectangle.Right, rectangle.Top)); |
|
||||
Point leftBottom = matrix.Transform(new Point(rectangle.Left, rectangle.Bottom)); |
|
||||
Point rightBottom = matrix.Transform(new Point(rectangle.Right, rectangle.Bottom)); |
|
||||
double left = Math.Min(Math.Min(leftTop.X, rightTop.X), Math.Min(leftBottom.X, rightBottom.X)); |
|
||||
double top = Math.Min(Math.Min(leftTop.Y, rightTop.Y), Math.Min(leftBottom.Y, rightBottom.Y)); |
|
||||
double right = Math.Max(Math.Max(leftTop.X, rightTop.X), Math.Max(leftBottom.X, rightBottom.X)); |
|
||||
double bottom = Math.Max(Math.Max(leftTop.Y, rightTop.Y), Math.Max(leftBottom.Y, rightBottom.Y)); |
|
||||
Rect rectTransformed = new Rect(left, top, right - left, bottom - top); |
|
||||
return rectTransformed; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Implements Windows Presentation Foundation's Matrix.Multiply on
|
|
||||
/// Silverlight.
|
|
||||
/// </summary>
|
|
||||
/// <param name="matrix1">The left matrix.</param>
|
|
||||
/// <param name="matrix2">The right matrix.</param>
|
|
||||
/// <returns>The product of the two matrices.</returns>
|
|
||||
private static Matrix MatrixMultiply(Matrix matrix1, Matrix matrix2) |
|
||||
{ |
|
||||
// WPF equivalent of following code:
|
|
||||
// return Matrix.Multiply(matrix1, matrix2);
|
|
||||
return new Matrix( |
|
||||
(matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21), |
|
||||
(matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22), |
|
||||
(matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21), |
|
||||
(matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22), |
|
||||
((matrix1.OffsetX * matrix2.M11) + (matrix1.OffsetY * matrix2.M21)) + matrix2.OffsetX, |
|
||||
((matrix1.OffsetX * matrix2.M12) + (matrix1.OffsetY * matrix2.M22)) + matrix2.OffsetY); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Implements Windows Presentation Foundation's Matrix.HasInverse on
|
|
||||
/// Silverlight.
|
|
||||
/// </summary>
|
|
||||
/// <param name="matrix">The matrix.</param>
|
|
||||
/// <returns>True if matrix has an inverse.</returns>
|
|
||||
private static bool MatrixHasInverse(Matrix matrix) |
|
||||
{ |
|
||||
// WPF equivalent of following code:
|
|
||||
// return matrix.HasInverse;
|
|
||||
return (0 != ((matrix.M11 * matrix.M22) - (matrix.M12 * matrix.M21))); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue