Series.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="Series.cs" company="OxyPlot">
  3. // Copyright (c) 2014 OxyPlot contributors
  4. // </copyright>
  5. // <summary>
  6. // Abstract base class for series.
  7. // </summary>
  8. // --------------------------------------------------------------------------------------------------------------------
  9. using Avalonia;
  10. using Avalonia.Utilities;
  11. namespace OxyPlot.Avalonia
  12. {
  13. using global::Avalonia.Controls;
  14. using global::Avalonia.Media;
  15. using global::Avalonia.Utilities;
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Specialized;
  19. /// <summary>
  20. /// Abstract base class for series.
  21. /// </summary>
  22. public abstract class Series : ItemsControl
  23. {
  24. /// <summary>
  25. /// Identifies the <see cref="Color"/> dependency property.
  26. /// </summary>
  27. public static readonly StyledProperty<Color> ColorProperty = AvaloniaProperty.Register<Series, Color>(nameof(Color), MoreColors.Automatic);
  28. /// <summary>
  29. /// Identifies the <see cref="Title"/> dependency property.
  30. /// </summary>
  31. public static readonly StyledProperty<string> TitleProperty = AvaloniaProperty.Register<Series, string>(nameof(Title), null);
  32. /// <summary>
  33. /// Identifies the <see cref="RenderInLegend"/> dependency property.
  34. /// </summary>
  35. public static readonly StyledProperty<bool> RenderInLegendProperty = AvaloniaProperty.Register<Series, bool>(nameof(RenderInLegend), true);
  36. /// <summary>
  37. /// Identifies the <see cref="TrackerFormatString"/> dependency property.
  38. /// </summary>
  39. public static readonly StyledProperty<string> TrackerFormatStringProperty = AvaloniaProperty.Register<Series, string>(nameof(TrackerFormatString), null);
  40. /// <summary>
  41. /// Identifies the <see cref="TrackerKey"/> dependency property.
  42. /// </summary>
  43. public static readonly StyledProperty<string> TrackerKeyProperty = AvaloniaProperty.Register<Series, string>(nameof(TrackerKey), null);
  44. /// <summary>
  45. /// Identifies the <see cref="EdgeRenderingMode"/> dependency property.
  46. /// </summary>
  47. public static readonly StyledProperty<EdgeRenderingMode> EdgeRenderingModeProperty = AvaloniaProperty.Register<Series, EdgeRenderingMode>(nameof(EdgeRenderingMode), EdgeRenderingMode.Automatic);
  48. /// <summary>
  49. /// The event listener used to subscribe to ItemSource.CollectionChanged events
  50. /// </summary>
  51. private readonly EventListener eventListener;
  52. /// <summary>
  53. /// Initializes static members of the <see cref="Series" /> class.
  54. /// </summary>
  55. static Series()
  56. {
  57. IsVisibleProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  58. BackgroundProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  59. ColorProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  60. TitleProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  61. RenderInLegendProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  62. TrackerFormatStringProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  63. TrackerKeyProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  64. EdgeRenderingModeProperty.Changed.AddClassHandler<Series>(AppearanceChanged);
  65. }
  66. /// <summary>
  67. /// Initializes a new instance of the <see cref="Series" /> class.
  68. /// </summary>
  69. protected Series()
  70. {
  71. eventListener = new EventListener(OnCollectionChanged);
  72. // Set Items to null for consistency with WPF behaviour in Oxyplot-Contrib
  73. // Works around issue with BarSeriesManager throwing on empty Items collection in OxyPlot.Core 2.1
  74. ItemsSource = null;
  75. ItemsView.CollectionChanged += ItemsViewOnCollectionChanged;
  76. }
  77. /// <summary>
  78. /// Gets or sets Color.
  79. /// </summary>
  80. public Color Color
  81. {
  82. get
  83. {
  84. return GetValue(ColorProperty);
  85. }
  86. set
  87. {
  88. SetValue(ColorProperty, value);
  89. }
  90. }
  91. /// <summary>
  92. /// Gets or sets the internal series.
  93. /// </summary>
  94. public OxyPlot.Series.Series InternalSeries { get; protected set; }
  95. /// <summary>
  96. /// Gets or sets Title.
  97. /// </summary>
  98. public string Title
  99. {
  100. get
  101. {
  102. return GetValue(TitleProperty);
  103. }
  104. set
  105. {
  106. SetValue(TitleProperty, value);
  107. }
  108. }
  109. /// <summary>
  110. /// Gets or sets a value indicating whether the series should be rendered in the legend.
  111. /// </summary>
  112. public bool RenderInLegend
  113. {
  114. get
  115. {
  116. return GetValue(RenderInLegendProperty);
  117. }
  118. set
  119. {
  120. SetValue(RenderInLegendProperty, value);
  121. }
  122. }
  123. /// <summary>
  124. /// Gets or sets TrackerFormatString.
  125. /// </summary>
  126. public string TrackerFormatString
  127. {
  128. get
  129. {
  130. return GetValue(TrackerFormatStringProperty);
  131. }
  132. set
  133. {
  134. SetValue(TrackerFormatStringProperty, value);
  135. }
  136. }
  137. /// <summary>
  138. /// Gets or sets TrackerKey.
  139. /// </summary>
  140. public string TrackerKey
  141. {
  142. get
  143. {
  144. return GetValue(TrackerKeyProperty);
  145. }
  146. set
  147. {
  148. SetValue(TrackerKeyProperty, value);
  149. }
  150. }
  151. /// <summary>
  152. /// Gets or sets the <see cref="OxyPlot.EdgeRenderingMode"/> for the series.
  153. /// </summary>
  154. public EdgeRenderingMode EdgeRenderingMode
  155. {
  156. get
  157. {
  158. return GetValue(EdgeRenderingModeProperty);
  159. }
  160. set
  161. {
  162. SetValue(EdgeRenderingModeProperty, value);
  163. }
  164. }
  165. /// <summary>
  166. /// Creates the model.
  167. /// </summary>
  168. /// <returns>A series.</returns>
  169. public abstract OxyPlot.Series.Series CreateModel();
  170. /// <summary>
  171. /// The appearance changed.
  172. /// </summary>
  173. /// <param name="d">The d.</param>
  174. /// <param name="e">The e.</param>
  175. protected static void AppearanceChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
  176. {
  177. ((Series)d).OnVisualChanged();
  178. }
  179. /// <summary>
  180. /// The on visual changed handler.
  181. /// </summary>
  182. protected void OnVisualChanged()
  183. {
  184. (this.Parent as IPlot)?.ElementAppearanceChanged(this);
  185. }
  186. /// <summary>
  187. /// The data changed.
  188. /// </summary>
  189. /// <param name="d">The d.</param>
  190. /// <param name="e">The e.</param>
  191. protected static void DataChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
  192. {
  193. ((Series)d).OnDataChanged();
  194. }
  195. /// <summary>
  196. /// The on data changed handler.
  197. /// </summary>
  198. protected void OnDataChanged()
  199. {
  200. (this.Parent as IPlot)?.ElementDataChanged(this);
  201. }
  202. private void ItemsViewOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  203. {
  204. SubscribeToCollectionChanged(e.OldItems, e.NewItems);
  205. OnDataChanged();
  206. }
  207. protected override void OnAttachedToLogicalTree(global::Avalonia.LogicalTree.LogicalTreeAttachmentEventArgs e)
  208. {
  209. base.OnAttachedToLogicalTree(e);
  210. //BeginInit();
  211. //EndInit();
  212. }
  213. /// <summary>
  214. /// Synchronizes the properties.
  215. /// </summary>
  216. /// <param name="s">The series.</param>
  217. protected virtual void SynchronizeProperties(OxyPlot.Series.Series s)
  218. {
  219. s.Background = Background.ToOxyColor();
  220. s.Title = Title;
  221. s.RenderInLegend = RenderInLegend;
  222. s.TrackerFormatString = TrackerFormatString;
  223. s.TrackerKey = TrackerKey;
  224. s.TrackerFormatString = TrackerFormatString;
  225. s.IsVisible = IsVisible;
  226. s.Font = FontFamily.ToString();
  227. s.TextColor = Foreground.ToOxyColor();
  228. s.EdgeRenderingMode = EdgeRenderingMode;
  229. }
  230. /// <summary>
  231. /// If the ItemsSource implements INotifyCollectionChanged update the visual when the collection changes.
  232. /// </summary>
  233. /// <param name="oldValue">The old ItemsSource</param>
  234. /// <param name="newValue">The new ItemsSource</param>
  235. private void SubscribeToCollectionChanged(IEnumerable oldValue, IEnumerable newValue)
  236. {
  237. if (oldValue is INotifyCollectionChanged collection)
  238. {
  239. WeakEvents.CollectionChanged.Unsubscribe(collection, eventListener);
  240. }
  241. collection = newValue as INotifyCollectionChanged;
  242. if (collection != null)
  243. {
  244. WeakEvents.CollectionChanged.Subscribe(collection, eventListener);
  245. }
  246. }
  247. /// <summary>
  248. /// Invalidate the view when the collection changes
  249. /// </summary>
  250. /// <param name="sender">The sender</param>
  251. /// <param name="notifyCollectionChangedEventArgs">The collection changed args</param>
  252. private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
  253. {
  254. OnDataChanged();
  255. }
  256. /// <summary>
  257. /// Listens to and forwards any collection changed events
  258. /// </summary>
  259. private class EventListener : IWeakEventSubscriber<NotifyCollectionChangedEventArgs>
  260. {
  261. /// <summary>
  262. /// The delegate to forward to
  263. /// </summary>
  264. private readonly EventHandler<NotifyCollectionChangedEventArgs> onCollectionChanged;
  265. /// <summary>
  266. /// Initializes a new instance of the <see cref="EventListener" /> class
  267. /// </summary>
  268. /// <param name="onCollectionChanged">The handler</param>
  269. public EventListener(EventHandler<NotifyCollectionChangedEventArgs> onCollectionChanged)
  270. {
  271. this.onCollectionChanged = onCollectionChanged;
  272. }
  273. public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs e)
  274. {
  275. onCollectionChanged(sender, e);
  276. }
  277. }
  278. }
  279. }