C# HitTest()的替代方案
目前,我的软件使用MSCharts中图表对象的C# HitTest()的替代方案,c#,visual-studio-2012,charts,C#,Visual Studio 2012,Charts,目前,我的软件使用MSCharts中图表对象的HitTest()方法,但当我将其放大到图表上越来越多的数据点时,再加上其他因素,这可能会对性能造成巨大影响 我想知道您是否知道有任何替代方案可以提供相同的功能(在图表上获取光标位置的X坐标),但没有性能命中,因为命中测试似乎是获取我答案的一种非常暴力的方式 我的图表是从类System.Windows.Forms.DataVisualization.Charting.chart 为清晰起见进行编辑:我需要在图表上找到一条线的位置,以便将其用于其他计算
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;
}
//获取鼠标指向的图表区域
专用图表区鼠标区(图表源,e点)
{
double relativeX=(double)e.X*100/源宽度;
双相对性=(双)e.Y*100/源高度;
foreach(source.ChartAreas中的ChartArea ca)
{
如果(relativeX>ca.Position.X&&relativeXca.Position.Y&&relativeYleftInnerPlot&&e.XtopInnerPlot&&e.Y(leftInnerPlot-axisYTickMarks-(int)Math.Round(axisYfontHeightSize))&&e.XtopInnerPlot&&e.Y
可以看出,代码比
HitTest()长。
。但是运行时间较短HitTest()函数只能在鼠标事件处理代码中使用。在人类时间运行,燃烧几十毫秒不是问题。如果需要更长的时间,那么你只是在图表中填充了太多的数据,远远超过了它所能显示的细节。因此,可以更好地过滤数据。图表需要数据量,HitTest用于确定光标旁边显示的图表信息,以便用户可以在图表上运行鼠标,并在鼠标位置有效地查看准确的数据。虽然一个解决方案可能是采用一组平均数据点,而不是使用这么多数据点,但精度损失并不理想。鼠标只有像素精度。因此,您所需的数据点永远不会超过屏幕上的像素数。也要考虑对你想要的特性意味着什么,用户永远看不到图中重叠的两个点的数据。最后但并非最不重要的一点是,您可以轻松确保