140 changed files with 28493 additions and 1 deletions
@ -0,0 +1,175 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// (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 |
|||
} |
|||
} |
|||
@ -0,0 +1,237 @@ |
|||
// (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) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
// (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}" }); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,363 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// (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
@ -0,0 +1,449 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,798 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,400 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,289 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,622 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,747 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,996 @@ |
|||
// (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)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,576 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,554 @@ |
|||
// (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)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,250 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,232 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
// (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
|
|||
@ -0,0 +1,136 @@ |
|||
// (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
|
|||
@ -0,0 +1,415 @@ |
|||
// (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>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,358 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,137 @@ |
|||
// (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
|
|||
@ -0,0 +1,265 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,268 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,260 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
// (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
@ -0,0 +1,665 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,352 @@ |
|||
// (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
@ -0,0 +1,21 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,306 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
// (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
|
|||
@ -0,0 +1,597 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,205 @@ |
|||
// (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
|
|||
@ -0,0 +1,113 @@ |
|||
// (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>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,635 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// (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, |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,354 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
@ -0,0 +1,143 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,371 @@ |
|||
// (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; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
// (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() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,815 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// (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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,192 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
@ -0,0 +1,307 @@ |
|||
// (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
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
// (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")] |
|||
@ -0,0 +1,54 @@ |
|||
// (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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// (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; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue