DateChartSlider.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. using MuchInfo.Chart.Data.Interfaces;
  2. using MuchInfo.Chart.Infrastructure.Helpers;
  3. using MuchInfo.Chart.WPF.Primitives.Interfaces;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Shapes;
  12. namespace MuchInfo.Chart.WPF.Primitives
  13. {
  14. /// <summary>
  15. /// 数据滑块类
  16. /// </summary>
  17. public class DateChartSlider : Canvas
  18. {
  19. #region Fields
  20. private Chart _chart;
  21. private IList<ILineDataPoint> _dataPoints;
  22. private int _dataPointsCount;
  23. private Point _oldPosition;
  24. private bool _isDarkBackground;
  25. /// <summary>
  26. /// 底部微型数据展示区域
  27. /// </summary>
  28. private Rect _miniAreaRect;
  29. private UIElement _miniTrendPath;
  30. /// <summary>
  31. /// 标志鼠标位于滑块状态
  32. /// </summary>
  33. private DateChartSlider.MouseLocationType _mouseLocation;
  34. private Rect _oldMiniAreaRect;
  35. private Rectangle _selectedArea;
  36. /// <summary>
  37. /// 滑块手柄区域
  38. /// </summary>
  39. private Rect _selectedAreaRec;
  40. /// <summary>
  41. /// 开始数据索引
  42. /// </summary>
  43. private int _startIndex;
  44. /// <summary>
  45. /// 滑块右边的x坐标-鼠标按下坐标
  46. /// </summary>
  47. private int _xRight;
  48. #endregion Fields
  49. #region Constructors
  50. public DateChartSlider(Chart aChart)
  51. {
  52. base.LostMouseCapture -= (new MouseEventHandler(this.DateChartSlider_LostMouseCapture));
  53. base.MouseLeftButtonDown -= (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonDown));
  54. base.MouseLeftButtonUp -= (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonUp));
  55. base.MouseMove -= (new MouseEventHandler(this.DateChartSlider_MouseMove));
  56. base.LostMouseCapture += (new MouseEventHandler(this.DateChartSlider_LostMouseCapture));
  57. base.MouseLeftButtonDown += (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonDown));
  58. base.MouseLeftButtonUp += (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonUp));
  59. base.MouseMove += (new MouseEventHandler(this.DateChartSlider_MouseMove));
  60. this._mouseLocation = DateChartSlider.MouseLocationType.Outer;
  61. this._isDarkBackground = true;
  62. this._chart = aChart;
  63. }
  64. #endregion Constructors
  65. #region Enumerations
  66. /// <summary>
  67. /// 标志鼠标位于滑块状态
  68. /// </summary>
  69. private enum MouseLocationType
  70. {
  71. /// <summary>
  72. /// 位于滑块手柄左边
  73. /// </summary>
  74. Left,
  75. /// <summary>
  76. /// 位于滑块手柄右边
  77. /// </summary>
  78. Right,
  79. /// <summary>
  80. /// 位于滑块手柄中间
  81. /// </summary>
  82. Center,
  83. /// <summary>
  84. /// 位于滑块手柄外面
  85. /// </summary>
  86. Outer
  87. }
  88. #endregion Enumerations
  89. #region Methods
  90. #region Internal Methods
  91. /// <summary>
  92. /// 构造微缩图例
  93. /// </summary>
  94. internal void BuildDateChartSlider()
  95. {
  96. float num = 0;
  97. if (this._chart != null)
  98. {
  99. this._isDarkBackground = this._chart.IsDarkBackground;
  100. num = (float)(this._chart.ActualHeight * 0.03);
  101. }
  102. num = num > 35f ? 35 : (num < 18f ? 18f : num);
  103. if (!this.ActualHeight.Equals(num)) this.Height = ((double)num);
  104. var list = new List<UIElement>();
  105. //var actualWidth = (float)(this.ActualWidth - 40.0) - (this._chart == null ? 0 : this._chart.RightBuffer);
  106. var actualWidth = this._chart == null ? 0 : this._chart.GetChartPaneWidth();
  107. actualWidth = (actualWidth < 0f) ? 0f : actualWidth;
  108. //绘制整个控件背景色矩形
  109. if (this._isDarkBackground)
  110. {
  111. this.BuildRectangle(0.0, 0.0, (double)actualWidth, this.ActualHeight, Color.FromArgb(255, 40, 40, 40), Colors.Transparent, list);
  112. }
  113. else
  114. {
  115. if (this._chart != null)
  116. {
  117. this.BuildRectangle(0.0, 0.0, (double)actualWidth, this.ActualHeight, this._chart.ChartBackgroundColor, Colors.Transparent, list);
  118. }
  119. this.BuildRectangle(-1.0, 0.0, (double)actualWidth, this.ActualHeight + 2.0, Colors.Transparent, Colors.Gray, list);
  120. }
  121. this._miniAreaRect = new Rect(0.0, 0.0, (double)actualWidth, this.ActualHeight);
  122. if (_chart != null && _chart.DateScaler != null)
  123. {
  124. var count = _chart.DateScaler.Count();
  125. if (count > 1)
  126. {
  127. //绘制两滑块中背景色矩形
  128. double startLeft = (double)checked((int)Math.Round((double)(unchecked(actualWidth * (float)_chart.DateScaler.StartDateIndex) / (float)(count - 1)))) + 0.5;
  129. double endLeft = (double)checked((int)Math.Round((double)(unchecked(actualWidth * (float)_chart.DateScaler.EndDateIndex) / (float)(count - 1)))) + 0.5;
  130. this._selectedArea = this.BuildRectangle(startLeft, 0.0, endLeft - startLeft, this.ActualHeight,
  131. this._isDarkBackground ? Colors.DimGray : Colors.SlateGray, Colors.Transparent, list);
  132. this._selectedAreaRec = new Rect(startLeft, 0.0, endLeft - startLeft, this.ActualHeight);
  133. //绘制背景走势矩形
  134. list.Add(this.BuildMiniTrendPath());
  135. //绘制滑块上下两根线
  136. this.BuildLine(startLeft, 0.0, startLeft, this.ActualHeight, this._isDarkBackground ? Colors.DarkGray : Colors.LightGray, list);
  137. this.BuildLine(endLeft, 0.0, endLeft, this.ActualHeight, this._isDarkBackground ? Colors.DarkGray : Colors.LightGray, list);
  138. //绘制起始滑块
  139. this.BuildSliderHandle(startLeft, this.ActualHeight / 2.0, list);
  140. //绘制结束滑块
  141. this.BuildSliderHandle(endLeft, this.ActualHeight / 2.0, list);
  142. }
  143. //控制时间滑块区域:TD1464
  144. var rectangleGeometry = new RectangleGeometry
  145. {
  146. Rect = new Rect(0.0, 0.0, this.ActualWidth, this.ActualHeight)
  147. };
  148. this.Clip = (rectangleGeometry);
  149. this.Children.Clear();
  150. foreach (var current in list)
  151. {
  152. this.Children.Add(current);
  153. }
  154. }
  155. }
  156. #endregion Internal Methods
  157. #region Private Methods
  158. /// <summary>
  159. /// 画线
  160. /// </summary>
  161. /// <param name="x1">The x1.</param>
  162. /// <param name="y1">The y1.</param>
  163. /// <param name="x2">The x2.</param>
  164. /// <param name="y2">The y2.</param>
  165. /// <param name="aColor">A color.</param>
  166. /// <param name="toAdd">To add.</param>
  167. private void BuildLine(double x1, double y1, double x2, double y2, Color aColor, List<UIElement> toAdd)
  168. {
  169. if (toAdd == null) return;
  170. toAdd.Add(new Line { X1 = (x1), X2 = (x2), Y1 = (y1), Y2 = (y2), Stroke = new SolidColorBrush(aColor) });
  171. }
  172. /// <summary>
  173. /// 数据微缩显示区块
  174. /// </summary>
  175. /// <returns></returns>
  176. private UIElement BuildMiniTrendPath()
  177. {
  178. if (this._miniAreaRect.Equals(this._oldMiniAreaRect) && this._miniTrendPath != null &&
  179. this._chart != null && this._chart.DataSet != null && this._chart.DataSet.DataPoints != null &&
  180. this._dataPoints == this._chart.DataSet.DataPoints && this._dataPointsCount == this._chart.DateScaler.Count())
  181. {
  182. return this._miniTrendPath;
  183. }
  184. this._oldMiniAreaRect = this._miniAreaRect;
  185. var num = (float)this._miniAreaRect.Width;
  186. var solidColorBrush = new SolidColorBrush
  187. {
  188. Color = this._isDarkBackground ? Colors.DarkGray : ColorHelper.SteelBlue,
  189. Opacity = (0.5)
  190. };
  191. var path = new Path
  192. {
  193. Stroke = solidColorBrush,
  194. StrokeThickness = (1.0),
  195. Fill = this._isDarkBackground
  196. ? new SolidColorBrush(Color.FromArgb(100, 100, 100, 100))
  197. : new SolidColorBrush(Color.FromArgb(100, 155, 155, 155))
  198. };
  199. if (this._chart == null || this._chart.DataSet == null || this._chart.DataSet.DataPoints == null || !this._chart.DataSet.DataPoints.Any())
  200. {
  201. return path;
  202. }
  203. IDateScaler dateScaler = this._chart.DateScaler;
  204. var list = new List<ILineDataPoint>();
  205. this._dataPoints = this._chart.DataSet.DataPoints;
  206. this._dataPointsCount = this._chart.DateScaler.Count();
  207. list.AddRange(this._dataPoints);
  208. var pathFigure = new PathFigure { StartPoint = new Point(0.0, this.ActualHeight) };
  209. var pathGeometry = new PathGeometry();
  210. pathGeometry.Figures.Add(pathFigure);
  211. path.Data = pathGeometry;
  212. var maxValue = list.Max(z => z.Value); //存储最大值
  213. var minValue = list.Where(z => z.Value > 0).Min(z => z.Value); //存储最小值
  214. var scalerLength = dateScaler.Count() - 1;
  215. scalerLength = scalerLength <= 0 ? 1 : scalerLength;
  216. LineSegment lineSegment;
  217. int index;
  218. float top;
  219. float left;
  220. checked
  221. {
  222. if (list.Count > 8)
  223. {
  224. int length = list.Count - 1;
  225. int i = 0;
  226. while (true)
  227. {
  228. int num6 = length;
  229. if (i > num6)
  230. {
  231. break;
  232. }
  233. lineSegment = new LineSegment();
  234. index = i;
  235. unchecked
  236. {
  237. //top = num * (float)index / (float)(dateScaler.Count());
  238. top = num * index / scalerLength;
  239. if (maxValue.Equals(minValue))
  240. {
  241. left = (float)(this.ActualHeight * 0.5);
  242. }
  243. else
  244. {
  245. left = (float)(this.ActualHeight * (double)(1f - (list[i].Value - minValue) / (maxValue - minValue)));
  246. }
  247. lineSegment.Point = new Point((double)top, (double)left);
  248. pathFigure.Segments.Add(lineSegment);
  249. }
  250. i += 5;
  251. }
  252. }
  253. lineSegment = new LineSegment();
  254. index = dateScaler.IndexFromDate(list[list.Count - 1].Date);
  255. }
  256. //top = num * (float)index / (float)(dateScaler.Count());
  257. top = num * index / scalerLength;
  258. if (maxValue.Equals(minValue))
  259. {
  260. left = (float)(this.ActualHeight * 0.5);
  261. }
  262. else
  263. {
  264. left = (float)(this.ActualHeight * (double)(1f - (list[checked(list.Count - 1)].Value - minValue) / (maxValue - minValue)));
  265. }
  266. lineSegment.Point = new Point((double)top, (double)left);
  267. pathFigure.Segments.Add(lineSegment);
  268. lineSegment = new LineSegment { Point = new Point((double)top, this.ActualHeight) };
  269. pathFigure.Segments.Add(lineSegment);
  270. lineSegment = new LineSegment { Point = new Point(0.0, this.ActualHeight) };
  271. pathFigure.Segments.Add(lineSegment);
  272. this._miniTrendPath = path;
  273. return path;
  274. }
  275. /// <summary>
  276. /// 构造一个矩形
  277. /// </summary>
  278. /// <param name="x"></param>
  279. /// <param name="y"></param>
  280. /// <param name="width"></param>
  281. /// <param name="height"></param>
  282. /// <param name="fill"></param>
  283. /// <param name="border"></param>
  284. /// <param name="toAdd"></param>
  285. /// <returns></returns>
  286. private Rectangle BuildRectangle(double x, double y, double width, double height, Color fill, Color border, List<UIElement> toAdd)
  287. {
  288. Rectangle result = null;
  289. if (width > 0.0 && height > 0.0)
  290. {
  291. var rectangle = new Rectangle()
  292. {
  293. Height = height,
  294. Width = width,
  295. };
  296. rectangle.SetValue(Canvas.LeftProperty, x);
  297. rectangle.SetValue(Canvas.TopProperty, y);
  298. if (!fill.Equals(Colors.Transparent))
  299. {
  300. rectangle.Fill = new SolidColorBrush(fill);
  301. }
  302. if (!border.Equals(Colors.Transparent))
  303. {
  304. rectangle.Stroke = new SolidColorBrush(border);
  305. }
  306. toAdd.Add(rectangle);
  307. result = rectangle;
  308. }
  309. return result;
  310. }
  311. /// <summary>
  312. /// 构造一个滑块手柄
  313. /// </summary>
  314. /// <param name="x"></param>
  315. /// <param name="y"></param>
  316. /// <param name="toAdd"></param>
  317. /// <returns></returns>
  318. private Rectangle BuildSliderHandle(double x, double y, List<UIElement> toAdd)
  319. {
  320. var rectangle = new Rectangle
  321. {
  322. Height = 16.0,
  323. Width = 10.0,
  324. Fill = this._isDarkBackground ? new SolidColorBrush(Colors.DarkGray) : new SolidColorBrush(Colors.LightGray),
  325. RadiusX = 4.0,
  326. RadiusY = 4.0,
  327. };
  328. rectangle.SetValue(Canvas.TopProperty, y - 8.0);
  329. rectangle.SetValue(Canvas.LeftProperty, x - 5.0);
  330. toAdd.Add(rectangle);
  331. int num = -2;
  332. do
  333. {
  334. var line = new Line
  335. {
  336. X1 = x + (double)num,
  337. X2 = x + (double)num,
  338. Y1 = y - 3.0,
  339. Y2 = y + 3.0,
  340. Stroke = new SolidColorBrush(Color.FromArgb(255, 130, 130, 130))
  341. };
  342. toAdd.Add(line);
  343. checked
  344. {
  345. num += 2;
  346. }
  347. }
  348. while (num <= 2);
  349. return rectangle;
  350. }
  351. private void DateChartSlider_LostMouseCapture(object sender, MouseEventArgs e)
  352. {
  353. this._mouseLocation = DateChartSlider.MouseLocationType.Outer;
  354. }
  355. private void DateChartSlider_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  356. {
  357. e.Handled = true;
  358. Point position = e.GetPosition(this);
  359. _oldPosition = position;
  360. this._mouseLocation = this.GetMouseLocationType(position);
  361. checked
  362. {
  363. this._xRight = (int)Math.Round(unchecked(this._selectedAreaRec.Right - position.X));
  364. this._startIndex = this._chart.DateScaler.StartDateIndex;
  365. switch (this._mouseLocation)
  366. {
  367. case DateChartSlider.MouseLocationType.Left:
  368. case DateChartSlider.MouseLocationType.Right:
  369. this.Cursor = Cursors.SizeWE;
  370. this.CaptureMouse();
  371. break;
  372. case DateChartSlider.MouseLocationType.Center:
  373. this.Cursor = Cursors.Hand;
  374. this.CaptureMouse();
  375. break;
  376. case DateChartSlider.MouseLocationType.Outer:
  377. this.Cursor = Cursors.Arrow;
  378. break;
  379. }
  380. }
  381. }
  382. private void DateChartSlider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  383. {
  384. e.Handled = true;
  385. var position = e.GetPosition(this);
  386. if (this._mouseLocation != DateChartSlider.MouseLocationType.Outer)
  387. {
  388. this.DragSliderTo(position);
  389. this._mouseLocation = DateChartSlider.MouseLocationType.Outer;
  390. this.ReleaseMouseCapture();
  391. }
  392. }
  393. private void DateChartSlider_MouseMove(object sender, MouseEventArgs e)
  394. {
  395. var position = e.GetPosition(this);
  396. bool flag = this._mouseLocation != DateChartSlider.MouseLocationType.Outer;
  397. if (flag)
  398. {
  399. this.DragSliderTo(position);
  400. }
  401. else
  402. {
  403. switch (this.GetMouseLocationType(position))
  404. {
  405. case DateChartSlider.MouseLocationType.Left:
  406. case DateChartSlider.MouseLocationType.Right:
  407. this.Cursor = Cursors.SizeWE;
  408. break;
  409. case DateChartSlider.MouseLocationType.Center:
  410. this.Cursor = Cursors.Hand;
  411. break;
  412. case DateChartSlider.MouseLocationType.Outer:
  413. this.Cursor = Cursors.Arrow;
  414. break;
  415. }
  416. }
  417. _oldPosition = position;
  418. }
  419. /// <summary>
  420. /// 拖拉、移动滑块执行操作
  421. /// </summary>
  422. /// <param name="pos"></param>
  423. private void DragSliderTo(Point pos)
  424. {
  425. //避免在选择块中间单击移动
  426. if (this._mouseLocation == MouseLocationType.Center)
  427. {
  428. var x = pos.X - _oldPosition.X;
  429. if (Math.Abs(x) < 2) return;
  430. }
  431. bool flag = false;
  432. var scalerCount = this._chart.DateScaler.Count();
  433. double num = (pos.X - this._miniAreaRect.X) / this._miniAreaRect.Width;
  434. num = num * scalerCount - 1.0; //得出num=鼠标点击处位于数据展示区的数据个数
  435. if (double.IsInfinity(num) || double.IsNaN(num)) return;
  436. switch (this._mouseLocation)
  437. {
  438. case DateChartSlider.MouseLocationType.Left:
  439. {
  440. checked
  441. {
  442. var number = (int)Math.Round(num);
  443. number = number < 0 ? 0 : number;
  444. if (number > this._chart.DateScaler.EndDateIndex - 3)
  445. {
  446. number = this._chart.DateScaler.EndDateIndex - 3;
  447. }
  448. if (this._chart.DateScaler.NumBars != this._chart.DateScaler.EndDateIndex - number)
  449. {
  450. this._chart.DateScaler.NumBars = this._chart.DateScaler.EndDateIndex - number;
  451. flag = true;
  452. }
  453. break;
  454. }
  455. }
  456. case DateChartSlider.MouseLocationType.Right:
  457. {
  458. checked
  459. {
  460. var number = (int)Math.Round(num);
  461. number = number < this._startIndex + 3 ? this._startIndex + 3 : number;
  462. if (number >= scalerCount - 1)
  463. {
  464. if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, DateTime.MaxValue) != 0)
  465. {
  466. this._chart.DateScaler.DesiredEndDate = DateTime.MaxValue;
  467. flag = true;
  468. }
  469. number = scalerCount - 1;
  470. }
  471. else
  472. {
  473. if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, this._chart.DateScaler.DateValue(number)) != 0)
  474. {
  475. this._chart.DateScaler.DesiredEndDate = this._chart.DateScaler.DateValue(number);
  476. flag = true;
  477. }
  478. }
  479. var bars = number - this._startIndex;
  480. if (this._chart.DateScaler.NumBars != bars)
  481. {
  482. this._chart.DateScaler.NumBars = bars;
  483. flag = true;
  484. }
  485. break;
  486. }
  487. }
  488. case DateChartSlider.MouseLocationType.Center:
  489. {
  490. checked
  491. {
  492. var number = (int)Math.Round(num);
  493. number = number - this._chart.DateScaler.NumBars < 0 ? this._chart.DateScaler.NumBars : number;
  494. if (number >= scalerCount - 1)
  495. {
  496. if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, DateTime.MaxValue) != 0)
  497. {
  498. this._chart.DateScaler.DesiredEndDate = DateTime.MaxValue;
  499. flag = true;
  500. }
  501. number = scalerCount - 1;
  502. }
  503. else
  504. {
  505. if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, this._chart.DateScaler.DateValue(number)) != 0)
  506. {
  507. this._chart.DateScaler.DesiredEndDate = this._chart.DateScaler.DateValue(number);
  508. flag = true;
  509. }
  510. }
  511. break;
  512. }
  513. }
  514. }
  515. if (flag)
  516. {
  517. this._chart.Refresh();
  518. }
  519. }
  520. /// <summary>
  521. /// 返回指定点位于滑块的位置
  522. /// </summary>
  523. /// <param name="pos"></param>
  524. /// <returns></returns>
  525. private DateChartSlider.MouseLocationType GetMouseLocationType(Point pos)
  526. {
  527. if (pos.X > this._selectedAreaRec.Left - 3.0 && pos.X < this._selectedAreaRec.Left + 3.0)
  528. return DateChartSlider.MouseLocationType.Left;
  529. if (pos.X > this._selectedAreaRec.Right - 3.0 && pos.X < this._selectedAreaRec.Right + 3.0)
  530. return DateChartSlider.MouseLocationType.Right;
  531. if (this._selectedAreaRec.Contains(pos)) return DateChartSlider.MouseLocationType.Center;
  532. return DateChartSlider.MouseLocationType.Outer;
  533. }
  534. #endregion Private Methods
  535. #endregion Methods
  536. }
  537. }