// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) 2014 OxyPlot contributors // // // Abstract base class for series. // // -------------------------------------------------------------------------------------------------------------------- using Avalonia; using Avalonia.Utilities; namespace OxyPlot.Avalonia { using global::Avalonia.Controls; using global::Avalonia.Media; using global::Avalonia.Utilities; using System; using System.Collections; using System.Collections.Specialized; /// /// Abstract base class for series. /// public abstract class Series : ItemsControl { /// /// Identifies the dependency property. /// public static readonly StyledProperty ColorProperty = AvaloniaProperty.Register(nameof(Color), MoreColors.Automatic); /// /// Identifies the dependency property. /// public static readonly StyledProperty TitleProperty = AvaloniaProperty.Register(nameof(Title), null); /// /// Identifies the dependency property. /// public static readonly StyledProperty RenderInLegendProperty = AvaloniaProperty.Register(nameof(RenderInLegend), true); /// /// Identifies the dependency property. /// public static readonly StyledProperty TrackerFormatStringProperty = AvaloniaProperty.Register(nameof(TrackerFormatString), null); /// /// Identifies the dependency property. /// public static readonly StyledProperty TrackerKeyProperty = AvaloniaProperty.Register(nameof(TrackerKey), null); /// /// Identifies the dependency property. /// public static readonly StyledProperty EdgeRenderingModeProperty = AvaloniaProperty.Register(nameof(EdgeRenderingMode), EdgeRenderingMode.Automatic); /// /// The event listener used to subscribe to ItemSource.CollectionChanged events /// private readonly EventListener eventListener; /// /// Initializes static members of the class. /// static Series() { IsVisibleProperty.Changed.AddClassHandler(AppearanceChanged); BackgroundProperty.Changed.AddClassHandler(AppearanceChanged); ColorProperty.Changed.AddClassHandler(AppearanceChanged); TitleProperty.Changed.AddClassHandler(AppearanceChanged); RenderInLegendProperty.Changed.AddClassHandler(AppearanceChanged); TrackerFormatStringProperty.Changed.AddClassHandler(AppearanceChanged); TrackerKeyProperty.Changed.AddClassHandler(AppearanceChanged); EdgeRenderingModeProperty.Changed.AddClassHandler(AppearanceChanged); } /// /// Initializes a new instance of the class. /// protected Series() { eventListener = new EventListener(OnCollectionChanged); // Set Items to null for consistency with WPF behaviour in Oxyplot-Contrib // Works around issue with BarSeriesManager throwing on empty Items collection in OxyPlot.Core 2.1 ItemsSource = null; ItemsView.CollectionChanged += ItemsViewOnCollectionChanged; } /// /// Gets or sets Color. /// public Color Color { get { return GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } } /// /// Gets or sets the internal series. /// public OxyPlot.Series.Series InternalSeries { get; protected set; } /// /// Gets or sets Title. /// public string Title { get { return GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// Gets or sets a value indicating whether the series should be rendered in the legend. /// public bool RenderInLegend { get { return GetValue(RenderInLegendProperty); } set { SetValue(RenderInLegendProperty, value); } } /// /// Gets or sets TrackerFormatString. /// public string TrackerFormatString { get { return GetValue(TrackerFormatStringProperty); } set { SetValue(TrackerFormatStringProperty, value); } } /// /// Gets or sets TrackerKey. /// public string TrackerKey { get { return GetValue(TrackerKeyProperty); } set { SetValue(TrackerKeyProperty, value); } } /// /// Gets or sets the for the series. /// public EdgeRenderingMode EdgeRenderingMode { get { return GetValue(EdgeRenderingModeProperty); } set { SetValue(EdgeRenderingModeProperty, value); } } /// /// Creates the model. /// /// A series. public abstract OxyPlot.Series.Series CreateModel(); /// /// The appearance changed. /// /// The d. /// The e. protected static void AppearanceChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) { ((Series)d).OnVisualChanged(); } /// /// The on visual changed handler. /// protected void OnVisualChanged() { (this.Parent as IPlot)?.ElementAppearanceChanged(this); } /// /// The data changed. /// /// The d. /// The e. protected static void DataChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) { ((Series)d).OnDataChanged(); } /// /// The on data changed handler. /// protected void OnDataChanged() { (this.Parent as IPlot)?.ElementDataChanged(this); } private void ItemsViewOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { SubscribeToCollectionChanged(e.OldItems, e.NewItems); OnDataChanged(); } protected override void OnAttachedToLogicalTree(global::Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); //BeginInit(); //EndInit(); } /// /// Synchronizes the properties. /// /// The series. protected virtual void SynchronizeProperties(OxyPlot.Series.Series s) { s.Background = Background.ToOxyColor(); s.Title = Title; s.RenderInLegend = RenderInLegend; s.TrackerFormatString = TrackerFormatString; s.TrackerKey = TrackerKey; s.TrackerFormatString = TrackerFormatString; s.IsVisible = IsVisible; s.Font = FontFamily.ToString(); s.TextColor = Foreground.ToOxyColor(); s.EdgeRenderingMode = EdgeRenderingMode; } /// /// If the ItemsSource implements INotifyCollectionChanged update the visual when the collection changes. /// /// The old ItemsSource /// The new ItemsSource private void SubscribeToCollectionChanged(IEnumerable oldValue, IEnumerable newValue) { if (oldValue is INotifyCollectionChanged collection) { WeakEvents.CollectionChanged.Unsubscribe(collection, eventListener); } collection = newValue as INotifyCollectionChanged; if (collection != null) { WeakEvents.CollectionChanged.Subscribe(collection, eventListener); } } /// /// Invalidate the view when the collection changes /// /// The sender /// The collection changed args private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) { OnDataChanged(); } /// /// Listens to and forwards any collection changed events /// private class EventListener : IWeakEventSubscriber { /// /// The delegate to forward to /// private readonly EventHandler onCollectionChanged; /// /// Initializes a new instance of the class /// /// The handler public EventListener(EventHandler onCollectionChanged) { this.onCollectionChanged = onCollectionChanged; } public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs e) { onCollectionChanged(sender, e); } } } }