Reputation: 33
I am using a circular Marker to indicate the position of data points. When the points are drawn on an axis I cannot select them (using HitTest)... but I can select them when they are drawn anywhere else on the chart. The unselectable points appear to be drawing under the axis. If I make the markers larger then I can select the points on the axis by clicking at the edge of the circle.
Is there a way to get the markers to draw on top of the axis (if that's the problem!)?
Upvotes: 3
Views: 107
Reputation: 54453
This is a really good question.
The problem is indeed real I don't know a simple solution.
You can modify the X
you test at when you find that you have hit the Axis
or an AxisLabel
or a TickMark
. Calculate the position mirrored at the axis ond offset by its width and test again.
For this you need to know the axis position in pixels and its width.
The latter is axis.Linewidth
. For the former use the axis function ax.ValueToPixelPosition
. It takes the x-value at the y-axis position. This will either be x-axis' Minimum (if it is set) or 0
or some automatic value, depending on your data.
This does improve the situation although I found that TickMarks still tend to get in the way, not sure why.
Users would love if the point the mouse is really near, would grow until they go away again. You can use the reverse function PixelPositionToValue
and some Linq to find the closest point.
Update:
Here is an example of a function to find the closest point, maybe in the MouseMove
event or maybe after the HitTest
has hit an axis or axis element..:
DataPoint GetNearestPoint(Series s, ChartArea ca, Point pt)
{
int limit = s.MarkerSize * 4; // pick a suitable distance!
Axis ay = ca.AxisY;
Axis ax = ca.AxisX;
var mins = s.Points.Cast<DataPoint>().Select((x, index) =>
new
{
val = Math.Abs(pt.Y - (int)ay.ValueToPixelPosition(x.YValues[0]))
+ Math.Abs(pt.X - (int)ax.ValueToPixelPosition(x.XValue)),
ix = index
});
var min = mins.Where(x => x.val == mins.Min(v => v.val)).Select(x => x.ix).First();
if (mins.ElementAt(min).val > limit) return null;
else return s.Points[min];
}
Here is how I call it after creating a class level variable:
DataPoint lastHit = null;
It colors the closest point while the cursor is close enough..:
var nearest = GetNearestPoint(someSeries, someChart.ChartAreas[0], e.Location);
if (lastHit != null) lastHit.MarkerColor = someSeries.Color;
if (nearest != null)
{
lastHit = nearest;
nearest.MarkerColor = Color.Red;
}
Enlarging the MarkerSize
is if course just as simple (at least unless your ChartType
is Bubble
, as is mine ;-)
ElementPositions
of the original and also of its InnerPlotPosition
and then use them for the overlay. Now you could maybe move the series to the 2nd, upper ChartArea
and also turn its Axis
etc off.. The HitTest
should work as expected now. But the positions need to be adjusted upon each resize.Upvotes: 2