HitTest()的替代方案
本文关键字:方案 HitTest | 更新日期: 2023-09-27 18:23:56
目前,我的软件在MSCharts中使用图表对象的HitTest()
方法,但随着我将其扩展到图表上越来越多的数据点,再加上其他因素,这可能会对性能产生巨大影响。
我想知道是否有其他选择可以提供相同的功能(获取图表上光标位置的X坐标),但没有性能打击,因为打击测试似乎是获得我答案的一种非常暴力的方式。
我的图表是从类System.Windows.Forms.DataVisualization.Charting.Chart
创建的
为了清晰起见,编辑:我需要在图表上找到一条线的位置,以便用于其他计算。
在鼠标滚轮事件中也有同样的性能问题。
这是我的解决方案:
-
要获取当前鼠标位置的轴值:
double posX = Math.Round(currentArea.AxisX.PixelPositionToValue(e.X)); double posY = Math.Round(currentArea.AxisY.PixelPositionToValue(e.Y));
取自图表控件上的显示鼠标轴坐标,稍作更改以使其更准确。
但您之前应该检查鼠标是否在图表区域中,否则它会向您抛出异常。
-
获取鼠标指向的聊天元素:
// Gets the ChartArea that the mouse points private ChartArea mouseinChartArea(Chart source, Point e) { double relativeX = (double)e.X * 100 / source.Width; double relativeY = (double)e.Y * 100 / source.Height; foreach (ChartArea ca in source.ChartAreas) { if (relativeX > ca.Position.X && relativeX < ca.Position.Right && relativeY > ca.Position.Y && relativeY < ca.Position.Bottom) return ca; } return null; } // for my purpose, returns an axis. But you can return anything private Axis findAxisforZooming(Chart source, Point e) { ChartArea currentArea = mouseinChartArea(source, new Point(e.X, e.Y)); // Check if inside if (currentArea == null) return null; double axisXfontSize = currentArea.AxisX.LabelAutoFitMinFontSize + ((double)source.Width / SystemInformation.PrimaryMonitorSize.Width) * (currentArea.AxisX.LabelAutoFitMaxFontSize - currentArea.AxisX.LabelAutoFitMinFontSize); double axisYfontSize = currentArea.AxisY.LabelAutoFitMinFontSize + ((double)source.Height / SystemInformation.PrimaryMonitorSize.Height) * (currentArea.AxisY.LabelAutoFitMaxFontSize - currentArea.AxisY.LabelAutoFitMinFontSize); double axisYfontHeightSize = (axisYfontSize - currentArea.AxisY.LabelStyle.Font.Size) + currentArea.AxisY.LabelStyle.Font.Height; Graphics g = this.CreateGraphics(); if (currentArea.AxisX.LabelStyle.Font.Unit == GraphicsUnit.Point) axisXfontSize = axisXfontSize * g.DpiX / 72; if (currentArea.AxisY.LabelStyle.Font.Unit == GraphicsUnit.Point) axisYfontHeightSize = axisYfontHeightSize * g.DpiX / 72; g.Dispose(); // Replacing the SystemInformation.PrimaryMonitorSize with the source.Width / Height will give the accurate TickMarks size. // But it doens't count for the gab between the tickMarks and the axis lables (so by replacing, it give a good proximity with the gab) int axisYTickMarks = (int)Math.Round(currentArea.AxisY.MajorTickMark.Size / 100 * SystemInformation.PrimaryMonitorSize.Width); // source.Width; int axisXTickMarks = (int)Math.Round(currentArea.AxisX.MajorTickMark.Size / 100 * SystemInformation.PrimaryMonitorSize.Height); // source.Height; int leftInnerPlot = (int)Math.Round(currentArea.Position.X / 100 * source.Width + currentArea.InnerPlotPosition.X / 100 * currentArea.Position.Width / 100 * source.Width); int rightInnerPlot = (int)Math.Round(currentArea.Position.X / 100 * this.chart1.Width + currentArea.InnerPlotPosition.Right / 100 * currentArea.Position.Width / 100 * source.Width); int topInnerPlot = (int)Math.Round(currentArea.Position.Y / 100 * this.chart1.Height + currentArea.InnerPlotPosition.Y / 100 * currentArea.Position.Height / 100 * source.Height); int bottomInnerPlot = (int)Math.Round(currentArea.Position.Y / 100 * source.Height + currentArea.InnerPlotPosition.Bottom / 100 * currentArea.Position.Height / 100 * source.Height); // Now you got the boundaries of every important ChartElement. // Only left to check if the mouse is within your desire ChartElement, // like the following: bottomInnerPlot += axisXTickMarks + (int)Math.Round(axisXfontSize); // Include AxisX if (e.X > leftInnerPlot && e.X < rightInnerPlot && e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisX if inside the InnerPlot area or on AxisX return currentArea.AxisX; else if (e.X > (leftInnerPlot - axisYTickMarks - (int)Math.Round(axisYfontHeightSize)) && e.X < rightInnerPlot && e.Y > topInnerPlot && e.Y < bottomInnerPlot) // return AxisY if on AxisY only return currentArea.AxisY; return null; }
可以看出,该代码比HitTest()
更长。但运行时间较短。