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