Browse Source

Added new DataVisualization Charting controls. These controls are the same ones that are currently in the WPF Toolkit except they have been updated to match the SIlvelright 4 toolkit. You get the bug fixes/updates before they are offically released in the next version of Microsofts WPF Toolkit. Special thanks to Microsoft's Dave Anson (a.k.a Delay)

pull/1645/head
brianlagunas_cp 16 years ago
parent
commit
70c20babc0
  1. 17
      ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln
  2. 175
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/AggregatedObservableCollection.cs
  3. 29
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/AnimationSequence.cs
  4. 237
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/Axis.cs
  5. 64
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisIntervalType.cs
  6. 112
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisLabel.cs
  7. 38
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisLocation.cs
  8. 31
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisOrientation.cs
  9. 363
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/CategoryAxis.cs
  10. 28
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/CategorySortOrder.cs
  11. 1014
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeAxis.cs
  12. 449
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeAxisLabel.cs
  13. 58
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeIntervalType.cs
  14. 798
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DisplayAxis.cs
  15. 106
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DisplayAxisGridLines.cs
  16. 24
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAnchoredToOrigin.cs
  17. 51
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAxis.cs
  18. 19
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAxisListener.cs
  19. 31
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/ICategoryAxis.cs
  20. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IDataConsumer.cs
  21. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IDataProvider.cs
  22. 35
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeAxis.cs
  23. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeConsumer.cs
  24. 24
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeProvider.cs
  25. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IValueMarginConsumer.cs
  26. 25
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IValueMarginProvider.cs
  27. 400
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/LinearAxis.cs
  28. 125
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NullableConverter.cs
  29. 289
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NumericAxis.cs
  30. 36
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NumericAxisLabel.cs
  31. 90
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/OrientedAxisGridLines.cs
  32. 622
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/RangeAxis.cs
  33. 90
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/ValueMargin.cs
  34. 747
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Chart/Chart.cs
  35. 152
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Chart/SeriesHostAxesCollection.cs
  36. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/AreaDataPoint.cs
  37. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/BarDataPoint.cs
  38. 156
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/BubbleDataPoint.cs
  39. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/ColumnDataPoint.cs
  40. 996
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/DataPoint.cs
  41. 43
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/DataPointState.cs
  42. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/LineDataPoint.cs
  43. 576
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/PieDataPoint.cs
  44. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/ScatterDataPoint.cs
  45. 57
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/FrameworkElementExtensions.cs
  46. 18
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/IRequireSeriesHost.cs
  47. 40
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ISeriesHost.cs
  48. 54
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ISeriesHostExtensions.cs
  49. 111
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/DelegatingListBox.cs
  50. 44
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/Edge.cs
  51. 554
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/EdgePanel.cs
  52. 56
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryDispensedEventArgs.cs
  53. 250
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryDispenser.cs
  54. 232
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryEnumerator.cs
  55. 207
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/AreaSeries.cs
  56. 136
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/BarSeries.cs
  57. 415
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/BubbleSeries.cs
  58. 358
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ColumnBarBaseSeries.cs
  59. 137
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ColumnSeries.cs
  60. 265
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/AreaSeries.cs
  61. 227
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/BarSeries.cs
  62. 227
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/ColumnSeries.cs
  63. 268
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/LineSeries.cs
  64. 260
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/ScatterSeries.cs
  65. 54
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/SelectionEnabledToSelectionModeConverter.cs
  66. 1398
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSeries.cs
  67. 665
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSeriesWithAxes.cs
  68. 352
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSingleSeriesWithAxes.cs
  69. 1611
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DefinitionSeries.cs
  70. 21
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/IRequireGlobalSeriesIndex.cs
  71. 20
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ISeries.cs
  72. 39
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LegendItem.cs
  73. 306
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LineAreaBaseSeries.cs
  74. 150
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LineSeries.cs
  75. 597
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/PieSeries.cs
  76. 205
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ScatterSeries.cs
  77. 113
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Series.cs
  78. 635
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/SeriesDefinition.cs
  79. 28
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/SeriesSelectionMode.cs
  80. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100AreaSeries.cs
  81. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100BarSeries.cs
  82. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100ColumnSeries.cs
  83. 22
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100LineSeries.cs
  84. 354
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedAreaLineSeries.cs
  85. 143
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedAreaSeries.cs
  86. 371
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedBarColumnSeries.cs
  87. 32
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedBarSeries.cs
  88. 32
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedColumnSeries.cs
  89. 86
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedLineSeries.cs
  90. 41
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ValueMarginCoordinateAndOverlap.cs
  91. 815
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/LeftLeaningRedBlackTree.cs
  92. 101
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/MultipleDictionary.cs
  93. 86
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/OrderedMultipleDictionary.cs
  94. 192
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/DependencyPropertyAnimationHelper.cs
  95. 44
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/DesignerProperties.cs
  96. 307
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/EnumerableFunctions.cs
  97. 60
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GenericEqualityComparer.cs
  98. 131
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GlobalSuppressions.cs
  99. 54
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GridExtensions.cs
  100. 32
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/IResourceDictionaryDispenser.cs

17
ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln

@ -7,9 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFToolkit.Extended.Samples
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFToolkit.Extended.Design", "Src\WPFToolkit.Extended.Design\WPFToolkit.Extended.Design.csproj", "{FA6645C6-7CA5-427C-91F2-916D9FECD76C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFToolkit.Extended.DataVisualization", "Src\WPFToolkit.Extended.DataVisualization\WPFToolkit.Extended.DataVisualization.csproj", "{C921A4BC-1781-4A14-B1D0-C41E49C7606A}"
EndProject
Global
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 4
SccNumberOfProjects = 5
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs02
SccLocalPath0 = .
@ -22,6 +24,9 @@ Global
SccProjectUniqueName3 = Src\\WPFToolkit.Extended.Design\\WPFToolkit.Extended.Design.csproj
SccProjectName3 = Src/WPFToolkit.Extended.Design
SccLocalPath3 = Src\\WPFToolkit.Extended.Design
SccProjectUniqueName4 = Src\\WPFToolkit.Extended.DataVisualization\\WPFToolkit.Extended.DataVisualization.csproj
SccProjectName4 = Src/WPFToolkit.Extended.DataVisualization
SccLocalPath4 = Src\\WPFToolkit.Extended.DataVisualization
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -62,6 +67,16 @@ Global
{FA6645C6-7CA5-427C-91F2-916D9FECD76C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{FA6645C6-7CA5-427C-91F2-916D9FECD76C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{FA6645C6-7CA5-427C-91F2-916D9FECD76C}.Release|x86.ActiveCfg = Release|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Debug|x86.ActiveCfg = Debug|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Release|Any CPU.Build.0 = Release|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C921A4BC-1781-4A14-B1D0-C41E49C7606A}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

175
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/AggregatedObservableCollection.cs

@ -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; }
}
}

29
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/AnimationSequence.cs

@ -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
}
}

237
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/Axis.cs

@ -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)
{
}
}
}

64
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisIntervalType.cs

@ -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,
}
}

112
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisLabel.cs

@ -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}" });
}
}
}

38
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisLocation.cs

@ -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,
}
}

31
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/AxisOrientation.cs

@ -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,
}
}

363
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/CategoryAxis.cs

@ -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;
}
}
}

28
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/CategorySortOrder.cs

@ -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
}
}

1014
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeAxis.cs

File diff suppressed because it is too large

449
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeAxisLabel.cs

@ -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();
}
}
}
}

58
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DateTimeIntervalType.cs

@ -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,
}
}

798
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DisplayAxis.cs

@ -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);
}
}
}
}

106
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/DisplayAxisGridLines.cs

@ -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();
}
}

24
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAnchoredToOrigin.cs

@ -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; }
}
}

51
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAxis.cs

@ -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; }
}
}

19
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IAxisListener.cs

@ -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);
}
}

31
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/ICategoryAxis.cs

@ -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);
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IDataConsumer.cs

@ -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);
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IDataProvider.cs

@ -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);
}
}

35
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeAxis.cs

@ -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; }
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeConsumer.cs

@ -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);
}
}

24
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IRangeProvider.cs

@ -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);
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IValueMarginConsumer.cs

@ -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);
}
}

25
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/IValueMarginProvider.cs

@ -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);
}
}

400
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/LinearAxis.cs

@ -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;
}
}
}

125
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NullableConverter.cs

@ -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);
}
}
}

289
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NumericAxis.cs

@ -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;
}
}
}

36
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/NumericAxisLabel.cs

@ -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
}
}
}

90
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/OrientedAxisGridLines.cs

@ -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();
}
}
}
}

622
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/RangeAxis.cs

@ -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; }
}
}

90
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Axis/ValueMargin.cs

@ -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();
}
}
}

747
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Chart/Chart.cs

@ -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; }
}
}
}

152
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Chart/SeriesHostAxesCollection.cs

@ -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);
}
}
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/AreaDataPoint.cs

@ -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
}
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/BarDataPoint.cs

@ -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
}
}
}

156
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/BubbleDataPoint.cs

@ -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
}
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/ColumnDataPoint.cs

@ -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
}
}
}

996
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/DataPoint.cs

@ -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));
}
}
}

43
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/DataPointState.cs

@ -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,
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/LineDataPoint.cs

@ -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
}
}
}

576
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/PieDataPoint.cs

@ -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);
}
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/DataPoint/ScatterDataPoint.cs

@ -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
}
}
}

57
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/FrameworkElementExtensions.cs

@ -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;
}
}
}

18
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/IRequireSeriesHost.cs

@ -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; }
}
}

40
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ISeriesHost.cs

@ -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; }
}
}

54
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ISeriesHostExtensions.cs

@ -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();
}
}
}

111
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/DelegatingListBox.cs

@ -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);
}
}
}
}

44
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/Edge.cs

@ -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,
}
}

554
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Primitives/EdgePanel.cs

@ -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));
}
}
}

56
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryDispensedEventArgs.cs

@ -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();
}
}
}

250
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryDispenser.cs

@ -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();
}
}
}

232
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ResourceDictionaryEnumerator.cs

@ -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);
}
}
}

207
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/AreaSeries.cs

@ -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

136
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/BarSeries.cs

@ -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

415
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/BubbleSeries.cs

@ -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>();
}
}
}

358
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ColumnBarBaseSeries.cs

@ -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; }
}
}
}

137
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ColumnSeries.cs

@ -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

265
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/AreaSeries.cs

@ -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
}
}

227
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/BarSeries.cs

@ -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
}
}

227
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/ColumnSeries.cs

@ -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
}
}

268
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/LineSeries.cs

@ -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
}
}

260
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/ScatterSeries.cs

@ -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
}
}

54
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Compatible/SelectionEnabledToSelectionModeConverter.cs

@ -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();
}
}
}

1398
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSeries.cs

File diff suppressed because it is too large

665
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSeriesWithAxes.cs

@ -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);
}
}
}

352
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DataPointSingleSeriesWithAxes.cs

@ -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();
}
}
}

1611
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/DefinitionSeries.cs

File diff suppressed because it is too large

21
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/IRequireGlobalSeriesIndex.cs

@ -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);
}
}

20
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ISeries.cs

@ -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; }
}
}

39
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LegendItem.cs

@ -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
}
}
}

306
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LineAreaBaseSeries.cs

@ -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);
}
}

150
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/LineSeries.cs

@ -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

597
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/PieSeries.cs

@ -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);
}
}
}

205
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/ScatterSeries.cs

@ -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

113
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Series.cs

@ -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>();
}
}
}

635
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/SeriesDefinition.cs

@ -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
}
}

28
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/SeriesSelectionMode.cs

@ -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,
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100AreaSeries.cs

@ -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;
}
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100BarSeries.cs

@ -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;
}
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100ColumnSeries.cs

@ -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;
}
}
}

22
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/Stacked100LineSeries.cs

@ -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;
}
}
}

354
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedAreaLineSeries.cs

@ -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);
}
}

143
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedAreaSeries.cs

@ -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; }
}
}
}

371
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedBarColumnSeries.cs

@ -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; }
}
}
}

32
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedBarSeries.cs

@ -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();
}
}
}

32
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedColumnSeries.cs

@ -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();
}
}
}

86
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/Series/StackedLineSeries.cs

@ -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
}
}
}

41
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Charting/ValueMarginCoordinateAndOverlap.cs

@ -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()
{
}
}
}

815
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/LeftLeaningRedBlackTree.cs

@ -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
}
}

101
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/MultipleDictionary.cs

@ -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();
}
}
}

86
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/Collections/OrderedMultipleDictionary.cs

@ -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();
}
}
}

192
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/DependencyPropertyAnimationHelper.cs

@ -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;
}
}
}

44
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/DesignerProperties.cs

@ -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;
}
}

307
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/EnumerableFunctions.cs

@ -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
}
}
}

60
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GenericEqualityComparer.cs

@ -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);
}
}
}

131
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GlobalSuppressions.cs

@ -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")]

54
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/GridExtensions.cs

@ -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);
}
}
}
}
}

32
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended.DataVisualization/IResourceDictionaryDispenser.cs

@ -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…
Cancel
Save