// (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.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Windows.Controls.DataVisualization.Charting.Primitives;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
namespace System.Windows.Controls.DataVisualization.Charting
{
///
/// Implements a series that is defined by one or more instances of the DefinitionSeries class.
///
/// Preview
[ContentProperty("SeriesDefinitions")]
[TemplatePart(Name = SeriesAreaName, Type = typeof(Grid))]
[TemplatePart(Name = ItemContainerName, Type = typeof(DelegatingListBox))]
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Class is maintainable.")]
public abstract class DefinitionSeries : Control, ISeries, IAxisListener, IRangeProvider, IValueMarginProvider, IDataProvider, ISeriesHost
{
///
/// Name of the SeriesArea property.
///
private const string SeriesAreaName = "SeriesArea";
///
/// Name of the ItemContainer property.
///
private const string ItemContainerName = "ItemContainer";
///
/// Gets or sets a value indicating whether the series is 100% stacked (versus normally stacked).
///
protected bool IsStacked100 { get; set; }
///
/// Gets the collection of DataItems representing the data of the series.
///
protected ObservableCollection DataItems { get; private set; }
///
/// Gets the SeriesArea template part instance.
///
protected Panel SeriesArea { get; private set; }
///
/// Stores an aggregated collection of legend items from the series definitions.
///
private readonly AggregatedObservableCollection _legendItems = new AggregatedObservableCollection();
///
/// Stores the collection of SeriesDefinitions that define the series.
///
private readonly ObservableCollection _seriesDefinitions = new UniqueObservableCollection();
///
/// Stores a mirror collection of ISeries corresponding directly to the collection of SeriesDefinitions.
///
///
/// Not using ObservableCollectionListAdapter because of race condition on ItemsChanged event
///
private readonly ObservableCollection _seriesDefinitionsAsISeries = new ObservableCollection();
///
/// Keeps the SeriesDefinitions collection synchronized with the Children collection of the SeriesArea.
///
private readonly ObservableCollectionListAdapter _seriesAreaChildrenListAdapter = new ObservableCollectionListAdapter();
///
/// Stores the clip geometry for the ItemContainer.
///
private readonly RectangleGeometry _clipGeometry = new RectangleGeometry();
///
/// Stores a reference to the ItemContainer template part.
///
private DelegatingListBox _itemContainer;
///
/// Tracks the collection of DataItem that are queued for update.
///
private readonly List _queueUpdateDataItemPlacement_DataItems = new List();
///
/// Tracks whether the dependent axis values changed for the next update.
///
private bool _queueUpdateDataItemPlacement_DependentAxisValuesChanged;
///
/// Tracks whether the independent axis values changed for the next update.
///
private bool _queueUpdateDataItemPlacement_IndependentAxisValuesChanged;
///
/// Stores a reference to the backing collection for the SelectedItems property.
///
private ObservableCollection _selectedItems = new ObservableCollection();
///
/// Tracks whether the SelectedItems collection is being synchronized (to prevent reentrancy).
///
private bool _synchronizingSelectedItems;
#if !SILVERLIGHT
///
/// Performs one-time initialization of DefinitionSeries data.
///
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")]
static DefinitionSeries()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DefinitionSeries), new FrameworkPropertyMetadata(typeof(DefinitionSeries)));
}
#endif
///
/// Initializes a new instance of the DefinitionSeries class.
///
protected DefinitionSeries()
{
#if SILVERLIGHT
this.DefaultStyleKey = typeof(DefinitionSeries);
#endif
_seriesDefinitions.CollectionChanged += new NotifyCollectionChangedEventHandler(SeriesDefinitionsCollectionChanged);
_seriesAreaChildrenListAdapter.Collection = _seriesDefinitions;
_selectedItems.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedItemsCollectionChanged);
DataItems = new ObservableCollection();
}
///
/// Gets or sets the dependent axis of the series.
///
public IAxis DependentAxis
{
get { return (IAxis)GetValue(DependentAxisProperty); }
set { SetValue(DependentAxisProperty, value); }
}
///
/// Identifies the DependentAxis dependency property.
///
public static readonly DependencyProperty DependentAxisProperty =
DependencyProperty.Register("DependentAxis", typeof(IAxis), typeof(DefinitionSeries), new PropertyMetadata(OnDependentAxisChanged));
///
/// Handles changes to the DependentAxis dependency property.
///
/// DependencyObject that changed.
/// Event data for the DependencyPropertyChangedEvent.
private static void OnDependentAxisChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DefinitionSeries)o).OnDependentAxisChanged((IAxis)e.OldValue, (IAxis)e.NewValue);
}
///
/// Handles changes to the DependentAxis property.
///
/// Old value.
/// New value.
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")]
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")]
private void OnDependentAxisChanged(IAxis oldValue, IAxis newValue)
{
if (null != ActualDependentAxis)
{
EnsureAxes(true, false, false);
}
}
///
/// Gets or sets the independent axis of the series.
///
public IAxis IndependentAxis
{
get { return (IAxis)GetValue(IndependentAxisProperty); }
set { SetValue(IndependentAxisProperty, value); }
}
///
/// Identifies the IndependentAxis dependency property.
///
public static readonly DependencyProperty IndependentAxisProperty =
DependencyProperty.Register("IndependentAxis", typeof(IAxis), typeof(DefinitionSeries), new PropertyMetadata(OnIndependentAxisChanged));
///
/// Handles changes to the IndependentAxis dependency property.
///
/// DependencyObject that changed.
/// Event data for the DependencyPropertyChangedEvent.
private static void OnIndependentAxisChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DefinitionSeries)o).OnIndependentAxisChanged((IAxis)e.OldValue, (IAxis)e.NewValue);
}
///
/// Handles changes to the IndependentAxis property.
///
/// Old value.
/// New value.
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "oldValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")]
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newValue", Justification = "Parameter is part of the pattern for DependencyProperty change handlers.")]
private void OnIndependentAxisChanged(IAxis oldValue, IAxis newValue)
{
if (null != ActualIndependentAxis)
{
EnsureAxes(false, true, false);
}
}
///
/// Gets the rendered dependent axis of the series.
///
public IAxis ActualDependentAxis
{
get { return (IAxis)GetValue(ActualDependentAxisProperty); }
protected set { SetValue(ActualDependentAxisProperty, value); }
}
///
/// Identifies the ActualDependentAxis dependency property.
///
public static readonly DependencyProperty ActualDependentAxisProperty =
DependencyProperty.Register("ActualDependentAxis", typeof(IAxis), typeof(DefinitionSeries), null);
///
/// Gets the rendered independent axis of the series.
///
public IAxis ActualIndependentAxis
{
get { return (IAxis)GetValue(ActualIndependentAxisProperty); }
protected set { SetValue(ActualIndependentAxisProperty, value); }
}
///
/// Identifies the ActualIndependentAxis dependency property.
///
public static readonly DependencyProperty ActualIndependentAxisProperty =
DependencyProperty.Register("ActualIndependentAxis", typeof(IAxis), typeof(DefinitionSeries), null);
///
/// Gets the ActualDependentAxis as an IRangeAxis instance.
///
protected IRangeAxis ActualDependentRangeAxis
{
get { return (IRangeAxis)ActualDependentAxis; }
}
///
/// Gets the collection of legend items for the series.
///
public ObservableCollection LegendItems
{
get { return _legendItems; }
}
///
/// Gets or sets the SeriesHost for the series.
///
public ISeriesHost SeriesHost
{
get { return _seriesHost; }
set
{
if (null != _seriesHost)
{
_seriesHost.ResourceDictionariesChanged -= new EventHandler(SeriesHostResourceDictionariesChanged);
if (null != ActualDependentAxis)
{
ActualDependentAxis.RegisteredListeners.Remove(this);
ActualDependentAxis = null;
}
if (null != ActualIndependentAxis)
{
ActualIndependentAxis.RegisteredListeners.Remove(this);
ActualIndependentAxis = null;
}
foreach (SeriesDefinition definition in SeriesDefinitions)
{
SeriesDefinitionItemsSourceChanged(definition, definition.ItemsSource, null);
}
}
_seriesHost = value;
SeriesHostResourceDictionariesChanged(null, null);
if (null != _seriesHost)
{
_seriesHost.ResourceDictionariesChanged += new EventHandler(SeriesHostResourceDictionariesChanged);
foreach (SeriesDefinition definition in SeriesDefinitions)
{
SeriesDefinitionItemsSourceChanged(definition, null, definition.ItemsSource);
}
}
}
}
///
/// Stores the SeriesHost for the series.
///
private ISeriesHost _seriesHost;
///
/// Gets or sets the collection of SeriesDefinitions that define the series.
///
[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 SeriesDefinitions
{
get { return _seriesDefinitions; }
set { throw new NotSupportedException(Properties.Resources.DefinitionSeries_SeriesDefinitions_SetterNotSupported); }
}
///
/// Gets or sets the SelectionMode property.
///
public SeriesSelectionMode SelectionMode
{
get { return (SeriesSelectionMode)GetValue(SelectionModeProperty); }
set { SetValue(SelectionModeProperty, value); }
}
///
/// Identifies the SelectionMode dependency property.
///
public static readonly DependencyProperty SelectionModeProperty =
DependencyProperty.Register("SelectionMode", typeof(SeriesSelectionMode), typeof(DefinitionSeries), new PropertyMetadata(SeriesSelectionMode.None, OnSelectionModeChanged));
///
/// Handles changes to the SelectionMode dependency property.
///
/// DependencyObject that changed.
/// Event data for the DependencyPropertyChangedEvent.
private static void OnSelectionModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DefinitionSeries)o).OnSelectionModeChanged((SeriesSelectionMode)e.OldValue, (SeriesSelectionMode)e.NewValue);
}
///
/// Handles changes to the SelectionMode 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 OnSelectionModeChanged(SeriesSelectionMode oldValue, SeriesSelectionMode newValue)
{
if (null != _itemContainer)
{
switch (newValue)
{
case SeriesSelectionMode.None:
_itemContainer.SelectedItem = null;
_itemContainer.SelectionMode = Controls.SelectionMode.Single;
break;
case SeriesSelectionMode.Single:
_itemContainer.SelectionMode = Controls.SelectionMode.Single;
break;
case SeriesSelectionMode.Multiple:
_itemContainer.SelectionMode = Controls.SelectionMode.Multiple;
break;
}
}
}
///
/// Gets or sets the SelectedIndex property.
///
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
///
/// Identifies the SelectedIndex dependency property.
///
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(DefinitionSeries), new PropertyMetadata(-1));
///
/// Gets or sets the SelectedItem property.
///
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
///
/// Identifies the SelectedItem dependency property.
///
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(DefinitionSeries), null);
///
/// Gets the currently selected items.
///
///
/// This property is meant to be used when SelectionMode is Multiple. If the selection mode is Single the correct property to use is SelectedItem.
///
public IList SelectedItems
{
get { return _selectedItems; }
}
///
/// Handles the CollectionChanged event of the SelectedItems collection.
///
/// Event source.
/// Event arguments.
private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (!_synchronizingSelectedItems)
{
try
{
_synchronizingSelectedItems = true;
// Synchronize the SelectedItems collection
if (null != _itemContainer)
{
if (NotifyCollectionChangedAction.Reset == e.Action)
{
if (0 < _itemContainer.SelectedItems.Count)
{
_itemContainer.SelectedItems.Clear();
}
foreach (DataItem dataItem in _selectedItems.SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v))))
{
_itemContainer.SelectedItems.Add(dataItem);
}
}
else
{
if (null != e.OldItems)
{
foreach (DataItem dataItem in e.OldItems.CastWrapper().SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v))))
{
_itemContainer.SelectedItems.Remove(dataItem);
}
}
if (null != e.NewItems)
{
foreach (DataItem dataItem in e.NewItems.CastWrapper().SelectMany(v => DataItems.Where(di => object.Equals(di.Value, v))))
{
_itemContainer.SelectedItems.Add(dataItem);
}
}
}
}
}
finally
{
_synchronizingSelectedItems = false;
}
}
}
///
/// Handles the SelectionChanged event of the ItemContainer class.
///
/// Event source.
/// Event arguments.
private void ItemContainerSelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataItem[] removedDataItems = e.RemovedItems.CastWrapper().ToArray();
DataItem[] addedDataItems = e.AddedItems.CastWrapper().ToArray();
if (!_synchronizingSelectedItems)
{
try
{
_synchronizingSelectedItems = true;
// Synchronize the SelectedItems collection
foreach (object obj in removedDataItems.Select(di => di.Value))
{
_selectedItems.Remove(obj);
}
foreach (object obj in addedDataItems.Select(di => di.Value))
{
_selectedItems.Add(obj);
}
}
finally
{
_synchronizingSelectedItems = false;
}
}
// Pass the SelectionChanged event on to any listeners
IList removedItems = removedDataItems.Select(di => di.Value).ToArray();
IList addedItems = addedDataItems.Select(di => di.Value).ToArray();
#if SILVERLIGHT
SelectionChangedEventHandler handler = SelectionChanged;
if (null != handler)
{
handler(this, new SelectionChangedEventArgs(removedItems, addedItems));
}
#else
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, removedItems, addedItems));
#endif
}
///
/// Occurs when the selection of a DefinitionSeries changes.
///
#if SILVERLIGHT
public event SelectionChangedEventHandler SelectionChanged;
#else
public event SelectionChangedEventHandler SelectionChanged
{
add { AddHandler(SelectionChangedEvent, value); }
remove { RemoveHandler(SelectionChangedEvent, value); }
}
///
/// Identifies the SelectionChanged routed event.
///
public static readonly RoutedEvent SelectionChangedEvent =
EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(DefinitionSeries));
#endif
///
/// Builds the visual tree for the control when a new template is applied.
///
public override void OnApplyTemplate()
{
if (null != _itemContainer)
{
_itemContainer.PrepareContainerForItem = null;
_itemContainer.ClearContainerForItem = null;
_itemContainer.ItemsSource = null;
_itemContainer.Clip = null;
_itemContainer.SizeChanged -= new SizeChangedEventHandler(ItemContainerSizeChanged);
_itemContainer.SelectionChanged -= new SelectionChangedEventHandler(ItemContainerSelectionChanged);
_itemContainer.ClearValue(Selector.SelectedIndexProperty);
_itemContainer.ClearValue(Selector.SelectedItemProperty);
}
base.OnApplyTemplate();
SeriesArea = GetTemplateChild(SeriesAreaName) as Panel;
if (null != SeriesArea)
{
_seriesAreaChildrenListAdapter.TargetList = SeriesArea.Children;
_seriesAreaChildrenListAdapter.Populate();
}
_itemContainer = GetTemplateChild(ItemContainerName) as DelegatingListBox;
if (null != _itemContainer)
{
_itemContainer.PrepareContainerForItem = PrepareContainerForItem;
_itemContainer.ClearContainerForItem = ClearContainerForItem;
_itemContainer.ItemsSource = DataItems;
_itemContainer.Clip = _clipGeometry;
_itemContainer.SizeChanged += new SizeChangedEventHandler(ItemContainerSizeChanged);
_itemContainer.SelectionChanged += new SelectionChangedEventHandler(ItemContainerSelectionChanged);
_itemContainer.SetBinding(Selector.SelectedIndexProperty, new Binding("SelectedIndex") { Source = this, Mode = BindingMode.TwoWay });
_itemContainer.SetBinding(Selector.SelectedItemProperty, new Binding("SelectedItem") { Source = this, Mode = BindingMode.TwoWay, Converter = new SelectedItemToDataItemConverter(DataItems) });
}
// Synchronize selection state with new ItemContainer
OnSelectionModeChanged(SeriesSelectionMode.None, SelectionMode);
SelectedItemsCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
///
/// Prepares the specified element to display the specified item.
///
/// The element used to display the specified item.
/// The item to display.
private void PrepareContainerForItem(DependencyObject element, object item)
{
DataItem dataItem = (DataItem)item;
DataPoint dataPoint = CreateDataPoint();
dataItem.DataPoint = dataPoint;
dataPoint.DataContext = dataItem.Value;
dataPoint.SetBinding(DataPoint.DependentValueProperty, dataItem.SeriesDefinition.DependentValueBinding);
dataPoint.SetBinding(DataPoint.IndependentValueProperty, dataItem.SeriesDefinition.IndependentValueBinding);
dataPoint.SetBinding(DataPoint.StyleProperty, new Binding("ActualDataPointStyle") { Source = dataItem.SeriesDefinition });
dataPoint.DependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointDependentValueChanged);
dataPoint.ActualDependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointActualDependentValueChanged);
dataPoint.IndependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointIndependentValueChanged);
dataPoint.ActualIndependentValueChanged += new RoutedPropertyChangedEventHandler(DataPointActualIndependentValueChanged);
dataPoint.StateChanged += new RoutedPropertyChangedEventHandler(DataPointStateChanged);
dataPoint.DefinitionSeriesIsSelectionEnabledHandling = true;
ContentControl container = (ContentControl)element;
dataItem.Container = container;
Binding selectionEnabledBinding = new Binding("SelectionMode") { Source = this, Converter = new SelectionModeToSelectionEnabledConverter() };
container.SetBinding(ContentControl.IsTabStopProperty, selectionEnabledBinding);
dataPoint.SetBinding(DataPoint.IsSelectionEnabledProperty, selectionEnabledBinding);
dataPoint.SetBinding(DataPoint.IsSelectedProperty, new Binding("IsSelected") { Source = container, Mode = BindingMode.TwoWay });
dataPoint.Visibility = Visibility.Collapsed;
dataPoint.State = DataPointState.Showing;
PrepareDataPoint(dataPoint);
container.Content = dataPoint;
}
///
/// Undoes the effects of the PrepareContainerForItemOverride method.
///
/// The container element.
/// The item to display.
private void ClearContainerForItem(DependencyObject element, object item)
{
DataItem dataItem = (DataItem)item;
DataPoint dataPoint = dataItem.DataPoint;
dataPoint.DependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointDependentValueChanged);
dataPoint.ActualDependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointActualDependentValueChanged);
dataPoint.IndependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointIndependentValueChanged);
dataPoint.ActualIndependentValueChanged -= new RoutedPropertyChangedEventHandler(DataPointActualIndependentValueChanged);
dataPoint.StateChanged -= new RoutedPropertyChangedEventHandler(DataPointStateChanged);
dataPoint.ClearValue(DataPoint.DependentValueProperty);
dataPoint.ClearValue(DataPoint.IndependentValueProperty);
dataPoint.ClearValue(DataPoint.StyleProperty);
dataPoint.ClearValue(DataPoint.IsSelectionEnabledProperty);
dataPoint.ClearValue(DataPoint.IsSelectedProperty);
ContentControl container = (ContentControl)dataItem.Container;
container.ClearValue(ContentControl.IsTabStopProperty);
dataPoint.DataContext = null;
}
///
/// Prepares a DataPoint for use.
///
/// DataPoint instance.
protected virtual void PrepareDataPoint(DataPoint dataPoint) { }
///
/// Creates a DataPoint for the series.
///
/// Series-appropriate DataPoint instance.
protected abstract DataPoint CreateDataPoint();
///
/// Provides an internally-accessible wrapper for calling CreateDataPoint.
///
/// Series-appropriate DataPoint instance.
internal DataPoint InternalCreateDataPoint()
{
return CreateDataPoint();
}
///
/// Handles the SizeChanged event of the ItemContainer.
///
/// Event source.
/// Event arguments.
private void ItemContainerSizeChanged(object sender, SizeChangedEventArgs e)
{
_clipGeometry.Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height);
QueueUpdateDataItemPlacement(false, false, DataItems);
}
///
/// Returns the DataItem corresponding to the specified DataPoint.
///
/// Specified DataPoint.
/// Corresponding DataItem.
protected DataItem DataItemFromDataPoint(DataPoint dataPoint)
{
return DataItems.Where(di => di.DataPoint == dataPoint).Single();
}
///
/// Handles the DependentValueChanged event of a DataPoint.
///
/// Event source.
/// Event arguments.
private void DataPointDependentValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
DataPoint dataPoint = (DataPoint)sender;
SeriesDefinition definition = DataItemFromDataPoint(dataPoint).SeriesDefinition;
TimeSpan transitionDuration = definition.TransitionDuration;
if (0 < transitionDuration.TotalMilliseconds)
{
dataPoint.BeginAnimation(DataPoint.ActualDependentValueProperty, "ActualDependentValue", e.NewValue, definition.TransitionDuration, definition.TransitionEasingFunction);
}
else
{
dataPoint.ActualDependentValue = e.NewValue;
}
}
///
/// Handles the ActualDependentValueChanged event of a DataPoint.
///
/// Event source.
/// Event arguments.
private void DataPointActualDependentValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
DataPoint dataPoint = (DataPoint)sender;
QueueUpdateDataItemPlacement(true, false, DataItems.Where(di => di.DataPoint == dataPoint));
}
///
/// Handles the IndependentValueChanged event of a DataPoint.
///
/// Event source.
/// Event arguments.
private void DataPointIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
DataPoint dataPoint = (DataPoint)sender;
SeriesDefinition definition = DataItemFromDataPoint(dataPoint).SeriesDefinition;
TimeSpan transitionDuration = definition.TransitionDuration;
if (0 < transitionDuration.TotalMilliseconds)
{
dataPoint.BeginAnimation(DataPoint.ActualIndependentValueProperty, "ActualIndependentValue", e.NewValue, definition.TransitionDuration, definition.TransitionEasingFunction);
}
else
{
dataPoint.ActualIndependentValue = e.NewValue;
}
}
///
/// Handles the ActualIndependentValueChanged event of a DataPoint.
///
/// Event source.
/// Event arguments.
private void DataPointActualIndependentValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
DataPoint dataPoint = (DataPoint)sender;
QueueUpdateDataItemPlacement(false, true, DataItems.Where(di => di.DataPoint == dataPoint));
}
///
/// Handles the StateChanged event of a DataPoint.
///
/// Event source.
/// Event arguments.
private void DataPointStateChanged(object sender, RoutedPropertyChangedEventArgs e)
{
DataPoint dataPoint = (DataPoint)sender;
if (DataPointState.Hidden == dataPoint.State)
{
DataItems.Remove(DataItems.Where(di => di.DataPoint == dataPoint).Single());
RemovedDataItems();
}
}
///
/// Notifies the specified axis of changes to values plotting against it.
///
/// Specified axis.
protected void NotifyAxisValuesChanged(IAxis axis)
{
if (null != axis)
{
IRangeConsumer rangeConsumer = axis as IRangeConsumer;
if (null != rangeConsumer)
{
IRangeProvider rangeProvider = (IRangeProvider)this;
rangeConsumer.RangeChanged(rangeProvider, new Range() /*rangeProvider.GetRange(rangeConsumer)*/);
}
IDataConsumer dataConsumer = axis as IDataConsumer;
if (null != dataConsumer)
{
IDataProvider dataProvider = (IDataProvider)this;
dataConsumer.DataChanged(dataProvider, null /*dataProvider.GetData(dataConsumer)*/);
}
}
}
///
/// Notifies the specified axis of changes to value margins plotting against it.
///
/// Specified axis.
/// Sequence of value margins that have changed.
protected void NotifyValueMarginsChanged(IAxis axis, IEnumerable valueMargins)
{
if (null != axis)
{
IValueMarginConsumer valueMarginConsumer = axis as IValueMarginConsumer;
if (null != valueMarginConsumer)
{
IValueMarginProvider valueMarginProvider = (IValueMarginProvider)this;
valueMarginConsumer.ValueMarginsChanged(valueMarginProvider, valueMargins);
}
}
}
///
/// Handles the CollectionChanged event of the SeriesDefinitions collection.
///
/// Event source.
/// Event arguments.
private void SeriesDefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SeriesDefinitionsCollectionChanged(e.Action, e.OldItems, e.OldStartingIndex, e.NewItems, e.NewStartingIndex);
}
///
/// Handles the CollectionChanged event of the SeriesDefinitions collection.
///
/// Type of change.
/// Sequence of old items.
/// Starting index of old items.
/// Sequence of new items.
/// Starting index of new items.
protected virtual void SeriesDefinitionsCollectionChanged(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex)
{
if (null != oldItems)
{
foreach (SeriesDefinition oldDefinition in oldItems.CastWrapper())
{
ISeries oldSeries = (ISeries)oldDefinition;
SeriesDefinitionItemsSourceChanged(oldDefinition, oldDefinition.ItemsSource, null);
_seriesDefinitionsAsISeries.Remove(oldDefinition);
_legendItems.ChildCollections.Remove(oldSeries.LegendItems);
UpdatePaletteProperties(oldDefinition);
oldSeries.SeriesHost = null;
oldDefinition.Index = -1;
}
}
if (null != newItems)
{
int index = newStartingIndex;
foreach (SeriesDefinition newDefinition in newItems.CastWrapper())
{
ISeries newSeries = (ISeries)newDefinition;
newSeries.SeriesHost = this;
UpdatePaletteProperties(newDefinition);
_legendItems.ChildCollections.Add(newSeries.LegendItems);
_seriesDefinitionsAsISeries.Add(newDefinition);
newDefinition.Index = index;
SeriesDefinitionItemsSourceChanged(newDefinition, null, newDefinition.ItemsSource);
index++;
}
}
}
///
/// Updates the palette properties of the specified SeriesDefinition.
///
/// Specified SeriesDefinition.
private void UpdatePaletteProperties(SeriesDefinition definition)
{
ResourceDictionary resources = null;
if (null != SeriesHost)
{
Type dataPointType = CreateDataPoint().GetType();
using (IEnumerator enumerator = SeriesHost.GetResourceDictionariesWhere(dictionary =>
{
Style style = dictionary["DataPointStyle"] as Style;
if (null != style)
{
return (null != style.TargetType) && (style.TargetType.IsAssignableFrom(dataPointType));
}
return false;
}))
{
if (enumerator.MoveNext())
{
resources = enumerator.Current;
}
}
}
definition.PaletteDataPointStyle = (null != resources) ? resources["DataPointStyle"] as Style : null;
definition.PaletteDataShapeStyle = (null != resources) ? resources["DataShapeStyle"] as Style : null;
definition.PaletteLegendItemStyle = (null != resources) ? resources["LegendItemStyle"] as Style : null;
}
///
/// Handles changes to the ItemsSource of a SeriesDefinition.
///
/// SeriesDefinition owner.
/// Old value.
/// New value.
internal void SeriesDefinitionItemsSourceChanged(SeriesDefinition definition, IEnumerable oldValue, IEnumerable newValue)
{
if (null != oldValue)
{
foreach (DataItem dataItem in DataItems.Where(di => di.SeriesDefinition == definition).ToArray())
{
DataItems.Remove(dataItem);
}
RemovedDataItems();
}
if (null != newValue)
{
// No need to add items if SeriesHost null; setting SeriesHost will take care of that
if (null != SeriesHost)
{
AddDataItems(definition, newValue.CastWrapper(), 0);
}
}
}
///
/// Handles changes to the ItemsSource collection of a SeriesDefinition.
///
/// SeriesDefinition owner.
/// Type of change.
/// Sequence of old items.
/// Starting index of old items.
/// Sequence of new items.
/// Starting index of new items.
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Linq is artificially increasing the rating.")]
internal void SeriesDefinitionItemsSourceCollectionChanged(SeriesDefinition definition, NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex, IList newItems, int newStartingIndex)
{
if (NotifyCollectionChangedAction.Replace == action)
{
// Perform in-place replacements
foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (newStartingIndex <= di.Index) && (di.Index < newStartingIndex + newItems.Count)))
{
dataItem.Value = newItems[dataItem.Index - newStartingIndex];
}
}
else
{
if (NotifyCollectionChangedAction.Reset == action)
{
// Set up parameters to allow normal old/new item handling to be used
Debug.Assert(null == oldItems, "Reset action with non-null oldItems.");
oldItems = DataItems.Where(di => (di.SeriesDefinition == definition)).ToArray();
oldStartingIndex = 0;
newItems = definition.ItemsSource.CastWrapper().ToArray();
newStartingIndex = 0;
}
if (null != oldItems)
{
// Get rid of old items
foreach (DataItem oldDataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (oldStartingIndex <= di.Index) && (di.Index < oldStartingIndex + oldItems.Count)))
{
oldDataItem.Index = -1;
if (null != oldDataItem.DataPoint)
{
oldDataItem.DataPoint.State = DataPointState.Hiding;
}
}
// Adjust index of shifted items
foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (oldStartingIndex + oldItems.Count <= di.Index)))
{
dataItem.Index -= oldItems.Count;
}
}
if (null != newItems)
{
// Adjust index of shifted items
foreach (DataItem dataItem in DataItems.Where(di => (di.SeriesDefinition == definition) && (newStartingIndex <= di.Index)))
{
dataItem.Index += newItems.Count;
}
// Add new items
AddDataItems(definition, newItems.CastWrapper(), newStartingIndex);
}
}
#if DEBUG
// Validate all DataItem index and value properties
foreach (var group in DataItems.Where(di => 0 <= di.Index).OrderBy(di => di.Index).GroupBy(di => di.SeriesDefinition))
{
object[] items = group.Key.ItemsSource.CastWrapper().ToArray();
int i = 0;
foreach (DataItem dataItem in group)
{
Debug.Assert(i == dataItem.Index, "DataItem index mis-match.");
Debug.Assert(dataItem.Value.Equals(items[i]), "DataItem value mis-match.");
i++;
}
}
#endif
}
///
/// Handles the ResourceDictionariesChanged event of the SeriesHost owner.
///
/// Event source.
/// Event arguments.
private void SeriesHostResourceDictionariesChanged(object sender, EventArgs e)
{
foreach (SeriesDefinition definition in SeriesDefinitions)
{
UpdatePaletteProperties(definition);
}
}
///
/// Creates and adds DataItems for the specified SeriesDefinition's items.
///
/// Specified SeriesDefinition.
/// Sequence of items.
/// Starting index.
private void AddDataItems(SeriesDefinition definition, IEnumerable items, int startingIndex)
{
int index = startingIndex;
foreach (object item in items)
{
DataItems.Add(new DataItem(definition) { Value = item, Index = index });
index++;
}
// Because properties (like DependentValueBinding) may still be getting set
Dispatcher.BeginInvoke((Action)AddedDataItems);
}
///
/// Updates the axes after DataItems have been added.
///
private void AddedDataItems()
{
EnsureAxes(false, false, true);
}
///
/// Notifies the axes after DataItems have been removed.
///
private void RemovedDataItems()
{
NotifyAxisValuesChanged(ActualIndependentAxis);
NotifyAxisValuesChanged(ActualDependentAxis);
}
///
/// Ensures that suitable axes are present and registered.
///
/// True if the dependent axis needs to be updated.
/// True if the independent axis needs to be updated.
/// True if both axis are to be notified unconditionally.
private void EnsureAxes(bool updateDependentAxis, bool updateIndependentAxis, bool unconditionallyNotifyAxes)
{
foreach (SeriesDefinition definition in SeriesDefinitions)
{
if (null == definition.DependentValueBinding)
{
throw new InvalidOperationException(Properties.Resources.DefinitionSeries_EnsureAxes_MissingDependentValueBinding);
}
if (null == definition.IndependentValueBinding)
{
throw new InvalidOperationException(Properties.Resources.DefinitionSeries_EnsureAxes_MissingIndependentValueBinding);
}
}
if ((null != SeriesHost) && DataItems.Any())
{
// Ensure a dependent axis is present or updated
bool changedActualDependentAxis = false;
if (updateDependentAxis && (null != ActualDependentAxis))
{
ActualDependentAxis.RegisteredListeners.Remove(this);
ActualDependentAxis = null;
}
if (null == ActualDependentAxis)
{
ActualDependentAxis = DependentAxis ?? AcquireDependentAxis();
ActualDependentAxis.RegisteredListeners.Add(this);
if (!SeriesHost.Axes.Contains(ActualDependentAxis))
{
SeriesHost.Axes.Add(ActualDependentAxis);
}
changedActualDependentAxis = true;
}
// Ensure an independent axis is present or updated
bool changedActualIndependentAxis = false;
if (updateIndependentAxis && (null != ActualIndependentAxis))
{
ActualIndependentAxis.RegisteredListeners.Remove(this);
ActualIndependentAxis = null;
}
if (null == ActualIndependentAxis)
{
ActualIndependentAxis = IndependentAxis ?? AcquireIndependentAxis();
ActualIndependentAxis.RegisteredListeners.Add(this);
if (!SeriesHost.Axes.Contains(ActualIndependentAxis))
{
SeriesHost.Axes.Add(ActualIndependentAxis);
}
changedActualIndependentAxis = true;
}
// Queue an update if necessary or requested
if (changedActualDependentAxis || changedActualIndependentAxis || unconditionallyNotifyAxes)
{
QueueUpdateDataItemPlacement(changedActualDependentAxis || unconditionallyNotifyAxes, changedActualIndependentAxis || unconditionallyNotifyAxes, DataItems);
}
}
}
///
/// Acquires a dependent axis suitable for use with the data values of the series.
///
/// Axis instance.
protected abstract IAxis AcquireDependentAxis();
///
/// Acquires an independent axis suitable for use with the data values of the series.
///
/// Axis instance.
protected abstract IAxis AcquireIndependentAxis();
///
/// Handles notification of the invalidation of an axis.
///
/// Invalidated axis.
void IAxisListener.AxisInvalidated(IAxis axis)
{
QueueUpdateDataItemPlacement(false, false, DataItems);
}
///
/// Queues an update of DataItem placement for the next update opportunity.
///
/// True if the dependent axis values have changed.
/// True if the independent axis values have changed.
/// Sequence of DataItems to update.
private void QueueUpdateDataItemPlacement(bool dependentAxisValuesChanged, bool independentAxisValuesChanged, IEnumerable dataItems)
{
_queueUpdateDataItemPlacement_DependentAxisValuesChanged |= dependentAxisValuesChanged;
_queueUpdateDataItemPlacement_IndependentAxisValuesChanged |= independentAxisValuesChanged;
_queueUpdateDataItemPlacement_DataItems.AddRange(dataItems);
InvalidateArrange();
}
///
/// Called when the control needs to arrange its children.
///
/// Bounds to arrange within.
/// Arranged size.
///
/// Used as a good place to dequeue queued work.
///
protected override Size ArrangeOverride(Size arrangeBounds)
{
Size arrangedSize = base.ArrangeOverride(arrangeBounds);
if (_queueUpdateDataItemPlacement_DependentAxisValuesChanged)
{
NotifyAxisValuesChanged(ActualDependentAxis);
_queueUpdateDataItemPlacement_DependentAxisValuesChanged = false;
}
if (_queueUpdateDataItemPlacement_IndependentAxisValuesChanged)
{
NotifyAxisValuesChanged(ActualIndependentAxis);
_queueUpdateDataItemPlacement_IndependentAxisValuesChanged = false;
}
UpdateDataItemPlacement(_queueUpdateDataItemPlacement_DataItems.Distinct());
_queueUpdateDataItemPlacement_DataItems.Clear();
return arrangedSize;
}
///
/// Updates the placement of the DataItems (data points) of the series.
///
/// DataItems in need of an update.
protected abstract void UpdateDataItemPlacement(IEnumerable dataItems);
///
/// Returns the range for the data points of the series.
///
/// Consumer of the range.
/// Range of values.
Range IRangeProvider.GetRange(IRangeConsumer rangeConsumer)
{
return IRangeProviderGetRange(rangeConsumer);
}
///
/// Returns the range for the data points of the series.
///
/// Consumer of the range.
/// Range of values.
protected virtual Range IRangeProviderGetRange(IRangeConsumer rangeConsumer)
{
if (rangeConsumer == ActualIndependentAxis)
{
if (ActualIndependentAxis.CanPlot(0.0))
{
return IndependentValueGroups
.Select(g => ValueHelper.ToDouble(g.IndependentValue))
.Where(d => ValueHelper.CanGraph(d))
.DefaultIfEmpty()
.CastWrapper()
.GetRange();
}
else
{
return IndependentValueGroups
.Select(g => ValueHelper.ToDateTime(g.IndependentValue))
.DefaultIfEmpty()
.CastWrapper()
.GetRange();
}
}
throw new NotSupportedException();
}
///
/// Returns the value margins for the data points of the series.
///
/// Consumer of the value margins.
/// Sequence of value margins.
IEnumerable IValueMarginProvider.GetValueMargins(IValueMarginConsumer valueMarginConsumer)
{
return IValueMarginProviderGetValueMargins(valueMarginConsumer);
}
///
/// Returns the value margins for the data points of the series.
///
/// Consumer of the value margins.
/// Sequence of value margins.
protected virtual IEnumerable IValueMarginProviderGetValueMargins(IValueMarginConsumer valueMarginConsumer)
{
throw new NotImplementedException();
}
///
/// Returns the data for the data points of the series.
///
/// Consumer of the data.
/// Sequence of data.
IEnumerable IDataProvider.GetData(IDataConsumer dataConsumer)
{
return IDataProviderGetData(dataConsumer);
}
///
/// Returns the data for the data points of the series.
///
/// Consumer of the data.
/// Sequence of data.
protected virtual IEnumerable IDataProviderGetData(IDataConsumer dataConsumer)
{
if (dataConsumer == ActualIndependentAxis)
{
return IndependentValueGroups.Select(cg => cg.IndependentValue).Distinct();
}
throw new NotImplementedException();
}
///
/// Gets a sequence of IndependentValueGroups.
///
protected virtual IEnumerable IndependentValueGroups
{
get
{
return DataItems
.GroupBy(di => di.ActualIndependentValue)
.Select(g => new IndependentValueGroup(g.Key, g.OrderBy(di => di.SeriesDefinition.Index)));
}
}
///
/// Gets a sequence of IndependentValueGroups ordered by independent value.
///
protected IEnumerable IndependentValueGroupsOrderedByIndependentValue
{
get
{
return IndependentValueGroups
.OrderBy(g => g.IndependentValue);
}
}
///
/// Gets a sequence of sequences of the dependent values associated with each independent value.
///
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nesting reflects the actual hierarchy of the data.")]
protected IEnumerable> IndependentValueDependentValues
{
get
{
return IndependentValueGroups
.Select(g =>
{
g.Denominator = IsStacked100 ?
g.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.ActualDependentValue))) :
1;
if (0 == g.Denominator)
{
g.Denominator = 1;
}
return g;
})
.Select(g => g.DataItems
.Select(di => ValueHelper.ToDouble(di.ActualDependentValue) * (IsStacked100 ? (100 / g.Denominator) : 1)));
}
}
///
/// Represents an independent value and the dependent values that are associated with it.
///
protected class IndependentValueGroup
{
///
/// Initializes a new instance of the IndependentValueGroup class.
///
/// Independent value.
/// Associated DataItems.
public IndependentValueGroup(object independentValue, IEnumerable dataItems)
{
IndependentValue = independentValue;
DataItems = dataItems;
}
///
/// Gets the independent value.
///
public object IndependentValue { get; private set; }
///
/// Gets a sequence of DataItems associated with the independent value.
///
public IEnumerable DataItems { get; private set; }
///
/// Gets or sets the denominator to use when computing with this instance.
///
///
/// Exists here purely to simplify the the corresponding algorithm.
///
public double Denominator { get; set; }
}
///
/// Represents a single data value from a SeriesDefinition's ItemsSource.
///
protected class DataItem
{
///
/// Stores a reference to a shared BindingHelper instance.
///
private static readonly BindingHelper _bindingHelper = new BindingHelper();
///
/// Initializes a new instance of the DataItem class.
///
/// SeriesDefinition owner.
public DataItem(SeriesDefinition seriesDefinition)
{
SeriesDefinition = seriesDefinition;
CenterPoint = new Point(double.NaN, double.NaN);
}
///
/// Gets the SeriesDefinition owner of the DataItem.
///
public SeriesDefinition SeriesDefinition { get; private set; }
///
/// Gets or sets the value of the DataItem.
///
public object Value
{
get { return _value; }
set
{
_value = value;
if (null != DataPoint)
{
DataPoint.DataContext = value;
}
}
}
///
/// Stores the value of the DataItem.
///
private object _value;
///
/// Gets or sets the index of the DataItem.
///
public int Index { get; set; }
///
/// Gets or sets the DataPoint associated with the DataItem.
///
public DataPoint DataPoint { get; set; }
///
/// Gets or sets the container for the DataPoint within its parent ItemsControl.
///
public UIElement Container { get; set; }
///
/// Gets the ActualDependentValue of the DataPoint (or its equivalent).
///
public IComparable ActualDependentValue
{
get
{
if (null != DataPoint)
{
return DataPoint.ActualDependentValue;
}
else
{
return (IComparable)_bindingHelper.EvaluateBinding(SeriesDefinition.DependentValueBinding, Value);
}
}
}
///
/// Gets the ActualIndependentValue of the DataPoint (or its equivalent).
///
public object ActualIndependentValue
{
get
{
if (null != DataPoint)
{
return DataPoint.ActualIndependentValue;
}
else
{
return _bindingHelper.EvaluateBinding(SeriesDefinition.IndependentValueBinding, Value);
}
}
}
///
/// Gets or sets the ActualDependentValue of the DataPoint after adjusting for applicable stacking.
///
public double ActualStackedDependentValue { get; set; }
///
/// Gets or sets the center-point of the DataPoint in plot area coordinates (if relevant).
///
public Point CenterPoint { get; set; }
}
///
/// Provides an easy way to evaluate a Binding against a source instance.
///
private class BindingHelper : FrameworkElement
{
///
/// Initializes a new instance of the BindingHelper class.
///
public BindingHelper()
{
}
///
/// Identifies the Result dependency property.
///
private static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(object), typeof(BindingHelper), null);
///
/// Evaluates a Binding against a source instance.
///
/// Binding to evaluate.
/// Source instance.
/// Result of Binding on source instance.
public object EvaluateBinding(Binding binding, object instance)
{
DataContext = instance;
SetBinding(ResultProperty, binding);
object result = GetValue(ResultProperty);
ClearValue(ResultProperty);
DataContext = null;
return result;
}
}
///
/// Converts from a selected item to the corresponding DataItem.
///
private class SelectedItemToDataItemConverter : IValueConverter
{
///
/// Stores a reference to the DataItem collection.
///
private ObservableCollection _dataItems;
///
/// Initializes a new instance of the SelectedItemToDataItemConverter class.
///
/// Collection of DataItems.
public SelectedItemToDataItemConverter(ObservableCollection dataItems)
{
_dataItems = dataItems;
}
///
/// Converts a value.
///
/// The value produced by the binding source.
/// The type of the binding target property.
/// The converter parameter to use.
/// The culture to use in the converter.
/// Converted value.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return _dataItems.Where(di => di.Value == value).FirstOrDefault();
}
///
/// Converts a value back.
///
/// The value produced by the binding source.
/// The type of the binding target property.
/// The converter parameter to use.
/// The culture to use in the converter.
/// Converted value.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
DataItem dataItem = value as DataItem;
return (null != dataItem) ? dataItem.Value : null;
}
}
///
/// Converts from a SeriesSelectionMode to a true/false value indicating whether selection is enabled.
///
private class SelectionModeToSelectionEnabledConverter : IValueConverter
{
///
/// Initializes a new instance of the SelectionModeToSelectionEnabledConverter class.
///
public SelectionModeToSelectionEnabledConverter()
{
}
///
/// Converts a value.
///
/// The value produced by the binding source.
/// The type of the binding target property.
/// The converter parameter to use.
/// The culture to use in the converter.
/// Converted value.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isSelectionEnabled = false;
if (value is SeriesSelectionMode)
{
isSelectionEnabled = !(SeriesSelectionMode.None == (SeriesSelectionMode)value);
}
return isSelectionEnabled;
}
///
/// Converts a value back.
///
/// The value produced by the binding source.
/// The type of the binding target property.
/// The converter parameter to use.
/// The culture to use in the converter.
/// Converted value.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
///
/// Gets the axes for the series as a series host.
///
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")]
ObservableCollection ISeriesHost.Axes
{
get { throw new NotImplementedException(); }
}
///
/// Gets the series for the series as a series host.
///
ObservableCollection ISeriesHost.Series
{
get { return _seriesDefinitionsAsISeries; }
}
///
/// Gets the foreground elements for the series as a series host.
///
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")]
ObservableCollection ISeriesHost.ForegroundElements
{
get { throw new NotImplementedException(); }
}
///
/// Gets the background elements for the series as a series host.
///
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")]
ObservableCollection ISeriesHost.BackgroundElements
{
get { throw new NotImplementedException(); }
}
///
/// Gets a IResourceDictionaryDispenser for the series as a series host.
///
/// Predicate function.
/// Sequence of ResourceDictionaries.
IEnumerator IResourceDictionaryDispenser.GetResourceDictionariesWhere(Func predicate)
{
throw new NotImplementedException();
}
///
/// Event that is triggered when the available ResourceDictionaries change.
///
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Property exists as an interface requirement; implementation is unnecessary.")]
event EventHandler IResourceDictionaryDispenser.ResourceDictionariesChanged
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
}
}