using DevExpress.Utils; using DevExpress.XtraCharts; using NPOI.SS.Formula.Functions; namespace Yw.WinFrmUI.Phart { /// /// /// public partial class ValveEditChart : DevExpress.XtraEditors.XtraUserControl { public ValveEditChart() { InitializeComponent(); InitialChart(); } #region Private Variable private XYDiagram _diagram; private XYDiagramDefaultPane _default_pane; private XYDiagramPane _bottom_pane; private AxisX _axis_x_flow; private AxisY _axis_y_head_loss; private SecondaryAxisX _axis_x_opening; private SecondaryAxisY _axis_y_k_loss; private Series _series_edit_pt; private Yw.Ahart.eCurveType _edit_curve_type = Ahart.eCurveType.QL; private ValveCoordinate _coordinate; private bool _initial_data = false; private bool _mouse_mode = false;//鼠标模式 private bool _exist_ql = false; private bool _exist_ol = false; private List _vm_list = null; #endregion #region Public Variable /// /// 鼠标模式 /// 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)this.chartControl1.Diagram; _default_pane = _diagram.DefaultPane; _bottom_pane = _diagram.Panes[0]; _axis_x_flow = _diagram.AxisX; _axis_x_flow.SetAxisXQDisplay(); _axis_y_head_loss = _diagram.AxisY; _axis_y_head_loss.SetAxisYQHDisplay(); _axis_x_flow.Visibility = DefaultBoolean.False; _axis_x_flow.GridLines.Visible = false; _axis_y_head_loss.Visibility = DefaultBoolean.False; _axis_y_head_loss.GridLines.Visible = false; _axis_x_opening = _diagram.SecondaryAxesX[0]; _axis_x_opening.SetAxisXQDisplay(); _axis_y_k_loss = _diagram.SecondaryAxesY[0]; _axis_y_k_loss.SetSecondaryAxisYQPDisplay(); _axis_x_opening.Visibility = DefaultBoolean.False; _axis_x_opening.GridLines.Visible = false; _axis_y_k_loss.Visibility = DefaultBoolean.False; _axis_y_k_loss.GridLines.Visible = false; this.chartControl1.SetChartMonoColorDisplay(); } /// /// 初始化坐标轴 /// private void InitialCoordinate() { //设置成白板坐标 _coordinate = new ValveCoordinate(); _coordinate.QL = new(); _coordinate.OL = new(); _coordinate.QL.GridNumberX = 30; _coordinate.QL.GridNumberY = 16; _coordinate.QL.StartLineNoY = 10; _coordinate.QL.EndLineNoY = 15; _coordinate.QL.CoordMinX = 0; _coordinate.QL.CoordSpaceX = 1000; _coordinate.QL.CoordMinY = 10; _coordinate.QL.CoordSpaceY = 100; _coordinate.OL.GridNumberX = 30; _coordinate.OL.GridNumberY = 16; _coordinate.OL.StartLineNoY = 10; _coordinate.OL.EndLineNoY = 15; _coordinate.OL.CoordMinX = 0; _coordinate.OL.CoordSpaceX = 1000; _coordinate.OL.CoordMinY = 10; _coordinate.OL.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.InSeries && hitInfo.Series != _series_edit_pt) { return; } 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); AxisYBase axis_y = null; if (_edit_curve_type == Ahart.eCurveType.QL) { axis_y = _axis_y_head_loss; } else { axis_y = _axis_y_k_loss; } 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 coord_space_x, coord_space_y; if (_edit_curve_type == Ahart.eCurveType.QL) { coord_space_x = _coordinate.QL.CoordSpaceX; coord_space_y = _coordinate.QL.CoordSpaceY; } else { coord_space_x = _coordinate.OL.CoordSpaceX; coord_space_y = _coordinate.OL.CoordSpaceY; } double space_x = coord_space_x / 50; double space_y = coord_space_y / 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; var vm = _vm_list.Find(x => x.IsUpdate); if (vm == null) return; vm.DefPointList[index].X = x; vm.DefPointList[index].Y = y; this.DefinePointChangedEvent?.Invoke(vm.DefPointList); } #endregion #region Set /// /// 绑定数据 /// public void Clear() { _initial_data = false; _coordinate = null; _series_edit_pt = null; _vm_list = null; _exist_ql = false; _exist_ol = false; this.chartControl1.BeginInit(); this.chartControl1.Series.Clear(); this.chartControl1.AnnotationRepository.Clear(); this.chartControl1.Legend.CustomItems.Clear(); this.chartControl1.EndInit(); } /// /// 绑定数据 /// public void SetBindingData(List vm_list, string coordinate = null) { _initial_data = false; _coordinate = ValveCoordinate.ToModel(coordinate); _series_edit_pt = null; _vm_list = vm_list; _exist_ql = false; _exist_ol = false; 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; } AxisXBase axis_x = null; AxisYBase axis_y = null; XYDiagramPaneBase pane = null; foreach (var vm in _vm_list) { if (vm.CurveType == Ahart.eCurveType.QL) { axis_x = _axis_x_flow; axis_y = _axis_y_head_loss; pane = _default_pane; _exist_ql = true; } else if (vm.CurveType == Ahart.eCurveType.OL) { axis_x = _axis_x_opening; axis_y = _axis_y_k_loss; pane = _bottom_pane; _exist_ol = true; } if (vm.IsUpdate) { _edit_curve_type = vm.CurveType; _series_edit_pt = AddPointSeries(Color.Red, axis_x, axis_y, pane, vm.DefPointList); AddLineSeries(Color.Red, axis_x, axis_y, pane, vm.FitPointList); } else { AddPointSeries(Color.Black, axis_x, axis_y, pane, vm.DefPointList); AddLineSeries(Color.Black, axis_x, axis_y, pane, vm.FitPointList); } } if (_coordinate == null) SetCoordinate(); SetChartAxis(); this.chartControl1.EndInit(); _initial_data = true; } //是否是无效数据 private bool IsInvalidData() { if (_vm_list == null || !_vm_list.Any()) { return true; } if (!_vm_list.Exists(x => x.IsUpdate)) { 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 _min_x_flow, _max_x_flow; private double _max_y_head_loss = 0, _min_y_head_loss = 10000; private double _min_x_opening, _max_x_opening; private double _max_y_k_loss = 0, _min_y_k_loss = 10000; /// /// 设置坐标 /// private void SetCoordinate() { InitialCoordinate(); if (IsInvalidData()) { return; } _max_x_flow = 0; _min_x_flow = 100000; _max_x_opening = 0; _min_x_opening = 100000; _max_y_head_loss = 0; _min_y_head_loss = 10000; _max_y_k_loss = 0; _min_y_k_loss = 10000; foreach (var vm in _vm_list) { if (vm.DefPointList == null || !vm.DefPointList.Any()) { continue; } if (vm.CurveType == Ahart.eCurveType.QL) { _min_x_flow = Math.Min(vm.DefPointList.Min(x => x.X), _min_x_flow); _max_x_flow = Math.Max(vm.DefPointList.Max(x => x.X), _max_x_flow); _min_y_head_loss = Math.Min(vm.DefPointList.Min(x => x.Y), _min_y_head_loss); _max_y_head_loss = Math.Max(vm.DefPointList.Max(x => x.Y), _max_y_head_loss); _min_x_flow = Math.Min(vm.FitPointList.Min(x => x.X), _min_x_flow); _max_x_flow = Math.Max(vm.FitPointList.Max(x => x.X), _max_x_flow); _min_y_head_loss = Math.Min(vm.FitPointList.Min(x => x.Y), _min_y_head_loss); _max_y_head_loss = Math.Max(vm.FitPointList.Max(x => x.Y), _max_y_head_loss); } else if (vm.CurveType == Ahart.eCurveType.OL) { _min_x_opening = Math.Min(vm.DefPointList.Min(x => x.X), _min_x_opening); _max_x_opening = Math.Max(vm.DefPointList.Max(x => x.X), _max_x_opening); _min_y_k_loss = Math.Min(vm.DefPointList.Min(x => x.Y), _min_y_k_loss); _max_y_k_loss = Math.Max(vm.DefPointList.Max(x => x.Y), _max_y_k_loss); _min_x_opening = Math.Min(vm.FitPointList.Min(x => x.X), _min_x_opening); _max_x_opening = Math.Max(vm.FitPointList.Max(x => x.X), _max_x_opening); _min_y_k_loss = Math.Min(vm.FitPointList.Min(x => x.Y), _min_y_k_loss); _max_y_k_loss = Math.Max(vm.FitPointList.Max(x => x.Y), _max_y_k_loss); } } _coordinate.QL = ValveQLCoordinate.CalcCoordinate(_min_x_flow, _max_x_flow, _min_y_head_loss, _max_y_head_loss); if (_coordinate.QL != null) { if (_coordinate.QL.CoordMinX + _coordinate.QL.CoordSpaceX * this._coordinate.QL.GridNumberX < _max_x_flow * 1.05) { _coordinate.QL.GridNumberX++; } } _coordinate.OL = ValveOLCoordinate.CalcCoordinate(_min_x_opening, _max_x_opening, _min_y_k_loss, _max_y_k_loss); } /// /// 设置图表轴 /// public void SetChartAxis() { if (_coordinate == null || IsInvalidData()) { _axis_x_flow.Visibility = DefaultBoolean.False; _axis_x_flow.GridLines.Visible = false; _axis_y_head_loss.Visibility = DefaultBoolean.False; _axis_y_head_loss.GridLines.Visible = false; _axis_x_opening.Visibility = DefaultBoolean.False; _axis_x_opening.GridLines.Visible = false; _axis_y_k_loss.Visibility = DefaultBoolean.False; _axis_y_k_loss.GridLines.Visible = false; return; } if (_exist_ql) { //计算刻度 x_flow var axis_x_flow_labels = new List(); var display_x_flow = _coordinate.QL.CoordMinX; for (int i = 0; i < _coordinate.QL.GridNumberX + 1; i++) { var x = _coordinate.QL.CoordSpaceX < 1 ? $"{display_x_flow:N1}" : $"{display_x_flow:N0}"; axis_x_flow_labels.Add(new CustomAxisLabel(x, display_x_flow)); display_x_flow = display_x_flow + _coordinate.QL.CoordSpaceX; } _axis_x_flow.CustomLabels.Clear(); _axis_x_flow.CustomLabels.AddRange(axis_x_flow_labels.ToArray()); _axis_x_flow.Visibility = DefaultBoolean.True; _axis_x_flow.GridLines.Visible = true; //计算刻度 y_head_loss var axis_y_head_loss_labels = new List(); var display_y_head_loss = _coordinate.QL.CoordMinY + _coordinate.QL.CoordSpaceY * _coordinate.QL.StartLineNoY; for (int i = _coordinate.QL.StartLineNoY; i < _coordinate.QL.EndLineNoY + 1; i++) { var y = _coordinate.QL.CoordSpaceY < 1 ? $"{display_y_head_loss:N1}" : $"{display_y_head_loss:N0}"; axis_y_head_loss_labels.Add(new CustomAxisLabel(y, display_y_head_loss)); display_y_head_loss = display_y_head_loss + _coordinate.QL.CoordSpaceY; } _axis_y_head_loss.CustomLabels.Clear(); _axis_y_head_loss.CustomLabels.AddRange(axis_y_head_loss_labels.ToArray()); _axis_y_head_loss.Visibility = DefaultBoolean.True; _axis_y_head_loss.GridLines.Visible = true; _axis_x_flow.SetAxisRange(_coordinate.QL.CoordMinX, _coordinate.QL.DispMaxX()); _axis_y_head_loss.SetAxisRange(_coordinate.QL.DispMinY(), _coordinate.QL.DispMaxY()); _default_pane.Visibility = ChartElementVisibility.Visible; } else { _default_pane.Visibility = ChartElementVisibility.Hidden; } if (_exist_ol) { //计算刻度 x_opening var axis_x_opening_labels = new List(); var display_x_opening = _coordinate.OL.CoordMinX; for (int i = 0; i < _coordinate.OL.GridNumberX + 1; i++) { var x = _coordinate.OL.CoordSpaceX < 1 ? $"{display_x_opening:N1}" : $"{display_x_opening:N0}"; axis_x_opening_labels.Add(new CustomAxisLabel(x, display_x_opening)); display_x_opening = display_x_opening + _coordinate.OL.CoordSpaceX; } _axis_x_opening.CustomLabels.Clear(); _axis_x_opening.CustomLabels.AddRange(axis_x_opening_labels.ToArray()); _axis_x_opening.Visibility = DefaultBoolean.True; _axis_x_opening.GridLines.Visible = true; //计算刻度 y_k_loss var axis_y_k_loss_labels = new List(); var display_y_k_loss = _coordinate.OL.CoordMinY + _coordinate.OL.CoordSpaceY * _coordinate.OL.StartLineNoY; for (int i = _coordinate.OL.StartLineNoY; i < _coordinate.OL.EndLineNoY + 1; i++) { var y = _coordinate.OL.CoordSpaceY < 1 ? $"{display_y_k_loss:N1}" : $"{display_y_k_loss:N0}"; axis_y_k_loss_labels.Add(new CustomAxisLabel(y, display_y_k_loss)); display_y_k_loss = display_y_k_loss + _coordinate.OL.CoordSpaceY; } _axis_y_k_loss.CustomLabels.Clear(); _axis_y_k_loss.CustomLabels.AddRange(axis_y_k_loss_labels.ToArray()); _axis_y_k_loss.Visibility = DefaultBoolean.True; _axis_y_k_loss.GridLines.Visible = true; _axis_x_opening.SetAxisRange(_coordinate.OL.CoordMinX, _coordinate.OL.DispMaxX()); _axis_y_k_loss.SetAxisRange(_coordinate.OL.DispMinY(), _coordinate.OL.DispMaxY()); _bottom_pane.Visibility = ChartElementVisibility.Visible; } else { _bottom_pane.Visibility = ChartElementVisibility.Hidden; } } #endregion Calc } }