// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) 2014 OxyPlot contributors // // // Represents a control that displays a . // // -------------------------------------------------------------------------------------------------------------------- using Avalonia; namespace OxyPlot.Avalonia { using global::Avalonia.Controls; using global::Avalonia.LogicalTree; using global::Avalonia.VisualTree; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; /// /// Represents a control that displays a . /// public partial class Plot : PlotBase, IPlot { /// /// The internal model. /// private readonly PlotModel internalModel; /// /// The default controller. /// private readonly IPlotController defaultController; /// /// Initializes a new instance of the class. /// public Plot() { series = new ObservableCollection(); axes = new ObservableCollection(); annotations = new ObservableCollection(); legends = new ObservableCollection(); series.CollectionChanged += OnSeriesChanged; axes.CollectionChanged += OnAxesChanged; annotations.CollectionChanged += OnAnnotationsChanged; legends.CollectionChanged += this.OnAnnotationsChanged; defaultController = new PlotController(); internalModel = new PlotModel(); ((IPlotModel)internalModel).AttachPlotView(this); } /// /// Gets the annotations. /// /// The annotations. public ObservableCollection Annotations { get { return annotations; } } /// /// Gets the actual model. /// /// The actual model. public override PlotModel ActualModel { get { return internalModel; } } /// /// Gets the actual Plot controller. /// /// The actual Plot controller. public override IPlotController ActualController { get { return defaultController; } } /// /// Updates the model. If Model==null, an internal model will be created. The ActualModel.Update will be called (updates all series data). /// /// if set to true , all data collections will be updated. protected new void UpdateModel(bool updateData = true) { SynchronizeProperties(); SynchronizeSeries(); SynchronizeAxes(); SynchronizeAnnotations(); SynchronizeLegends(); // TODO: does this achieve anything? we always call InvalidatePlot after UpdateModel base.UpdateModel(updateData); } /// /// Called when the visual appearance is changed. /// protected void OnAppearanceChanged() { UpdateModel(false); InvalidatePlot(false); } void IPlot.ElementAppearanceChanged(object element) { // TODO: determine type of element to perform a more fine-grained update this.UpdateModel(false); base.InvalidatePlot(false); } void IPlot.ElementDataChanged(object element) { // TODO: determine type of element to perform a more fine-grained update this.UpdateModel(true); base.InvalidatePlot(true); } /// /// Called when the visual appearance is changed. /// /// The d. /// The instance containing the event data. private static void AppearanceChanged(global::Avalonia.AvaloniaObject d, global::Avalonia.AvaloniaPropertyChangedEventArgs e) { ((Plot)d).OnAppearanceChanged(); } /// /// Called when annotations is changed. /// /// The sender. /// The instance containing the event data. private void OnAnnotationsChanged(object sender, NotifyCollectionChangedEventArgs e) { SyncLogicalTree(e); } /// /// Called when axes is changed. /// /// The sender. /// The instance containing the event data. private void OnAxesChanged(object sender, NotifyCollectionChangedEventArgs e) { SyncLogicalTree(e); } /// /// Called when series is changed. /// /// The sender. /// The instance containing the event data. private void OnSeriesChanged(object sender, NotifyCollectionChangedEventArgs e) { SyncLogicalTree(e); } /// /// Synchronizes the logical tree. /// /// The instance containing the event data. private void SyncLogicalTree(NotifyCollectionChangedEventArgs e) { // In order to get DataContext and binding to work with the series, axes and annotations // we add the items to the logical tree if (e.NewItems != null) { foreach (var item in e.NewItems.OfType()) { item.SetParent(this); } LogicalChildren.AddRange(e.NewItems.OfType()); VisualChildren.AddRange(e.NewItems.OfType()); } if (e.OldItems != null) { foreach (var item in e.OldItems.OfType()) { item.SetParent(null); } foreach (var item in e.OldItems) { LogicalChildren.Remove((ILogical)item); VisualChildren.Remove((Visual)item); } } this.OnAppearanceChanged(); } /// /// Synchronize properties in the internal Plot model /// private void SynchronizeProperties() { var m = internalModel; m.PlotType = PlotType; m.PlotMargins = PlotMargins.ToOxyThickness(); m.Padding = Padding.ToOxyThickness(); m.TitlePadding = TitlePadding; m.Culture = Culture; m.DefaultColors = DefaultColors.Select(c => c.ToOxyColor()).ToArray(); m.DefaultFont = DefaultFont; m.DefaultFontSize = DefaultFontSize; m.Title = Title; m.TitleColor = TitleColor.ToOxyColor(); m.TitleFont = TitleFont; m.TitleFontSize = TitleFontSize; m.TitleFontWeight = (int)TitleFontWeight; m.TitleToolTip = TitleToolTip; m.Subtitle = Subtitle; m.SubtitleColor = SubtitleColor.ToOxyColor(); m.SubtitleFont = SubtitleFont; m.SubtitleFontSize = SubtitleFontSize; m.SubtitleFontWeight = (int)SubtitleFontWeight; m.TextColor = TextColor.ToOxyColor(); m.SelectionColor = SelectionColor.ToOxyColor(); m.RenderingDecorator = RenderingDecorator; m.AxisTierDistance = AxisTierDistance; m.IsLegendVisible = IsLegendVisible; m.PlotAreaBackground = PlotAreaBackground.ToOxyColor(); m.PlotAreaBorderColor = PlotAreaBorderColor.ToOxyColor(); m.PlotAreaBorderThickness = PlotAreaBorderThickness.ToOxyThickness(); } /// /// Synchronizes the annotations in the internal model. /// private void SynchronizeAnnotations() { internalModel.Annotations.Clear(); foreach (var a in Annotations) { internalModel.Annotations.Add(a.CreateModel()); } } /// /// Synchronizes the axes in the internal model. /// private void SynchronizeAxes() { internalModel.Axes.Clear(); foreach (var a in Axes) { internalModel.Axes.Add(a.CreateModel()); } } /// /// Synchronizes the series in the internal model. /// private void SynchronizeSeries() { internalModel.Series.Clear(); foreach (var s in Series) { internalModel.Series.Add(s.CreateModel()); } } /// /// Synchronizes the legends in the internal model. /// private void SynchronizeLegends() { internalModel.Legends.Clear(); foreach (var l in Legends) { internalModel.Legends.Add(l.CreateModel()); } } } }