PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/ChartView/WPF/TrackBallLikeAnnotations/ChartUtilities.cs

https://gitlab.com/webartoli/repro-lowquality-exporttoimage-radtreeview
C# | 327 lines | 273 code | 54 blank | 0 comment | 40 complexity | b98902905bf9c1ac5a16bb68734fe509 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using Telerik.Windows.Controls;
  7. using Telerik.Charting;
  8. using Telerik.Windows.Controls.ChartView;
  9. using System.Windows.Controls;
  10. using System.Windows.Media;
  11. namespace TrackBallLikeAnnotations
  12. {
  13. public class ChartUtilities
  14. {
  15. private static HashSet<RadCartesianChart> attachedCharts = new HashSet<RadCartesianChart>();
  16. private static RadCartesianChart lastMouseOveredChart;
  17. public static bool HidesAnnotationsOnMouseLeave { get; set; }
  18. public static string GetAnnotationsGroup(DependencyObject obj)
  19. {
  20. return (string)obj.GetValue(AnnotationsGroupProperty);
  21. }
  22. public static void SetAnnotationsGroup(DependencyObject obj, string value)
  23. {
  24. obj.SetValue(AnnotationsGroupProperty, value);
  25. }
  26. public static DataTemplate GetIntersectionPointTemplate(DependencyObject obj)
  27. {
  28. return (DataTemplate)obj.GetValue(IntersectionPointTemplateProperty);
  29. }
  30. public static void SetIntersectionPointTemplate(DependencyObject obj, DataTemplate value)
  31. {
  32. obj.SetValue(IntersectionPointTemplateProperty, value);
  33. }
  34. public static DataTemplate GetDataPointTemplate(DependencyObject obj)
  35. {
  36. return (DataTemplate)obj.GetValue(DataPointTemplateProperty);
  37. }
  38. public static void SetDataPointTemplate(DependencyObject obj, DataTemplate value)
  39. {
  40. obj.SetValue(DataPointTemplateProperty, value);
  41. }
  42. private static object GetCurrentXCategory(DependencyObject obj)
  43. {
  44. return (object)obj.GetValue(CurrentXCategoryProperty);
  45. }
  46. private static void SetCurrentXCategory(DependencyObject obj, object value)
  47. {
  48. obj.SetValue(CurrentXCategoryProperty, value);
  49. }
  50. public static readonly DependencyProperty AnnotationsGroupProperty = DependencyProperty.RegisterAttached("AnnotationsGroup", typeof(string), typeof(ChartUtilities), new PropertyMetadata(null, AnnotationsGroupChanged));
  51. public static readonly DependencyProperty IntersectionPointTemplateProperty = DependencyProperty.RegisterAttached("IntersectionPointTemplate", typeof(DataTemplate), typeof(ChartUtilities), new PropertyMetadata(null));
  52. public static readonly DependencyProperty DataPointTemplateProperty = DependencyProperty.RegisterAttached("DataPointTemplate", typeof(DataTemplate), typeof(ChartUtilities), new PropertyMetadata(null));
  53. private static readonly DependencyProperty CurrentXCategoryProperty = DependencyProperty.RegisterAttached("CurrentXCategory", typeof(object), typeof(ChartUtilities), new PropertyMetadata(null));
  54. private static void AnnotationsGroupChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
  55. {
  56. RadCartesianChart chart = target as RadCartesianChart;
  57. if (chart == null)
  58. {
  59. return;
  60. }
  61. string group = (string)args.NewValue;
  62. if (group != null && !attachedCharts.Contains(chart))
  63. {
  64. attachedCharts.Add(chart);
  65. chart.MouseMove += Chart_MouseMove;
  66. chart.MouseLeave += Chart_MouseLeave;
  67. }
  68. else if (group == null)
  69. {
  70. chart.MouseMove -= Chart_MouseMove;
  71. attachedCharts.Remove(chart);
  72. chart.MouseLeave -= Chart_MouseLeave;
  73. if (lastMouseOveredChart == chart)
  74. {
  75. lastMouseOveredChart = null;
  76. }
  77. }
  78. }
  79. private static void Chart_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
  80. {
  81. RadCartesianChart chart = (RadCartesianChart)sender;
  82. if (HidesAnnotationsOnMouseLeave)
  83. {
  84. string group = GetAnnotationsGroup(chart);
  85. HideAnnotations(group);
  86. }
  87. }
  88. private static void Chart_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
  89. {
  90. RadCartesianChart chart = (RadCartesianChart)sender;
  91. lastMouseOveredChart = chart;
  92. string group = GetAnnotationsGroup(chart);
  93. object currentXCategory = GetCurrentXCategory(chart);
  94. Point position = e.GetPosition(chart);
  95. if (!chart.PlotAreaClip.Contains(position.X, position.Y))
  96. {
  97. if (HidesAnnotationsOnMouseLeave && currentXCategory != null)
  98. {
  99. HideAnnotations(group);
  100. }
  101. return;
  102. }
  103. DataTuple tuple = chart.ConvertPointToData(position);
  104. object xCategory = tuple.FirstValue;
  105. if (object.Equals(xCategory, currentXCategory))
  106. {
  107. return;
  108. }
  109. UpdateCharts(group, xCategory);
  110. }
  111. private static void HideAnnotations(string group)
  112. {
  113. foreach (var chart in attachedCharts.Where(chart => group == GetAnnotationsGroup(chart)))
  114. {
  115. RemoveAnnotations(chart);
  116. SetCurrentXCategory(chart, null);
  117. }
  118. }
  119. public static void UpdateCharts(string group, object xCategory)
  120. {
  121. var charts = attachedCharts.Where(chart => object.Equals(group, GetAnnotationsGroup(chart)));
  122. foreach (var chart in charts)
  123. {
  124. SetCurrentXCategory(chart, xCategory);
  125. RemoveAnnotations(chart);
  126. AddAnnotations(chart, xCategory);
  127. }
  128. }
  129. private static void RemoveAnnotations(RadCartesianChart chart)
  130. {
  131. var toBeRemoved = chart.Annotations.Where(ann => object.Equals(ann.Tag, typeof(ChartUtilities))).ToList();
  132. foreach (var ann in toBeRemoved)
  133. {
  134. chart.Annotations.Remove(ann);
  135. }
  136. }
  137. private static void AddAnnotations(RadCartesianChart chart, object xCategory)
  138. {
  139. chart.Annotations.Add(new CartesianGridLineAnnotation { Axis = chart.HorizontalAxis, Value = xCategory, Tag = typeof(ChartUtilities) });
  140. StackPanel dataPointsInfoPanel = new StackPanel();
  141. foreach (CartesianSeries series in chart.Series)
  142. {
  143. CategoricalSeries categoricalSeries = series as CategoricalSeries;
  144. if (categoricalSeries == null)
  145. {
  146. continue;
  147. }
  148. var dataPoint = GetDataPoint(categoricalSeries.DataPoints, xCategory);
  149. if (dataPoint == null)
  150. {
  151. continue;
  152. }
  153. AddIntersectionPoint(chart, GetIntersectionPointTemplate(series), dataPoint);
  154. ContentPresenter cp = new ContentPresenter();
  155. cp.ContentTemplate = GetDataPointTemplate(series);
  156. cp.Content = dataPoint;
  157. dataPointsInfoPanel.Children.Add(cp);
  158. }
  159. foreach (IndicatorBase indicator in chart.Indicators)
  160. {
  161. foreach (var dataPoint in GetDataPoints(indicator, xCategory))
  162. {
  163. AddIntersectionPoint(chart, GetIntersectionPointTemplate(indicator), dataPoint);
  164. }
  165. }
  166. LinearAxis vAxis = (LinearAxis)chart.VerticalAxis;
  167. CartesianCustomAnnotation infoAnnotation = new CartesianCustomAnnotation();
  168. infoAnnotation.Tag = typeof(ChartUtilities);
  169. infoAnnotation.HorizontalValue = xCategory;
  170. infoAnnotation.VerticalValue = vAxis.ActualRange.Maximum;
  171. infoAnnotation.Content = PrepareInfoContent(dataPointsInfoPanel);
  172. chart.Annotations.Add(infoAnnotation);
  173. }
  174. private static object PrepareInfoContent(StackPanel dataPointsInfoPanel)
  175. {
  176. Border border = new Border();
  177. border.Child = dataPointsInfoPanel;
  178. border.Background = new SolidColorBrush(Colors.White);
  179. border.BorderBrush = new SolidColorBrush(Colors.Black);
  180. border.BorderThickness = new Thickness(1);
  181. border.CornerRadius = new CornerRadius(4);
  182. border.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  183. border.Margin = new Thickness(-border.DesiredSize.Width / 2, 0, 0, 0);
  184. return border;
  185. }
  186. private static CategoricalDataPoint GetDataPoint(DataPointCollection<CategoricalDataPoint> dataPoints, object xCategory)
  187. {
  188. foreach (var dp in dataPoints)
  189. {
  190. if (object.Equals(xCategory, dp.Category))
  191. {
  192. return dp;
  193. }
  194. }
  195. return null;
  196. }
  197. private static IEnumerable<CategoricalDataPoint> GetDataPoints(IndicatorBase indicator, object xCategory)
  198. {
  199. yield return GetDataPoint(indicator.DataPoints, xCategory);
  200. var bollinger = indicator as BollingerBandsIndicator;
  201. if (bollinger != null)
  202. {
  203. yield return GetDataPoint(bollinger.LowerBandDataPoints, xCategory);
  204. }
  205. }
  206. private static void AddIntersectionPoint(RadCartesianChart chart, DataTemplate intersectionPointTemplate, CategoricalDataPoint dataPoint)
  207. {
  208. CartesianCustomAnnotation annotation = new CartesianCustomAnnotation();
  209. annotation.ContentTemplate = intersectionPointTemplate;
  210. annotation.HorizontalValue = dataPoint.Category;
  211. annotation.VerticalValue = dataPoint.Value;
  212. annotation.Tag = typeof(ChartUtilities);
  213. chart.Annotations.Add(annotation);
  214. }
  215. public static void RightKeyPressed()
  216. {
  217. var chart = lastMouseOveredChart;
  218. if (chart == null)
  219. {
  220. return;
  221. }
  222. object xCategory = GetCurrentXCategory(chart);
  223. object rightXCategory = null;
  224. bool categoryReached = false;
  225. for (int i = 0; i < chart.ActualWidth; i++)
  226. {
  227. DataTuple tuple = chart.ConvertPointToData(new Point(i, 0));
  228. if (object.Equals(xCategory, tuple.FirstValue))
  229. {
  230. categoryReached = true;
  231. }
  232. else if (categoryReached)
  233. {
  234. rightXCategory = tuple.FirstValue;
  235. break;
  236. }
  237. }
  238. if (rightXCategory == null)
  239. {
  240. return;
  241. }
  242. UpdateCharts(GetAnnotationsGroup(chart), rightXCategory);
  243. }
  244. public static void LeftKeyPressed()
  245. {
  246. var chart = lastMouseOveredChart;
  247. if (chart == null)
  248. {
  249. return;
  250. }
  251. object xCategory = GetCurrentXCategory(chart);
  252. if (xCategory == null)
  253. {
  254. return;
  255. }
  256. object leftXCategory = null;
  257. for (int i = 0; i <= chart.ActualWidth; i++)
  258. {
  259. DataTuple tuple = chart.ConvertPointToData(new Point(i, 0));
  260. if (object.Equals(xCategory, tuple.FirstValue))
  261. {
  262. break;
  263. }
  264. leftXCategory = tuple.FirstValue;
  265. }
  266. if (leftXCategory == null)
  267. {
  268. return;
  269. }
  270. UpdateCharts(GetAnnotationsGroup(chart), leftXCategory);
  271. }
  272. }
  273. }