using MuchInfo.Chart.Data.Interfaces; using MuchInfo.Chart.Infrastructure.Helpers; using MuchInfo.Chart.WPF.Primitives.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; namespace MuchInfo.Chart.WPF.Primitives { /// /// 数据滑块类 /// public class DateChartSlider : Canvas { #region Fields private Chart _chart; private IList _dataPoints; private int _dataPointsCount; private Point _oldPosition; private bool _isDarkBackground; /// /// 底部微型数据展示区域 /// private Rect _miniAreaRect; private UIElement _miniTrendPath; /// /// 标志鼠标位于滑块状态 /// private DateChartSlider.MouseLocationType _mouseLocation; private Rect _oldMiniAreaRect; private Rectangle _selectedArea; /// /// 滑块手柄区域 /// private Rect _selectedAreaRec; /// /// 开始数据索引 /// private int _startIndex; /// /// 滑块右边的x坐标-鼠标按下坐标 /// private int _xRight; #endregion Fields #region Constructors public DateChartSlider(Chart aChart) { base.LostMouseCapture -= (new MouseEventHandler(this.DateChartSlider_LostMouseCapture)); base.MouseLeftButtonDown -= (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonDown)); base.MouseLeftButtonUp -= (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonUp)); base.MouseMove -= (new MouseEventHandler(this.DateChartSlider_MouseMove)); base.LostMouseCapture += (new MouseEventHandler(this.DateChartSlider_LostMouseCapture)); base.MouseLeftButtonDown += (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonDown)); base.MouseLeftButtonUp += (new MouseButtonEventHandler(this.DateChartSlider_MouseLeftButtonUp)); base.MouseMove += (new MouseEventHandler(this.DateChartSlider_MouseMove)); this._mouseLocation = DateChartSlider.MouseLocationType.Outer; this._isDarkBackground = true; this._chart = aChart; } #endregion Constructors #region Enumerations /// /// 标志鼠标位于滑块状态 /// private enum MouseLocationType { /// /// 位于滑块手柄左边 /// Left, /// /// 位于滑块手柄右边 /// Right, /// /// 位于滑块手柄中间 /// Center, /// /// 位于滑块手柄外面 /// Outer } #endregion Enumerations #region Methods #region Internal Methods /// /// 构造微缩图例 /// internal void BuildDateChartSlider() { float num = 0; if (this._chart != null) { this._isDarkBackground = this._chart.IsDarkBackground; num = (float)(this._chart.ActualHeight * 0.03); } num = num > 35f ? 35 : (num < 18f ? 18f : num); if (!this.ActualHeight.Equals(num)) this.Height = ((double)num); var list = new List(); //var actualWidth = (float)(this.ActualWidth - 40.0) - (this._chart == null ? 0 : this._chart.RightBuffer); var actualWidth = this._chart == null ? 0 : this._chart.GetChartPaneWidth(); actualWidth = (actualWidth < 0f) ? 0f : actualWidth; //绘制整个控件背景色矩形 if (this._isDarkBackground) { this.BuildRectangle(0.0, 0.0, (double)actualWidth, this.ActualHeight, Color.FromArgb(255, 40, 40, 40), Colors.Transparent, list); } else { if (this._chart != null) { this.BuildRectangle(0.0, 0.0, (double)actualWidth, this.ActualHeight, this._chart.ChartBackgroundColor, Colors.Transparent, list); } this.BuildRectangle(-1.0, 0.0, (double)actualWidth, this.ActualHeight + 2.0, Colors.Transparent, Colors.Gray, list); } this._miniAreaRect = new Rect(0.0, 0.0, (double)actualWidth, this.ActualHeight); if (_chart != null && _chart.DateScaler != null) { var count = _chart.DateScaler.Count(); if (count > 1) { //绘制两滑块中背景色矩形 double startLeft = (double)checked((int)Math.Round((double)(unchecked(actualWidth * (float)_chart.DateScaler.StartDateIndex) / (float)(count - 1)))) + 0.5; double endLeft = (double)checked((int)Math.Round((double)(unchecked(actualWidth * (float)_chart.DateScaler.EndDateIndex) / (float)(count - 1)))) + 0.5; this._selectedArea = this.BuildRectangle(startLeft, 0.0, endLeft - startLeft, this.ActualHeight, this._isDarkBackground ? Colors.DimGray : Colors.SlateGray, Colors.Transparent, list); this._selectedAreaRec = new Rect(startLeft, 0.0, endLeft - startLeft, this.ActualHeight); //绘制背景走势矩形 list.Add(this.BuildMiniTrendPath()); //绘制滑块上下两根线 this.BuildLine(startLeft, 0.0, startLeft, this.ActualHeight, this._isDarkBackground ? Colors.DarkGray : Colors.LightGray, list); this.BuildLine(endLeft, 0.0, endLeft, this.ActualHeight, this._isDarkBackground ? Colors.DarkGray : Colors.LightGray, list); //绘制起始滑块 this.BuildSliderHandle(startLeft, this.ActualHeight / 2.0, list); //绘制结束滑块 this.BuildSliderHandle(endLeft, this.ActualHeight / 2.0, list); } //控制时间滑块区域:TD1464 var rectangleGeometry = new RectangleGeometry { Rect = new Rect(0.0, 0.0, this.ActualWidth, this.ActualHeight) }; this.Clip = (rectangleGeometry); this.Children.Clear(); foreach (var current in list) { this.Children.Add(current); } } } #endregion Internal Methods #region Private Methods /// /// 画线 /// /// The x1. /// The y1. /// The x2. /// The y2. /// A color. /// To add. private void BuildLine(double x1, double y1, double x2, double y2, Color aColor, List toAdd) { if (toAdd == null) return; toAdd.Add(new Line { X1 = (x1), X2 = (x2), Y1 = (y1), Y2 = (y2), Stroke = new SolidColorBrush(aColor) }); } /// /// 数据微缩显示区块 /// /// private UIElement BuildMiniTrendPath() { if (this._miniAreaRect.Equals(this._oldMiniAreaRect) && this._miniTrendPath != null && this._chart != null && this._chart.DataSet != null && this._chart.DataSet.DataPoints != null && this._dataPoints == this._chart.DataSet.DataPoints && this._dataPointsCount == this._chart.DateScaler.Count()) { return this._miniTrendPath; } this._oldMiniAreaRect = this._miniAreaRect; var num = (float)this._miniAreaRect.Width; var solidColorBrush = new SolidColorBrush { Color = this._isDarkBackground ? Colors.DarkGray : ColorHelper.SteelBlue, Opacity = (0.5) }; var path = new Path { Stroke = solidColorBrush, StrokeThickness = (1.0), Fill = this._isDarkBackground ? new SolidColorBrush(Color.FromArgb(100, 100, 100, 100)) : new SolidColorBrush(Color.FromArgb(100, 155, 155, 155)) }; if (this._chart == null || this._chart.DataSet == null || this._chart.DataSet.DataPoints == null || !this._chart.DataSet.DataPoints.Any()) { return path; } IDateScaler dateScaler = this._chart.DateScaler; var list = new List(); this._dataPoints = this._chart.DataSet.DataPoints; this._dataPointsCount = this._chart.DateScaler.Count(); list.AddRange(this._dataPoints); var pathFigure = new PathFigure { StartPoint = new Point(0.0, this.ActualHeight) }; var pathGeometry = new PathGeometry(); pathGeometry.Figures.Add(pathFigure); path.Data = pathGeometry; var maxValue = list.Max(z => z.Value); //存储最大值 var minValue = list.Where(z => z.Value > 0).Min(z => z.Value); //存储最小值 var scalerLength = dateScaler.Count() - 1; scalerLength = scalerLength <= 0 ? 1 : scalerLength; LineSegment lineSegment; int index; float top; float left; checked { if (list.Count > 8) { int length = list.Count - 1; int i = 0; while (true) { int num6 = length; if (i > num6) { break; } lineSegment = new LineSegment(); index = i; unchecked { //top = num * (float)index / (float)(dateScaler.Count()); top = num * index / scalerLength; if (maxValue.Equals(minValue)) { left = (float)(this.ActualHeight * 0.5); } else { left = (float)(this.ActualHeight * (double)(1f - (list[i].Value - minValue) / (maxValue - minValue))); } lineSegment.Point = new Point((double)top, (double)left); pathFigure.Segments.Add(lineSegment); } i += 5; } } lineSegment = new LineSegment(); index = dateScaler.IndexFromDate(list[list.Count - 1].Date); } //top = num * (float)index / (float)(dateScaler.Count()); top = num * index / scalerLength; if (maxValue.Equals(minValue)) { left = (float)(this.ActualHeight * 0.5); } else { left = (float)(this.ActualHeight * (double)(1f - (list[checked(list.Count - 1)].Value - minValue) / (maxValue - minValue))); } lineSegment.Point = new Point((double)top, (double)left); pathFigure.Segments.Add(lineSegment); lineSegment = new LineSegment { Point = new Point((double)top, this.ActualHeight) }; pathFigure.Segments.Add(lineSegment); lineSegment = new LineSegment { Point = new Point(0.0, this.ActualHeight) }; pathFigure.Segments.Add(lineSegment); this._miniTrendPath = path; return path; } /// /// 构造一个矩形 /// /// /// /// /// /// /// /// /// private Rectangle BuildRectangle(double x, double y, double width, double height, Color fill, Color border, List toAdd) { Rectangle result = null; if (width > 0.0 && height > 0.0) { var rectangle = new Rectangle() { Height = height, Width = width, }; rectangle.SetValue(Canvas.LeftProperty, x); rectangle.SetValue(Canvas.TopProperty, y); if (!fill.Equals(Colors.Transparent)) { rectangle.Fill = new SolidColorBrush(fill); } if (!border.Equals(Colors.Transparent)) { rectangle.Stroke = new SolidColorBrush(border); } toAdd.Add(rectangle); result = rectangle; } return result; } /// /// 构造一个滑块手柄 /// /// /// /// /// private Rectangle BuildSliderHandle(double x, double y, List toAdd) { var rectangle = new Rectangle { Height = 16.0, Width = 10.0, Fill = this._isDarkBackground ? new SolidColorBrush(Colors.DarkGray) : new SolidColorBrush(Colors.LightGray), RadiusX = 4.0, RadiusY = 4.0, }; rectangle.SetValue(Canvas.TopProperty, y - 8.0); rectangle.SetValue(Canvas.LeftProperty, x - 5.0); toAdd.Add(rectangle); int num = -2; do { var line = new Line { X1 = x + (double)num, X2 = x + (double)num, Y1 = y - 3.0, Y2 = y + 3.0, Stroke = new SolidColorBrush(Color.FromArgb(255, 130, 130, 130)) }; toAdd.Add(line); checked { num += 2; } } while (num <= 2); return rectangle; } private void DateChartSlider_LostMouseCapture(object sender, MouseEventArgs e) { this._mouseLocation = DateChartSlider.MouseLocationType.Outer; } private void DateChartSlider_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { e.Handled = true; Point position = e.GetPosition(this); _oldPosition = position; this._mouseLocation = this.GetMouseLocationType(position); checked { this._xRight = (int)Math.Round(unchecked(this._selectedAreaRec.Right - position.X)); this._startIndex = this._chart.DateScaler.StartDateIndex; switch (this._mouseLocation) { case DateChartSlider.MouseLocationType.Left: case DateChartSlider.MouseLocationType.Right: this.Cursor = Cursors.SizeWE; this.CaptureMouse(); break; case DateChartSlider.MouseLocationType.Center: this.Cursor = Cursors.Hand; this.CaptureMouse(); break; case DateChartSlider.MouseLocationType.Outer: this.Cursor = Cursors.Arrow; break; } } } private void DateChartSlider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { e.Handled = true; var position = e.GetPosition(this); if (this._mouseLocation != DateChartSlider.MouseLocationType.Outer) { this.DragSliderTo(position); this._mouseLocation = DateChartSlider.MouseLocationType.Outer; this.ReleaseMouseCapture(); } } private void DateChartSlider_MouseMove(object sender, MouseEventArgs e) { var position = e.GetPosition(this); bool flag = this._mouseLocation != DateChartSlider.MouseLocationType.Outer; if (flag) { this.DragSliderTo(position); } else { switch (this.GetMouseLocationType(position)) { case DateChartSlider.MouseLocationType.Left: case DateChartSlider.MouseLocationType.Right: this.Cursor = Cursors.SizeWE; break; case DateChartSlider.MouseLocationType.Center: this.Cursor = Cursors.Hand; break; case DateChartSlider.MouseLocationType.Outer: this.Cursor = Cursors.Arrow; break; } } _oldPosition = position; } /// /// 拖拉、移动滑块执行操作 /// /// private void DragSliderTo(Point pos) { //避免在选择块中间单击移动 if (this._mouseLocation == MouseLocationType.Center) { var x = pos.X - _oldPosition.X; if (Math.Abs(x) < 2) return; } bool flag = false; var scalerCount = this._chart.DateScaler.Count(); double num = (pos.X - this._miniAreaRect.X) / this._miniAreaRect.Width; num = num * scalerCount - 1.0; //得出num=鼠标点击处位于数据展示区的数据个数 if (double.IsInfinity(num) || double.IsNaN(num)) return; switch (this._mouseLocation) { case DateChartSlider.MouseLocationType.Left: { checked { var number = (int)Math.Round(num); number = number < 0 ? 0 : number; if (number > this._chart.DateScaler.EndDateIndex - 3) { number = this._chart.DateScaler.EndDateIndex - 3; } if (this._chart.DateScaler.NumBars != this._chart.DateScaler.EndDateIndex - number) { this._chart.DateScaler.NumBars = this._chart.DateScaler.EndDateIndex - number; flag = true; } break; } } case DateChartSlider.MouseLocationType.Right: { checked { var number = (int)Math.Round(num); number = number < this._startIndex + 3 ? this._startIndex + 3 : number; if (number >= scalerCount - 1) { if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, DateTime.MaxValue) != 0) { this._chart.DateScaler.DesiredEndDate = DateTime.MaxValue; flag = true; } number = scalerCount - 1; } else { if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, this._chart.DateScaler.DateValue(number)) != 0) { this._chart.DateScaler.DesiredEndDate = this._chart.DateScaler.DateValue(number); flag = true; } } var bars = number - this._startIndex; if (this._chart.DateScaler.NumBars != bars) { this._chart.DateScaler.NumBars = bars; flag = true; } break; } } case DateChartSlider.MouseLocationType.Center: { checked { var number = (int)Math.Round(num); number = number - this._chart.DateScaler.NumBars < 0 ? this._chart.DateScaler.NumBars : number; if (number >= scalerCount - 1) { if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, DateTime.MaxValue) != 0) { this._chart.DateScaler.DesiredEndDate = DateTime.MaxValue; flag = true; } number = scalerCount - 1; } else { if (DateTime.Compare(this._chart.DateScaler.DesiredEndDate, this._chart.DateScaler.DateValue(number)) != 0) { this._chart.DateScaler.DesiredEndDate = this._chart.DateScaler.DateValue(number); flag = true; } } break; } } } if (flag) { this._chart.Refresh(); } } /// /// 返回指定点位于滑块的位置 /// /// /// private DateChartSlider.MouseLocationType GetMouseLocationType(Point pos) { if (pos.X > this._selectedAreaRec.Left - 3.0 && pos.X < this._selectedAreaRec.Left + 3.0) return DateChartSlider.MouseLocationType.Left; if (pos.X > this._selectedAreaRec.Right - 3.0 && pos.X < this._selectedAreaRec.Right + 3.0) return DateChartSlider.MouseLocationType.Right; if (this._selectedAreaRec.Contains(pos)) return DateChartSlider.MouseLocationType.Center; return DateChartSlider.MouseLocationType.Outer; } #endregion Private Methods #endregion Methods } }