using DevExpress.Utils; using DevExpress.XtraCharts; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace IStation.WinFrmUI.Curve { /// /// /// public partial class QHPDCurveExpressChart : DevExpress.XtraEditors.XtraUserControl { public QHPDCurveExpressChart() { InitializeComponent(); InitialChart(); this.chartControl1.RuntimeHitTesting = true; } #region Private Variable private XYDiagram _mainChartDiagram; private AxisX _axisXQ; private AxisY _axisYQH; private ConstantLine _workPointLine; private ConstantLine _workHLine; private Series _seriesCurveQH, _seriesCurveQPD; private Series _seriesPointQH, _seriesPointQPD; private TextAnnotation _workPointTextAnnot; private Model.CurveExpress _curveExpressQH; private Model.CurveExpress _curveExpressQPD; private List _curvePointsQH; private List _curvePointsQPD; private List _definePointsQH; private List _definePointsQPD; private Model.CurveCoordinateParas _coordinateParas; private Model.GroupPoint _workPoint = new Model.GroupPoint(0, 0, 0, 0, 0); private bool _initialData = false; #endregion #region Public Variable /// /// 定义点是否可见 /// public bool DefinePointVisible { get => _definePointVisible; set { _definePointVisible = value; this.barCekDefinePointVisible.Checked = _definePointVisible; } } private bool _definePointVisible = false; /// /// 工作线是否可见 /// public bool LineVisible { get => _lineVisible; set { _lineVisible = value; this.barCekLineVisible.Checked = _lineVisible; } } private bool _lineVisible = false; #endregion #region Public Evnet public event Action OnCurveCoordinateParasChanged; #endregion #region Initial /// /// 初始化图表 /// private void InitialChart() { this.chartControl1.SetChartDisplay(); this.chartControl1.Legend.Visibility = DevExpress.Utils.DefaultBoolean.False; _mainChartDiagram = (XYDiagram)chartControl1.Diagram; _axisXQ = _mainChartDiagram.AxisX; _axisXQ.SetAxisXQDisplay(); _axisYQH = _mainChartDiagram.AxisY; _axisYQH.SetAxisYQHDisplay(); _workPointLine = (ConstantLine)_mainChartDiagram.AxisX.ConstantLines.GetElementByName("WorkPointLine"); _workPointLine.SetWorkPointLineDisplay(); _workHLine = (ConstantLine)_mainChartDiagram.AxisY.ConstantLines.GetElementByName("WorkHLine"); _workHLine.SetWorkHLineDisplay(); _seriesCurveQH = this.chartControl1.GetSeriesByName("SeriesCurveQH"); _seriesCurveQH.SetCurveQHDisplay(); _seriesCurveQPD = this.chartControl1.GetSeriesByName("SeriesCurveQPD"); _seriesCurveQPD.SetCurveQPDisplay(); _seriesPointQH = this.chartControl1.GetSeriesByName("SeriesPointQH"); _seriesPointQH.SetPointQHDisplay(); _seriesPointQPD = this.chartControl1.GetSeriesByName("SeriesPointQPD"); _seriesPointQPD.SetPointQPDisplay(); _workPointTextAnnot = this.chartControl1.AnnotationRepository[0] as TextAnnotation; _workPointTextAnnot.SetTextAnnoWorkPointDisplay(); _axisXQ.Visibility = DefaultBoolean.False; _axisXQ.GridLines.Visible = false; _axisYQH.Visibility = DefaultBoolean.False; _axisYQH.GridLines.Visible = false; _workPointLine.Visible = false; _workHLine.Visible = false; _workPointTextAnnot.Visible = false; _seriesPointQH.Visible = false; _seriesPointQPD.Visible = false; this.chartControl1.ObjectHotTracked += new DevExpress.XtraCharts.HotTrackEventHandler(this.chartControl1_ObjectHotTracked); 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.Resize += new System.EventHandler(this.chartControl1_Resize); } /// /// 初始化图表数据 /// public void InitialChartData() { _initialData = false; _curveExpressQH = null; _curveExpressQPD = null; _curvePointsQH = null; _curvePointsQPD = null; _definePointsQH = null; _definePointsQPD = null; _coordinateParas = null; _workPoint = new Model.GroupPoint(0, 0, 0, 0, 0); UpdateChart(false); } #endregion /// /// 绑定数据 /// /// /// /// /// public void SetBindingData( Model.CurveExpress curveQH, Model.CurveExpress curveQPD, Model.CurveCoordinateParas coordinateParas = null, bool calcCoordinate = false) { if (curveQH == null) { InitialChartData(); return; } List qhPoints, qpPoints = null; if (curveQH.DefinePoints != null) qhPoints = curveQH.DefinePoints; else qhPoints = curveQH.GetFitPoints(12); if (curveQPD != null) if (curveQPD?.DefinePoints != null) qpPoints = curveQPD.DefinePoints; else qpPoints = curveQPD.GetFitPoints(12); SetBindingData(curveQH, curveQPD, qhPoints, qpPoints, coordinateParas, calcCoordinate); } /// /// 绑定数据 /// /// /// /// /// /// /// /// /// public void SetBindingData( Model.CurveExpress curveQH, Model.CurveExpress curveQPD, List definePointsQH, List definePointsQPD, Model.CurveCoordinateParas coordinateParas = null, bool calcCoordinate = false) { if (curveQH == null) { InitialChartData(); return; } _initialData = true; _curveExpressQH = curveQH; _curveExpressQPD = curveQPD; _curvePointsQH = curveQH.GetFitPoints(12); _curvePointsQPD = curveQPD?.GetFitPoints(12); _definePointsQH = definePointsQH; _definePointsQPD = definePointsQPD; _coordinateParas = coordinateParas; UpdateChart(calcCoordinate); } /// /// 更新图表 /// /// 计算坐标 public void UpdateChart(bool calcCoordinate = false) { if (calcCoordinate || _coordinateParas == null) { //不强迫计算,就用上次更新的坐标系 CalcCoordinate(); } CalcChartAxis(false); CalcSeries(); CalcWorkPointByQ(); CalcTextAnchorPoint(); } #region Calc /// /// 计算坐标 /// public void CalcCoordinate() { if (_curvePointsQH == null || _curvePointsQH.Count < 4) { //设置成白板坐标 _coordinateParas = new Model.CurveCoordinateParas(); _coordinateParas.GridNumberX = 30; _coordinateParas.GridNumberY = 16; //显示的坐标线号 _coordinateParas.StartLineNoH = 10; _coordinateParas.EndLineNoH = 15; _coordinateParas.StartLineNoE = 0; _coordinateParas.EndLineNoE = 10; _coordinateParas.StartLineNoP = 2; _coordinateParas.EndLineNoP = 9; //坐标最小值和间隔 _coordinateParas.CoordMinQ = 0; _coordinateParas.CoordSpaceQ = 1000; _coordinateParas.CoordMinH = 10; _coordinateParas.CoordSpaceH = 100; _coordinateParas.CoordMinE = 0; _coordinateParas.CoordSpaceE = 100; _coordinateParas.CoordMinP = 10; _coordinateParas.CoordSpaceP = 100; return; } _coordinateParas = Model.CurveCoordinateParas.CalcCoordinate(_curveExpressQH,null,null); } /// /// 计算图表轴 /// public void CalcChartAxis(bool splitPanel) { if (_coordinateParas == null) { _axisXQ.Visibility = DefaultBoolean.False; _axisXQ.GridLines.Visible = false; _axisYQH.Visibility = DefaultBoolean.False; _axisYQH.GridLines.Visible = false; _workPointLine.Visible = false; _workHLine.Visible = false; _workPointTextAnnot.Visible = false; return; } var pointView = (PointSeriesView)_seriesPointQPD.View; var curveView = (SplineSeriesView)_seriesCurveQPD.View; //流量 if (_curvePointsQH != null) { //计算刻度 var labels = new List(); var disQ = _coordinateParas.CoordMinQ; for (int i = 0; i < _coordinateParas.GridNumberX + 1; i++) { labels.Add(new CustomAxisLabel(disQ.ToString("N0"), disQ)); disQ = disQ + _coordinateParas.CoordSpaceQ; } //坐标刻度 _axisXQ.CustomLabels.Clear(); _axisXQ.CustomLabels.AddRange(labels.ToArray()); _axisXQ.Visibility = DefaultBoolean.True; _axisXQ.GridLines.Visible = true; _axisXQ.SetAxisRange(_coordinateParas.CoordMinQ, _coordinateParas.CoordMinQ + _coordinateParas.GridNumberX * _coordinateParas.CoordSpaceQ); _workPointTextAnnot.Visible = _lineVisible; } //扬程 if (_curvePointsQH != null) { //计算刻度 var labels = new List(); var disH = _coordinateParas.CoordMinH + _coordinateParas.CoordSpaceH * _coordinateParas.StartLineNoH; for (int i = _coordinateParas.StartLineNoH; i < _coordinateParas.EndLineNoH + 1; i++) { labels.Add(new CustomAxisLabel(disH.ToString(), disH)); disH = disH + _coordinateParas.CoordSpaceH; } _axisYQH.CustomLabels.Clear(); _axisYQH.CustomLabels.AddRange(labels.ToArray()); _axisYQH.Visibility = DefaultBoolean.True; _axisYQH.GridLines.Visible = true; } //功率 if (_curvePointsQPD != null) { //计算刻度 var labels = new List(); double disP = _coordinateParas.CoordMinP + _coordinateParas.CoordSpaceP * _coordinateParas.StartLineNoP; for (int i = _coordinateParas.StartLineNoP; i < _coordinateParas.EndLineNoP + 1; i++) { labels.Add(new CustomAxisLabel(disP.ToString(), disP)); disP = disP + _coordinateParas.CoordSpaceP; } } //是否分割面板:流量效率在上,功率在下 if (splitPanel) { var gridNumH = _coordinateParas.EndLineNoH - _coordinateParas.StartLineNoH; var gridNumE = _coordinateParas.EndLineNoE - _coordinateParas.StartLineNoE; int gridNumUp = Math.Max(gridNumH, gridNumE) + 2;//多两条 var maxAxisH = _coordinateParas.CoordMinH + _coordinateParas.EndLineNoH * _coordinateParas.CoordSpaceH; var minAxisH = maxAxisH - gridNumUp * _coordinateParas.CoordSpaceH; _axisYQH.SetAxisRange(minAxisH, maxAxisH); } else { _axisXQ.SetAxisRange(_coordinateParas.CoordMinQ, _coordinateParas.CoordMinQ + _coordinateParas.GridNumberX * _coordinateParas.CoordSpaceQ); _axisYQH.SetAxisRange(_coordinateParas.DispMinH(), _coordinateParas.DispMaxH()); pointView.Pane = _mainChartDiagram.DefaultPane; curveView.Pane = _mainChartDiagram.DefaultPane; } } /// /// 计算系列 /// public void CalcSeries() { if (_curvePointsQH != null && _curvePointsQH.Count > 3) { _seriesCurveQH.Visible = true; _seriesCurveQH.Points.Clear(); foreach (var curvePoint in _curvePointsQH) { var seriesPoint = new SeriesPoint(curvePoint.X, curvePoint.Y); _seriesCurveQH.Points.Add(seriesPoint); } } else { _seriesCurveQH.Points.Clear(); _seriesCurveQH.Visible = false; _workPointLine.Visible = false; _workHLine.Visible = false; _workPointTextAnnot.Visible = false; } if (_curvePointsQPD != null && _curvePointsQPD.Count > 3) { _seriesCurveQPD.Visible = true; _seriesCurveQPD.Points.Clear(); foreach (var curvePoint in _curvePointsQPD) { var seriesPoint = new SeriesPoint(curvePoint.X, curvePoint.Y); _seriesCurveQPD.Points.Add(seriesPoint); } } else { _seriesCurveQPD.Points.Clear(); _seriesCurveQPD.Visible = false; } if (_definePointsQH != null && _definePointsQH.Any()) { _seriesPointQH.Points.Clear(); foreach (var definePoint in _definePointsQH) { var seriesPoint = new SeriesPoint(definePoint.X, definePoint.Y); _seriesPointQH.Points.Add(seriesPoint); } } if (_definePointsQPD != null && _definePointsQPD.Any()) { _seriesPointQPD.Points.Clear(); foreach (var definePoint in _definePointsQPD) { var seriesPoint = new SeriesPoint(definePoint.X, definePoint.Y); _seriesPointQPD.Points.Add(seriesPoint); } } } /// /// 计算注释定位 /// private void CalcTextAnchorPoint() { var x = this.chartControl1.Location.X + this.chartControl1.Width - (100); var y = this.chartControl1.Height - (200); (_workPointTextAnnot.AnchorPoint as ChartAnchorPoint).X = x; (_workPointTextAnnot.AnchorPoint as ChartAnchorPoint).Y = y; } /// /// 计算工作点 /// /// public void CalcWorkPointByQ(double? workQ = null) { if (!_lineVisible) { _workPointLine.Visible = false; _workPointLine.Title.Visible = false; _workHLine.Visible = false; _workHLine.Title.Visible = false; _workPointTextAnnot.Visible = false; return; } else { _workPointLine.Visible = true; _workPointLine.Title.Visible = true; _workHLine.Visible = true; _workHLine.Title.Visible = true; _workPointTextAnnot.Visible = true; } if (_curveExpressQH == null) return; var minQ = _curvePointsQH.Min(x => x.X); var maxQ = _curvePointsQH.Max(x => x.X); if (workQ == null) { workQ = (minQ + maxQ) / 2; } else { if (workQ < minQ || workQ > maxQ) return; } _workPoint.Q = workQ.Value; _workPoint.H = Model.FitCurveHelper.GetFitPointY(_curveExpressQH, _workPoint.Q); var workInfoStringBuilder = new StringBuilder(); workInfoStringBuilder.AppendLine($"流量:{_workPoint.Q.ToString("N1")} "); workInfoStringBuilder.AppendLine($"扬程:{_workPoint.H.ToString("N1")} "); if (_curveExpressQPD != null) { _workPoint.P = Model.FitCurveHelper.GetFitPointY(_curveExpressQPD, _workPoint.Q); workInfoStringBuilder.Append($"压差:{_workPoint.P.ToString("N1")} "); } _workPointLine.AxisValue = _workPoint.Q; _workPointLine.Title.Text = _workPoint.Q.ToString("N1"); _workHLine.AxisValue = _workPoint.H; _workHLine.Title.Text = _workPoint.H.ToString("N1"); //测试展示效果 _workPointTextAnnot.Text = workInfoStringBuilder.ToString(); _workPointTextAnnot.AutoSize = true; } /// /// 根据扬程计算工作点 /// /// public void CalcWorkPointByH(double workH) { if (!_lineVisible) return; if (_curveExpressQH == null) return; var minH = _curvePointsQH.Min(x => x.Y); var maxH = _curvePointsQH.Max(x => x.Y); if (workH < minH || workH > maxH) return; var InterPoints = Model.FitCurveHelper.GetInterPointX(_curveExpressQH, workH); if (InterPoints == null || InterPoints.Count == 0) return; var workQ = InterPoints.Last().X; CalcWorkPointByQ(workQ); } /// /// 根据最大流量计算工作点 /// public void CalcWorkPointByMaxQ() { if (!_lineVisible) return; if (_curveExpressQH == null) return; var workQ = _curveExpressQH.Max; CalcWorkPointByQ(workQ); } #endregion #region ChartEvent ToolTipController toolTip = new ToolTipController(); private void chartControl1_ObjectHotTracked(object sender, HotTrackEventArgs e) { if (!_initialData) return; if (e.AdditionalObject is SeriesPoint seriesPoint) { var tip = string.Format("X:{0:N1} Y:{1:N1}", seriesPoint.Argument, seriesPoint.Values[0]); toolTip.ShowHint(tip); } else { toolTip.HideHint(); } } // 右键对象 private object _rightClickObj = null; private bool _onMoveWorkPointLine = false; private bool _onMoveWorkHLine = false; private void chartControl1_MouseDown(object sender, MouseEventArgs e) { if (!_initialData) return; var hitInfo = chartControl1.CalcHitInfo(e.Location); if (e.Button == MouseButtons.Left) { if (hitInfo.InSeries) { _rightClickObj = hitInfo.Series; } else if (hitInfo.InAxis) { _rightClickObj = hitInfo.Axis; } else if (hitInfo.InConstantLine) { if (hitInfo.ConstantLine == _workPointLine) { _onMoveWorkPointLine = true; } else if (hitInfo.ConstantLine == _workHLine) { _onMoveWorkHLine = true; } } else if (hitInfo.InAnnotation) { _rightClickObj = hitInfo.Annotation; } else { _rightClickObj = null; } } else if (e.Button == MouseButtons.Right) { if (hitInfo.InConstantLine) { this.popMenuLine.ShowPopup(MousePosition); } else { this.popMenuChart.ShowPopup(MousePosition); } } } private void chartControl1_MouseMove(object sender, MouseEventArgs e) { if (!_initialData) return; if (_onMoveWorkPointLine) { var diagramCoordinates = _mainChartDiagram.PointToDiagram(e.Location); var axisValue = diagramCoordinates.GetAxisValue(_axisXQ); if (axisValue == null) return; double chartQ = axisValue.NumericalValue; CalcWorkPointByQ(chartQ); } else if (_onMoveWorkHLine) { var diagramCoordinates = _mainChartDiagram.PointToDiagram(e.Location); var axisValue = diagramCoordinates.GetAxisValue(_axisYQH); if (axisValue == null) return; double chartH = axisValue.NumericalValue; CalcWorkPointByH(chartH); } } private void chartControl1_MouseUp(object sender, MouseEventArgs e) { if (!_initialData) return; _onMoveWorkPointLine = false; _onMoveWorkHLine = false; } private void chartControl1_Resize(object sender, EventArgs e) { CalcTextAnchorPoint(); } #endregion #region Right Click Menu #region Event private void barBtnSetAxisQValue_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (_curvePointsQH == null || !_curvePointsQH.Any()) return; var dlg = new AxisValueDlg(); dlg.SetBindingData(); dlg.VerifyValueChanged += (value) => { var min = _curvePointsQH.Min(x => x.X); var max = _curvePointsQH.Max(x => x.X); if (value < min || value > max) return false; CalcWorkPointByQ(value); return true; }; dlg.ShowDialog(); } private void barBtnSetAxisHValue_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (_curvePointsQH == null || !_curvePointsQH.Any()) return; var dlg = new AxisValueDlg(); dlg.SetBindingData(); dlg.VerifyValueChanged += (value) => { var min = _curvePointsQH.Min(x => x.Y); var max = _curvePointsQH.Max(x => x.Y); if (value < min || value > max) return false; CalcWorkPointByH(value); return true; }; dlg.ShowDialog(); } private void barBtnPositioningMaxQ_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { CalcWorkPointByMaxQ(); } private void barBtnPositioningMaxE_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { } private void barCekDefinePointVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetDefinePointVisible(this.barCekDefinePointVisible.Checked); } private void barCekLineVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetLineVisible(this.barCekLineVisible.Checked); } private void barCekLegendVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetLegendVisible(this.barCekLegendVisible.Checked); } private void barCekSetAxisNameVisible_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetAxisNameVisible(this.barCekSetAxisNameVisible.Checked); } private void barCekChartDisplay_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetChartDisplay(this.barCekChartDisplay.Checked); } private void barBtnSetChartAxis_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { SetChartAxis(); } private void barBtnExportImage_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ExportImage(); } #endregion /// /// 设置定义点显示 /// public void SetDefinePointVisible(bool visible) { _seriesPointQH.Visible = visible; _seriesPointQPD.Visible = visible; } /// /// 设置工作点显示 /// public void SetLineVisible(bool visible) { if (!_initialData) return; _lineVisible = visible; CalcWorkPointByQ(); } /// /// 设置图例显示 /// public void SetLegendVisible(bool visible) { this.chartControl1.Legend.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; } /// /// 设置轴名称显示 /// public void SetAxisNameVisible(bool visible) { _axisXQ.Title.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; _axisYQH.Title.Visibility = visible ? DefaultBoolean.True : DefaultBoolean.False; } /// /// 设置图表显示 /// public void SetChartDisplay(bool monoColor) { if (monoColor) { this.chartControl1.SetChartMonoColorDisplay(); } else { this.chartControl1.SetChartBackColor(); _axisXQ.SetAxisYQColorDisplay(); _axisYQH.SetAxisYQHColorDisplay(_seriesCurveQH, _seriesPointQH, true); } } /// /// 设置坐标轴 /// public void SetChartAxis() { var dlg = new ChartCoordinateDlg(); var onlyQH = true; dlg.SetBindingData(_coordinateParas, onlyQH); dlg.OnChangedCoord += (rhs) => { _coordinateParas = rhs; CalcChartAxis(false); this.OnCurveCoordinateParasChanged?.Invoke(_coordinateParas); }; dlg.ShowDialog(); } /// /// 导出图片 /// public void ExportImage() { var dlg = new SaveFileDialog(); dlg.Filter = "jpg 图片(*.jpg)|*.jpg"; if (dlg.ShowDialog() != DialogResult.OK) return; this.chartControl1.ExportToImage(dlg.FileName, System.Drawing.Imaging.ImageFormat.Jpeg); } #endregion } }