using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Data;
using System.Collections.Specialized;
namespace SilverlightChart
{
///
/// Helper that allows for the easy setting of global chart properties and binding to multiple series.
/// Original code is provided at http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/351/Silverlight-Charts-Binding-multiple-Series.aspx
/// Thanks to Jeremiah Morrill for the initial implementation.
///
public class ChartHelper
{
#region SeriesType enum
///
/// Represents the supported series types
///
public enum SeriesType
{
///
/// A line chart
///
Line,
///
/// A bar chart (note that Bar displays its Independent and Dependent data on reversed axes)
///
Bar,
///
/// A column chart
///
Column,
///
/// A scatter chart
///
Scatter,
///
/// A pie chart
///
Pie,
///
/// An area chart
///
Area,
///
/// A bubble chart
///
Bubble
}
#endregion
#region SeriesSource
///
/// Dependency property binds the source of data for the series
///
public static readonly DependencyProperty SeriesSourceProperty =
DependencyProperty.RegisterAttached("SeriesSource",
typeof(IEnumerable),
typeof(ChartHelper),
new PropertyMetadata(SeriesSourceChanged));
///
/// Gets the series source.
///
/// The chart object upon which this helper acts.
///
public static IEnumerable GetSeriesSource(DependencyObject d)
{
return (IEnumerable)d.GetValue(SeriesSourceProperty);
}
///
/// Sets the series source.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetSeriesSource(DependencyObject d, IEnumerable value)
{
d.SetValue(SeriesSourceProperty, value);
}
#endregion
#region DependentValueBinding
///
/// Represents the Dependent Value binding (which property on the objects contained in the SeriesSource should be used as the dependent value)
///
public static readonly DependencyProperty DependentValueBindingProperty =
DependencyProperty.RegisterAttached("DependentValueBinding",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the dependent value binding.
///
/// The chart object upon which this helper acts.
///
public static string GetDependentValueBinding(DependencyObject d)
{
return (string)d.GetValue(DependentValueBindingProperty);
}
///
/// Sets the dependent value binding.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetDependentValueBinding(DependencyObject d, string value)
{
d.SetValue(DependentValueBindingProperty, value);
}
#endregion
#region IndependentValueBinding
///
/// Represents the Independent Value binding (which property on the objects contained in the SeriesSource should be used as the independent value)
///
public static readonly DependencyProperty IndependentValueBindingProperty =
DependencyProperty.RegisterAttached("IndependentValueBinding",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the independent value binding.
///
/// The chart object upon which this helper acts.
///
public static string GetIndependentValueBinding(DependencyObject d)
{
return (string)d.GetValue(IndependentValueBindingProperty);
}
///
/// Sets the independent value binding.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetIndependentValueBinding(DependencyObject d, string value)
{
d.SetValue(IndependentValueBindingProperty, value);
}
#endregion
#region Title
///
/// Represents the Title binding (which DataContext property should be bound to Title)
///
public static readonly DependencyProperty TitleProperty =
DependencyProperty.RegisterAttached("Title",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the title.
///
/// The chart object upon which this helper acts.
///
public static string GetTitle(DependencyObject d)
{
return (string)d.GetValue(TitleProperty);
}
///
/// Sets the title.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetTitle(DependencyObject d, string value)
{
d.SetValue(TitleProperty, value);
}
#endregion
#region XAxisTitle
///
/// Represents the X Axis Title binding (which DataContext property should be bound to X Axis Title)
///
public static readonly DependencyProperty XAxisTitleProperty =
DependencyProperty.RegisterAttached("XAxisTitle",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the X axis title.
///
/// The chart object upon which this helper acts.
///
public static string GetXAxisTitle(DependencyObject d)
{
return (string)d.GetValue(XAxisTitleProperty);
}
///
/// Sets the X axis title.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetXAxisTitle(DependencyObject d, string value)
{
d.SetValue(XAxisTitleProperty, value);
}
#endregion
#region XAxisMinimum
///
/// Represents the X Axis Minimum binding (which DataContext property should be bound to X Axis Minimum value)
///
public static readonly DependencyProperty XAxisMinimumProperty =
DependencyProperty.RegisterAttached("XAxisMinimum",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the X axis minimum.
///
/// The chart object upon which this helper acts.
///
public static string GetXAxisMinimum(DependencyObject d)
{
return (string)d.GetValue(XAxisMinimumProperty);
}
///
/// Sets the X axis minimum.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetXAxisMinimum(DependencyObject d, string value)
{
d.SetValue(XAxisMinimumProperty, value);
}
#endregion
#region XAxisMaximum
///
/// Represents the X Axis Maximum binding (which DataContext property should be bound to X Axis Maximum value)
///
public static readonly DependencyProperty XAxisMaximumProperty =
DependencyProperty.RegisterAttached("XAxisMaximum",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the X axis maximum.
///
/// The chart object upon which this helper acts.
///
public static string GetXAxisMaximum(DependencyObject d)
{
return (string)d.GetValue(XAxisMaximumProperty);
}
///
/// Sets the X axis maximum.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetXAxisMaximum(DependencyObject d, string value)
{
d.SetValue(XAxisMaximumProperty, value);
}
#endregion
#region YAxisTitle
///
/// Represents the Y Axis Title binding (which DataContext property should be bound to Y Axis Title)
///
public static readonly DependencyProperty YAxisTitleProperty =
DependencyProperty.RegisterAttached("YAxisTitle",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the Y axis title.
///
/// The chart object upon which this helper acts.
///
public static string GetYAxisTitle(DependencyObject d)
{
return (string)d.GetValue(YAxisTitleProperty);
}
///
/// Sets the Y axis title.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetYAxisTitle(DependencyObject d, string value)
{
d.SetValue(YAxisTitleProperty, value);
}
#endregion
#region YAxisMinimum
///
/// Represents the Y Axis Minimum binding (which DataContext property should be bound to Y Axis Minimum value)
///
public static readonly DependencyProperty YAxisMinimumProperty =
DependencyProperty.RegisterAttached("YAxisMinimum",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the Y axis minimum.
///
/// The chart object upon which this helper acts.
///
public static string GetYAxisMinimum(DependencyObject d)
{
return (string)d.GetValue(YAxisMinimumProperty);
}
///
/// Sets the Y axis minimum.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetYAxisMinimum(DependencyObject d, string value)
{
d.SetValue(YAxisMinimumProperty, value);
}
#region YAxisMaximum
///
/// Represents the Y Axis Maximum binding (which DataContext property should be bound to Y Axis Maximum value)
///
public static readonly DependencyProperty YAxisMaximumProperty =
DependencyProperty.RegisterAttached("YAxisMaximum",
typeof(string),
typeof(ChartHelper),
null);
///
/// Gets the Y axis maximum.
///
/// The chart object upon which this helper acts.
///
public static string GetYAxisMaximum(DependencyObject d)
{
return (string)d.GetValue(YAxisMaximumProperty);
}
///
/// Sets the Y axis maximum.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetYAxisMaximum(DependencyObject d, string value)
{
d.SetValue(YAxisMaximumProperty, value);
}
#endregion
#endregion
#region SeriesType
///
/// Represents the data binding for the Series Type. See the enum for acceptable values.
///
public static readonly DependencyProperty SeriesTypeProperty =
DependencyProperty.RegisterAttached("SeriesType",
typeof(SeriesType),
typeof(ChartHelper),
new PropertyMetadata(SeriesType.Bar, SeriesSourceChanged));
///
/// Gets the type of the series.
///
/// The chart object upon which this helper acts.
///
public static SeriesType GetSeriesType(DependencyObject d)
{
return (SeriesType)d.GetValue(SeriesTypeProperty);
}
///
/// Sets the type of the series.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetSeriesType(DependencyObject d, SeriesType value)
{
d.SetValue(SeriesTypeProperty, value);
}
#endregion
#region SeriesStyle
///
/// Represents the data binding for the Series Style.
///
public static readonly DependencyProperty SeriesStyleProperty =
DependencyProperty.RegisterAttached("SeriesStyle",
typeof(Style),
typeof(ChartHelper),
null);
///
/// Gets the series style.
///
/// The chart object upon which this helper acts.
///
public static Style GetSeriesStyle(DependencyObject d)
{
return (Style)d.GetValue(SeriesStyleProperty);
}
///
/// Sets the series style.
///
/// The chart object upon which this helper acts.
/// The value.
public static void SetSeriesStyle(DependencyObject d, Style value)
{
d.SetValue(SeriesStyleProperty, value);
}
#endregion
///
/// An event handler that is fired when the Series Source or other relavent values change.
///
/// The chart object upon which this helper acts.
/// The instance containing the event data.
private static void SeriesSourceChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is Chart))
{
throw new Exception("Series attached property only works on a Chart type");
}
var chart = d as Chart;
/* Clear out any old series in the chart */
chart.Series.Clear();
/* Get our collection of data we need for each series */
var chartSeriesSource = e.NewValue as IEnumerable;
if (chartSeriesSource == null)
{
if (chart.DataContext == null)
{
return;
}
chartSeriesSource = GetSeriesSource(d);
}
if (chartSeriesSource == null)
{
return;
}
/* Loop over each collection of data */
foreach (var dataSource in chartSeriesSource)
{
DataPointSeries series;
/* Find out what type of series we want to use */
var seriesType = GetSeriesType(chart);
switch (seriesType)
{
case SeriesType.Line:
series = new LineSeries();
break;
case SeriesType.Bar:
series = new BarSeries();
break;
case SeriesType.Column:
series = new ColumnSeries();
break;
case SeriesType.Pie:
series = new PieSeries();
break;
case SeriesType.Scatter:
series = new ScatterSeries();
break;
case SeriesType.Area:
series = new AreaSeries();
break;
case SeriesType.Bubble:
series = new BubbleSeries();
break;
default:
throw new ArgumentOutOfRangeException();
}
/* Get and set the style of the newly created series */
var seriesStyle = GetSeriesStyle(chart);
series.Style = seriesStyle;
string titleBindingName = GetTitle(chart);
if (!string.IsNullOrEmpty(titleBindingName))
{
/* Do some binding of the Title property */
var titleBinding = new Binding(titleBindingName)
{
Source = series.Title,
Mode = BindingMode.TwoWay
};
series.SetBinding(Series.TitleProperty, titleBinding);
series.DataContext = dataSource;
}
/* Setup the bindings configured in the attached properties */
series.DependentValueBinding = new Binding(GetDependentValueBinding(chart));
series.IndependentValueBinding = new Binding(GetIndependentValueBinding(chart));
/*Set the ItemsSource property, which gives the data to the series to be rendered */
series.ItemsSource = dataSource as IEnumerable;
/* Add the series to the chart */
chart.Series.Add(series);
}
// NOTE: In order to change the Title, Maximum, and Minimum properties of the axes, you must handle the Axes_CollectionChanged event.
// However, you only want this event to fire once, so we always remove our handler here, and then re-add it to make sure we don't have
// an ever-increasing number of calls to Axes_CollectionChanged
((ISeriesHost)chart).Axes.CollectionChanged -= new NotifyCollectionChangedEventHandler(Axes_CollectionChanged);
((ISeriesHost)chart).Axes.CollectionChanged +=new NotifyCollectionChangedEventHandler(Axes_CollectionChanged);
}
///
/// Handles the CollectionChanged event of the Axes control. Here, X and Y axis titles and min/max properties are set once the graph creates or assigns the axes we need.
///
/// The source of the event.
/// The instance containing the event data.
static void Axes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs ccEventArgs)
{
if (ccEventArgs.Action != NotifyCollectionChangedAction.Remove)
{
Chart chart = null;
foreach (DisplayAxis axis in ccEventArgs.NewItems)
{
chart = (Chart)axis.SeriesHost;
if ((axis.Orientation == AxisOrientation.X && GetSeriesType(chart)!=SeriesType.Bar) || (axis.Orientation == AxisOrientation.Y && GetSeriesType(chart)==SeriesType.Bar))
{
axis.SetBinding(DisplayAxis.TitleProperty, new Binding(GetXAxisTitle(chart)));
if (axis is LinearAxis)
{
axis.SetBinding(LinearAxis.MinimumProperty, new Binding(GetXAxisMinimum(chart)));
axis.SetBinding(LinearAxis.MaximumProperty, new Binding(GetXAxisMaximum(chart)));
}
else if (axis is DateTimeAxis)
{
axis.SetBinding(DateTimeAxis.MinimumProperty, new Binding(GetXAxisMinimum(chart)));
axis.SetBinding(DateTimeAxis.MaximumProperty, new Binding(GetXAxisMaximum(chart)));
}
}
else
{
axis.SetBinding(DisplayAxis.TitleProperty, new Binding(GetYAxisTitle(chart)));
if (axis is LinearAxis)
{
axis.SetBinding(LinearAxis.MinimumProperty, new Binding(GetYAxisMinimum(chart)));
axis.SetBinding(LinearAxis.MaximumProperty, new Binding(GetYAxisMaximum(chart)));
}
else if (axis is DateTimeAxis)
{
axis.SetBinding(DateTimeAxis.MinimumProperty, new Binding(GetYAxisMinimum(chart)));
axis.SetBinding(DateTimeAxis.MaximumProperty, new Binding(GetYAxisMaximum(chart)));
}
}
}
}
}
}
}