// (c) Copyright Microsoft Corporation. // This source 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 { /// /// Defines the attributes of a series that is to be rendered by the DefinitionSeries class. /// /// Preview [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(DataPoint))] [StyleTypedProperty(Property = LegendItemStyleName, StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = DataShapeStyleName, StyleTargetType = typeof(Shape))] public class SeriesDefinition : FrameworkElement, ISeries, IRequireGlobalSeriesIndex { /// /// Name of the DataPointStyle property. /// private const string DataPointStyleName = "DataPointStyle"; /// /// Name of the LegendItemStyle property. /// private const string LegendItemStyleName = "LegendItemStyle"; /// /// Name of the DataShapeStyle property. /// private const string DataShapeStyleName = "DataShapeStyle"; /// /// Provides the store for the ISeries.LegendItems property. /// private readonly ObservableCollection _legendItems = new ObservableCollection(); /// /// Represents the single LegendItem corresponding to the SeriesDefinition. /// private readonly LegendItem _legendItem; /// /// Keeps a reference to the WeakEventListener used to prevent leaks of collections assigned to the ItemsSource property. /// private WeakEventListener _weakEventListener; /// /// Gets or sets the index of the series definition. /// internal int Index { get; set; } /// /// Initializes a new instance of the SeriesDefinition class. /// 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); } /// /// Gets or sets a sequence that provides the content of the series. /// public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } /// /// Identifies the ItemsSource dependency property. /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SeriesDefinition), new PropertyMetadata(OnItemsSourceChanged)); /// /// Handles changes to the ItemsSource dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue); } /// /// Handles changes to the ItemsSource property. /// /// Old value. /// New value. 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(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); } } /// /// Handles the CollectionChanged event for the ItemsSource property. /// /// Event source. /// Event arguments.. private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (null != ParentDefinitionSeries) { ParentDefinitionSeries.SeriesDefinitionItemsSourceCollectionChanged(this, e.Action, e.OldItems, e.OldStartingIndex, e.NewItems, e.NewStartingIndex); } } /// /// Gets or sets the automatic title of the series definition. /// private object AutomaticTitle { get { return _automaticTitle; } set { _automaticTitle = value; ActualTitle = Title ?? _automaticTitle; } } /// /// Stores the automatic title of the series definition. /// private object _automaticTitle; /// /// Gets or sets the Title of the series definition. /// public object Title { get { return (object)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Identifies the Title dependency property. /// public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(SeriesDefinition), new PropertyMetadata(OnTitleChanged)); /// /// Handles changes to the Title dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnTitleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnTitleChanged((object)e.OldValue, (object)e.NewValue); } /// /// Handles changes to the Title property. /// /// Old value. /// New value. [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; } /// /// Gets the rendered Title of the series definition. /// public object ActualTitle { get { return (object)GetValue(ActualTitleProperty); } protected set { SetValue(ActualTitleProperty, value); } } /// /// Identifies the ActualTitle dependency property. /// public static readonly DependencyProperty ActualTitleProperty = DependencyProperty.Register("ActualTitle", typeof(object), typeof(SeriesDefinition), null); /// /// Gets or sets the DataPoint Style from the SeriesHost's Palette. /// internal Style PaletteDataPointStyle { get { return _paletteDataPointStyle; } set { _paletteDataPointStyle = value; ActualDataPointStyle = DataPointStyle ?? _paletteDataPointStyle; } } /// /// Stores the DataPoint Style from the SeriesHost's Palette. /// private Style _paletteDataPointStyle; /// /// Gets or sets the DataPoint Style for the series definition. /// public Style DataPointStyle { get { return (Style)GetValue(DataPointStyleProperty); } set { SetValue(DataPointStyleProperty, value); } } /// /// Identifies the DataPointStyle dependency property. /// public static readonly DependencyProperty DataPointStyleProperty = DependencyProperty.Register(DataPointStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataPointStyleChanged)); /// /// Handles changes to the DataPointStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnDataPointStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnDataPointStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the DataPointStyle property. /// /// Old value. /// New value. [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; } /// /// Gets the rendered DataPoint Style for the series definition. /// public Style ActualDataPointStyle { get { return (Style)GetValue(ActualDataPointStyleProperty); } protected set { SetValue(ActualDataPointStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// public static readonly DependencyProperty ActualDataPointStyleProperty = DependencyProperty.Register("ActualDataPointStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the LegendItem Style from the SeriesHost's Palette. /// internal Style PaletteLegendItemStyle { get { return _paletteLegendItemStyle; } set { _paletteLegendItemStyle = value; ActualLegendItemStyle = LegendItemStyle ?? _paletteLegendItemStyle; } } /// /// Stores the LegendItem Style from the SeriesHost's Palette. /// private Style _paletteLegendItemStyle; /// /// Gets or sets the LegendItem Style for the series definition. /// public Style LegendItemStyle { get { return (Style)GetValue(LegendItemStyleProperty); } set { SetValue(LegendItemStyleProperty, value); } } /// /// Identifies the LegendItemStyle dependency property. /// public static readonly DependencyProperty LegendItemStyleProperty = DependencyProperty.Register(LegendItemStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnLegendItemStyleChanged)); /// /// Handles changes to the LegendItemStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnLegendItemStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnLegendItemStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the LegendItemStyle property. /// /// Old value. /// New value. [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; } /// /// Gets the rendered LegendItem Style for the series definition. /// public Style ActualLegendItemStyle { get { return (Style)GetValue(ActualLegendItemStyleProperty); } protected set { SetValue(ActualLegendItemStyleProperty, value); } } /// /// Identifies the ActualDataPointStyle dependency property. /// public static readonly DependencyProperty ActualLegendItemStyleProperty = DependencyProperty.Register("ActualLegendItemStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the DataShape Style from the SeriesHost's Palette. /// internal Style PaletteDataShapeStyle { get { return _paletteDataShapeStyle; } set { _paletteDataShapeStyle = value; ActualDataShapeStyle = DataShapeStyle ?? _paletteDataShapeStyle; } } /// /// Stores the DataShape Style from the SeriesHost's Palette. /// private Style _paletteDataShapeStyle; /// /// Gets or sets the DataShape Style for the series definition. /// public Style DataShapeStyle { get { return (Style)GetValue(DataShapeStyleProperty); } set { SetValue(DataShapeStyleProperty, value); } } /// /// Identifies the DataShapeStyle dependency property. /// public static readonly DependencyProperty DataShapeStyleProperty = DependencyProperty.Register(DataShapeStyleName, typeof(Style), typeof(SeriesDefinition), new PropertyMetadata(OnDataShapeStyleChanged)); /// /// Handles changes to the DataShapeStyle dependency property. /// /// DependencyObject that changed. /// Event data for the DependencyPropertyChangedEvent. private static void OnDataShapeStyleChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((SeriesDefinition)o).OnDataShapeStyleChanged((Style)e.OldValue, (Style)e.NewValue); } /// /// Handles changes to the DataShapeStyle property. /// /// Old value. /// New value. [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; } /// /// Gets the rendered DataShape Style for the series definition. /// public Style ActualDataShapeStyle { get { return (Style)GetValue(ActualDataShapeStyleProperty); } protected set { SetValue(ActualDataShapeStyleProperty, value); } } /// /// Identifies the ActualDataShapeStyle dependency property. /// public static readonly DependencyProperty ActualDataShapeStyleProperty = DependencyProperty.Register("ActualDataShapeStyle", typeof(Style), typeof(SeriesDefinition), null); /// /// Gets or sets the Binding to use for identifying the dependent value. /// public Binding DependentValueBinding { get { return _dependentValueBinding; } set { if (value != _dependentValueBinding) { _dependentValueBinding = value; Reset(); } } } /// /// The binding used to identify the dependent value binding. /// private Binding _dependentValueBinding; /// /// Gets or sets the Binding Path to use for identifying the dependent value. /// public string DependentValuePath { get { return (null != DependentValueBinding) ? DependentValueBinding.Path.Path : null; } set { if (null == value) { DependentValueBinding = null; } else { DependentValueBinding = new Binding(value); } } } /// /// Gets or sets the Binding to use for identifying the independent value. /// public Binding IndependentValueBinding { get { return _independentValueBinding; } set { if (_independentValueBinding != value) { _independentValueBinding = value; Reset(); } } } /// /// The binding used to identify the independent value binding. /// private Binding _independentValueBinding; /// /// Gets or sets the Binding Path to use for identifying the independent value. /// public string IndependentValuePath { get { return (null != IndependentValueBinding) ? IndependentValueBinding.Path.Path : null; } set { if (null == value) { IndependentValueBinding = null; } else { IndependentValueBinding = new Binding(value); } } } /// /// Resets the display of the series definition. /// private void Reset() { if (null != ParentDefinitionSeries) { ParentDefinitionSeries.SeriesDefinitionItemsSourceChanged(this, ItemsSource, ItemsSource); } } /// /// Gets the SeriesHost as a DefinitionSeries instance. /// private DefinitionSeries ParentDefinitionSeries { get { return (DefinitionSeries)((ISeries)this).SeriesHost; } } /// /// Gets the collection of legend items for the series definition. /// ObservableCollection ISeries.LegendItems { get { return _legendItems; } } /// /// Gets or sets the SeriesHost for the series definition. /// 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; } } } /// /// Stores the SeriesHost for the series definition. /// private ISeriesHost _seriesHost; /// /// Handles changes to the global series index of the series definition. /// /// New index. void IRequireGlobalSeriesIndex.GlobalSeriesIndexChanged(int? globalIndex) { if (globalIndex.HasValue) { AutomaticTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Series_OnGlobalSeriesIndexPropertyChanged_UntitledSeriesFormatString, globalIndex + 1); } } /// /// Gets or sets the TimeSpan to use for the duration of data transitions. /// public TimeSpan TransitionDuration { get { return (TimeSpan)GetValue(TransitionDurationProperty); } set { SetValue(TransitionDurationProperty, value); } } /// /// Identifies the TransitionDuration dependency property. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register("TransitionDuration", typeof(TimeSpan), typeof(SeriesDefinition), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); #if !NO_EASING_FUNCTIONS /// /// Gets or sets the IEasingFunction to use for data transitions. /// public IEasingFunction TransitionEasingFunction { get { return (IEasingFunction)GetValue(TransitionEasingFunctionProperty); } set { SetValue(TransitionEasingFunctionProperty, value); } } /// /// Identifies the TransitionEasingFunction dependency property. /// public static readonly DependencyProperty TransitionEasingFunctionProperty = DependencyProperty.Register("TransitionEasingFunction", typeof(IEasingFunction), typeof(SeriesDefinition), new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseInOut })); #else /// /// Gets or sets a placeholder for the TransitionEasingFunction dependency property. /// internal IEasingFunction TransitionEasingFunction { get; set; } #endif } }