using DevExpress.Utils; using DevExpress.XtraCharts; namespace Yw.WinFrmUI.Phart { /// /// /// public partial class UniversalEditChart : DevExpress.XtraEditors.XtraUserControl { public UniversalEditChart() { InitializeComponent(); InitialChart(); } #region Private Variable private XYDiagram _diagram; private XYDiagramDefaultPane _default_pane; private AxisX _axis_x; private AxisY _axis_y; private Series _series_edit_pt; private List _def_pt_list; private List _fit_pt_list; private UniversalCoordinate _coordinate; private bool _initial_data = false; private bool _mouse_mode = false;//鼠标模式 #endregion #region Public Variable /// /// X轴标题 /// public string AxisXTitle { get { return _axis_x.Title.Text; } set { _axis_x.Title.Text = value; } } /// /// Y轴标题 /// public string AxisYTitle { get { return _axis_y.Title.Text; } set { _axis_y.Title.Text = value; } } /// /// 鼠标模式 /// public bool MouseModel { get { return _mouse_mode; } set { _mouse_mode = value; } } #endregion #region Public Evnet /// /// 坐标变更事件 /// public event Action CoordinateChangedEvent; /// /// 定义点变更事件 /// public event Action> DefinePointChangedEvent; /// /// 选中点 /// public event Action SelectedPointIndexChangedEvent; #endregion #region Private Initial /// /// 初始化图表 /// private void InitialChart() { this.chartControl1.SetChartDisplay(); this.chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False; this.chartControl1.SeriesSelectionMode = SeriesSelectionMode.Point; this.chartControl1.SelectionMode = ElementSelectionMode.Single; this.chartControl1.RuntimeHitTesting = true; this.chartControl1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseMove); this.chartControl1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseUp); this.chartControl1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.chartControl1_MouseDown); this.chartControl1.KeyUp += new System.Windows.Forms.KeyEventHandler(this.chartControl1_KeyUp); _diagram = (XYDiagram)chartControl1.Diagram; _default_pane = _diagram.DefaultPane; _axis_x = _diagram.AxisX; _axis_x.SetAxisXQDisplay(); _axis_y = _diagram.AxisY; _axis_y.SetAxisYQHDisplay(); _axis_x.Visibility = DefaultBoolean.False; _axis_x.GridLines.Visible = false; _axis_y.Visibility = DefaultBoolean.False; _axis_y.GridLines.Visible = false; this.chartControl1.SetChartMonoColorDisplay(); } /// /// 初始化坐标轴 /// private void InitialCoordinate() { //设置成白板坐标 _coordinate = new UniversalCoordinate(); _coordinate.GridNumberX = 30; _coordinate.GridNumberY = 16; _coordinate.StartLineNoY = 10; _coordinate.EndLineNoY = 15; _coordinate.CoordMinX = 0; _coordinate.CoordSpaceX = 1000; _coordinate.CoordMinY = 10; _coordinate.CoordSpaceY = 100; } #endregion Initial #region Private Chart Event private int _pick_point_index = -1; private void chartControl1_MouseDown(object sender, MouseEventArgs e) { if (!_initial_data) return; var hitInfo = chartControl1.CalcHitInfo(e.Location); _pick_point_index = -1; if (e.Button == MouseButtons.Left) { if (hitInfo.InSeriesPoint && hitInfo.SeriesPoint.Tag is int index) { _pick_point_index = index; this.SelectedPointIndexChangedEvent?.Invoke(_pick_point_index); } } } private void chartControl1_MouseMove(object sender, MouseEventArgs e) { if (!_mouse_mode) return; if (_pick_point_index < 0) return; if (IsInvalidData()) return; var diagram_coordinates = _diagram.PointToDiagram(e.Location); var axis_value = diagram_coordinates.GetAxisValue(_axis_y); if (axis_value == null) return; _series_edit_pt.Points[_pick_point_index].Values[0] = axis_value.NumericalValue; _series_edit_pt.Points[_pick_point_index].NumericalArgument = diagram_coordinates.NumericalArgument; } private void chartControl1_MouseUp(object sender, MouseEventArgs e) { if (!_mouse_mode) return; if (_pick_point_index < 0) return; if (IsInvalidData()) return; var x = _series_edit_pt.Points[_pick_point_index].NumericalArgument; var y = _series_edit_pt.Points[_pick_point_index].Values[0]; x = Math.Round(x, 2); y = Math.Round(y, 2); SetPointValue(_pick_point_index, x, y); _pick_point_index = -1; } private void chartControl1_KeyUp(object sender, KeyEventArgs e) { if (_mouse_mode) return; if (_pick_point_index < 0) return; if (IsInvalidData()) return; double space_x = _coordinate.CoordSpaceX / 50; double space_y = _coordinate.CoordSpaceY / 50; double x = _series_edit_pt.Points[_pick_point_index].NumericalArgument; double y = _series_edit_pt.Points[_pick_point_index].Values[0]; if (e.KeyCode == Keys.Up) { y += space_y; } else if (e.KeyCode == Keys.Down) { y -= space_y; } else if (e.KeyCode == Keys.Left) { x -= space_x; } else if (e.KeyCode == Keys.Right) { x += space_x; } x = Math.Round(x, 2); y = Math.Round(y, 2); _series_edit_pt.Points[_pick_point_index].NumericalArgument = x; _series_edit_pt.Points[_pick_point_index].Values[0] = y; SetPointValue(_pick_point_index, x, y); } /// /// 更新数据 /// private void SetPointValue(int index, double x, double y) { if (IsInvalidData()) return; _def_pt_list[index].X = x; _def_pt_list[index].Y = y; this.DefinePointChangedEvent?.Invoke(_def_pt_list); } #endregion #region Set /// /// 绑定数据 /// public void Clear() { _initial_data = false; _def_pt_list = null; _fit_pt_list = null; _coordinate = null; _series_edit_pt = null; this.chartControl1.BeginInit(); this.chartControl1.Series.Clear(); this.chartControl1.AnnotationRepository.Clear(); this.chartControl1.Legend.CustomItems.Clear(); this.chartControl1.EndInit(); } /// /// 绑定数据 /// public void SetBindingData(List def_pt_list, List fit_pt_list, string coordinate = null, Color? color = null) { _initial_data = false; _def_pt_list = def_pt_list?.Select(x => new Geometry.Point2d(x.X, x.Y)).ToList(); _fit_pt_list = fit_pt_list?.Select(x => new Geometry.Point2d(x.X, x.Y)).ToList(); _coordinate = UniversalCoordinate.ToModel(coordinate); _series_edit_pt = null; if (color == null) color = Color.DodgerBlue; this.chartControl1.BeginInit(); this.chartControl1.Series.Clear(); this.chartControl1.AnnotationRepository.Clear(); this.chartControl1.Legend.CustomItems.Clear(); if (IsInvalidData()) { _initial_data = false; this.chartControl1.EndInit(); return; } _series_edit_pt = AddPointSeries(color.Value, _axis_x, _axis_y, _default_pane, _def_pt_list); AddLineSeries(color.Value, _axis_x, _axis_y, _default_pane, _fit_pt_list); if (_coordinate == null) SetCoordinate(); SetChartAxis(); this.chartControl1.EndInit(); _initial_data = true; } //是否是无效数据 private bool IsInvalidData() { if (_def_pt_list == null || !_def_pt_list.Any()) { return true; } if (_fit_pt_list == null || !_fit_pt_list.Any()) { return true; } return false; } #region Add Chart Data private Series AddPointSeries(Color color, AxisXBase axis_x, AxisYBase axis_y, XYDiagramPaneBase pane, List pt_list) { if (pt_list == null || !pt_list.Any()) return null; var view = new DevExpress.XtraCharts.PointSeriesView(); view.PointMarkerOptions.Size = 8; view.PointMarkerOptions.Kind = MarkerKind.Circle; view.PointMarkerOptions.BorderColor = color; view.Color = color; view.AxisX = axis_x; view.AxisY = axis_y; view.EmptyPointOptions.Color = Color.Transparent; view.Pane = pane; var series_pt_list = new List(); for (int i = 0; i < pt_list.Count; i++) { var pt = pt_list[i]; var series_pt = new DevExpress.XtraCharts.SeriesPoint(pt.X, new double[] { pt.Y }); series_pt.Tag = i; series_pt_list.Add(series_pt); } var series = new DevExpress.XtraCharts.Series(); series.ShowInLegend = false; series.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.Numerical; series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.False; series.CrosshairEnabled = DefaultBoolean.False; series.CrosshairLabelVisibility = DefaultBoolean.False; series.ToolTipEnabled = DefaultBoolean.False; series.SeriesPointsSorting = SortingMode.None; series.Visible = true; series.View = view; series.CrosshairLabelPattern = "{A}"; series.Points.AddRange(series_pt_list.ToArray()); this.chartControl1.Series.Add(series); return series; } private void AddLineSeries(Color color, AxisXBase axis_x, AxisYBase axis_y, XYDiagramPaneBase pane, List pt_list, DashStyle dash = DashStyle.Solid) { if (pt_list == null || !pt_list.Any()) return; var view = new DevExpress.XtraCharts.LineSeriesView(); view.LineStyle.DashStyle = dash; view.LineStyle.LineJoin = System.Drawing.Drawing2D.LineJoin.Round; view.LineStyle.Thickness = 2; view.Color = color; view.EnableAntialiasing = DefaultBoolean.True; view.MarkerVisibility = DefaultBoolean.False; view.AxisX = axis_x; view.AxisY = axis_y; view.EmptyPointOptions.Color = Color.Transparent; view.Pane = pane; var series_pt_list = pt_list.Select(x => x.ToSeriesPoint()).ToArray(); var series = new DevExpress.XtraCharts.Series(); series.ShowInLegend = false; series.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.Numerical; series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.False; series.CrosshairEnabled = DefaultBoolean.False; series.ToolTipEnabled = DefaultBoolean.False; series.SeriesPointsSorting = SortingMode.None; series.Visible = true; series.View = view; series.Points.AddRange(series_pt_list); this.chartControl1.Series.Add(series); } #endregion #endregion #region Set Axis private double _minX, _maxX; private double _maxY = 0, _minY = 10000; /// /// 设置坐标 /// private void SetCoordinate() { if (IsInvalidData()) { InitialCoordinate(); return; } _minX = _def_pt_list.Min(x => x.X); _maxX = _def_pt_list.Max(x => x.X); _minY = _def_pt_list.Min(x => x.Y); _maxY = _def_pt_list.Max(x => x.Y); _minX = Math.Min(_fit_pt_list.Min(x => x.X), _minX); _maxX = Math.Min(_fit_pt_list.Max(x => x.X), _maxX); _minY = Math.Min(_fit_pt_list.Min(x => x.Y), _minY); _maxY = Math.Min(_fit_pt_list.Max(x => x.Y), _maxY); _coordinate = UniversalCoordinate.CalcCoordinate(_minX, _maxX, _minY, _maxY); if (_coordinate == null) return; if (_coordinate.CoordMinX + _coordinate.CoordSpaceX * this._coordinate.GridNumberX < _maxX * 1.05) { _coordinate.GridNumberX++; } } /// /// 设置图表轴 /// public void SetChartAxis() { if (_coordinate == null || IsInvalidData()) { _axis_x.Visibility = DefaultBoolean.False; _axis_x.GridLines.Visible = false; _axis_y.Visibility = DefaultBoolean.False; _axis_y.GridLines.Visible = false; return; } //计算刻度 X var axis_x_labels = new List(); var display_x = _coordinate.CoordMinX; for (int i = 0; i < _coordinate.GridNumberX + 1; i++) { axis_x_labels.Add(new CustomAxisLabel(display_x.ToString("N0"), display_x)); display_x = display_x + _coordinate.CoordSpaceX; } _axis_x.CustomLabels.Clear(); _axis_x.CustomLabels.AddRange(axis_x_labels.ToArray()); _axis_x.Visibility = DefaultBoolean.True; _axis_x.GridLines.Visible = true; //计算刻度 Y var axis_y_labels = new List(); var display_y = _coordinate.CoordMinY + _coordinate.CoordSpaceY * _coordinate.StartLineNoY; for (int i = _coordinate.StartLineNoY; i < _coordinate.EndLineNoY + 1; i++) { axis_y_labels.Add(new CustomAxisLabel(display_y.ToString(), display_y)); display_y = display_y + _coordinate.CoordSpaceY; } _axis_y.CustomLabels.Clear(); _axis_y.CustomLabels.AddRange(axis_y_labels.ToArray()); _axis_y.Visibility = DefaultBoolean.True; _axis_y.GridLines.Visible = true; _axis_x.SetAxisRange(_coordinate.CoordMinX, _coordinate.DispMaxX()); _axis_y.SetAxisRange(_coordinate.DispMinY(), _coordinate.DispMaxY()); } #endregion Calc } }