This question already has answers here:
get mark position in ms charts on mouse click
(2 answers)
Closed 6 years ago.
I'm working whit charts on c#.
I would like to know a way of get the iten (the spline) clicked on the chart to do something like change color or hide.
You can check the clicked item using HitTest, as suggested..:
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
HitTestResult result = chart1.HitTest(e.X, e.Y);
// now do what you want with the data..:
if (result.Series != null && result.PointIndex >= 0)
Text = "X = " + result.Series.Points[result.PointIndex].XValue +
" Y = " + result.Series.Points[result.PointIndex].YValues[0];
}
To help the user you can indicate a possible hit:
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
HitTestResult result = chart1.HitTest(e.X, e.Y);
Cursor = result.Series != null ? Cursors.Hand : Cursors.Default;
}
Do your users a favor by making the spline lines a little larger:
eachOfYourSeries.BorderWidth = 4;
Of course the chart you show makes a controlled clicking impossible without zooming in first..
Now if all you want is hiding a Series or changing its Color the HitTestResult is good enough:
result.Series.Enabled = flase;
or
result.Series.Color = Color.Fuchsia;
But to access the clicked values, it may not be..:
Note that especially a Spline or a Line graph, need only very few DataPoints to create their lines; so the result from HitTest simply is not enough; it will tell you which series was clicked and also the closest DataPoint but to get at the correct values you still may need to convert the pixels to data values.
So instead of the HitTest result you may prefer to use the handy function PixelPositionToValue:
float px = (float)yourChartArea.AxisX.PixelPositionToValue(e.X);
This will get all the values between the actual DataPoints. Of course you can do the same for the Y-Values:
float py = (float)yourChartArea.AxisY.PixelPositionToValue(e.Y);
Note that you can only use these function when the chart is not busy with determining the layout; officially this is only during any of the three Paint events. But I found that in practice a mouse event is also fine. You will get a null value when the chart is not ready..
Related
IsMouseOverMarker property detects clicking on marker just fine, but when trying to use IsMouseOverPolygon property of GMap Control to detect if user clicked on polygon line - it doesn't seem to be working.
Note: PolygonEnabled property of GMap control is set to True.
The OnPolygonClick event doesn't even fire:
private void gMap_OnPolygonClick(GMapPolygon item, MouseEventArgs e) {
double pLat = item.From.Value.Lat;
}
Map Click event does fire, but the 'IsMouseOverPolygon` never gets True value:
private void gMap_Click(object sender, EventArgs e) {
if (gMap.IsMouseOverMarker) {
MessageBox.Show("Clicked on marker and it works!");
}
if (gMap.IsMouseOverPolygon) {
MessageBox.Show("clicked on line - never works");
}
}
I wonder if there is something wrong in a way I'm adding polygons or is it because in my case it's just lines:
GMapOverlay polyOverlay = new GMapOverlay("polygons");
gMap.Overlays.Add(polyOverlay);
List<PointLatLng> points = new List<PointLatLng>();
points.Add(start);
points.Add(end);
polygon = new GMapPolygon(points, "mypolygon");
polygon.Stroke = new Pen(Color.Blue, 5);
polyOverlay.Polygons.Add(polygon);
So, the question is: how should I go about detecting mouse click on those lines?
I can see two issues within the code. First you need to explicitely define the polygon as HitTestVisible:
polygon.IsHitTestVisible = true;
Second, to set up a polygon add at least three points that are not aligned and actually spawn an area. I've found that the click will only be noticed on an actual area, where in theory a polygon can consist of two points.
With the hints above the check for gMap.IsMouseOverPolygon should return true then.
I'm using C# chart control to draw a nyquist plot. Now i want data points appear on the curve each time the user moves the mouse on it. So i used hit test method in GetToolTipText event.
private void BodePlot_GetToolTipText(object sender, ToolTipEventArgs e)
{
HitTestResult result = BodePlot.HitTest(e.X, e.Y);
selectDataPoint = null;
if (result.ChartElementType == ChartElementType.DataPoint)
{
selectDataPoint = (DataPoint)result.Object;
e.Text = selectDataPoint.ToString();
}
{
The problem is only a part of the curve shows values, others don't. When i use e.Text = result.Object.ToString(); to get the object on which the mouse is pointing to, here what i found :
Instead of showing the data points, the text on tooltip show custom label. So i guess the reason is that the curve is covered by the labels of x and y axis.
The only solution that i found is disabling the x and y axis, with that everything works fine. But i want to keep those axes, so how can i make those labels hide under the curve.
Your analysis is likely correct. The way to go around this would be to provide HitTest() with the optional third argument which define the desired element type.
public HitTestResult HitTest (
int x,
int y,
ChartElementType requestedElement
)
This should return underlying data points even if other elements are overlapping them.
I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}
I am experiencing a weird problem with a render transform in WPF. The project I'm working on needs to display a clicked user point over an image. When the user clicks a point, a custom control is placed at the location of their click. The image should then be able to be scaled around any point using the mouse wheel, and the custom control should be translated (not scaled) to the correct location.
To do this, I follow the MouseWheel event as follows:
private void MapPositioner_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point location = Mouse.GetPosition(MainWindow.Instance.imageMap);
MainWindow.Instance.imageMap.RenderTransform = null;
ScaleTransform st = new ScaleTransform(scale + (e.Delta < 0 ? -0.2 : 0.2), scale += (e.Delta < 0 ? -0.2 : 0.2));
st.CenterX = location.X;
st.CenterY = location.Y;
TransformGroup tg = new TransformGroup();
tg.Children.Add(st);
//tg.Children.Add(tt);
MainWindow.Instance.imageMap.RenderTransform = tg;
if (scale <= 1)
{
MainWindow.Instance.imageMap.RenderTransform = null;
}
if (TransformationChanged != null)
TransformationChanged();
}
Then, I implemented an event handler in the custom control for the TransformationChanged event seen at the end of the above code block as follows:
private void Instance_TransformationChanged()
{
//check image coords
//
if (MainWindow.Instance.imageMap.RenderTransform != null)
{
if (MainWindow.Instance.imageMap.RenderTransform != Transform.Identity)
{
Transform st = MainWindow.Instance.imageMap.RenderTransform;
Point image = MainWindow.VideoOverlayCanvas.TransformToVisual(MainWindow.Instance.MapImage).Transform(loc2);
Point trans = st.Transform(image);
Point final = MainWindow.Instance.MapImage.TransformToVisual(MainWindow.VideoOverlayCanvas).Transform(trans);
// selected = anchor2;
// final = ClipToOverlay(final);
// selected = null;
connector.X2 = final.X;
connector.Y2 = final.Y;
Canvas.SetLeft(anchor2, final.X);
Canvas.SetTop(anchor2, final.Y);
}
}
else
{
connector.X2 = loc2.X;
connector.Y2 = loc2.Y;
Canvas.SetLeft(anchor2, loc2.X);
Canvas.SetTop(anchor2, loc2.Y);
}
}
This way, I can ensure that the custom control's position is updated only after the new transform is set. Note that since I am applying the transform to the point, there is no scaling done to the control, the effect is that it is translated to the point it should. This works fine as long as the user is only scaling around one point. If they change that point, it doesnt work.
Here are some images that show the problem:
User clicks a point
user zooms out, what happened here?
after zooming out (all the way out in this case) it looks ok
I've been messing with this for about two days now, so I apologize if my code looks messy. I know this is a pretty obscure question so any help would be appreciated.
Thanks,
Max
If anyone is looking for an answer to this, because of deadlines, I had to write a workaround by having the user pan with the right mouse button and zoom with the mouse wheel. This way zooming always happens around the center of the image, so the controls are always lined up. I'm still looking for answers to the original question though if anyone can figure it out
Thanks,
Max
I'm not sure what's wrong with your transform, but have you considered an alternate approach? For example, you might want to add a transparent canvas set to stay at the same size as the image, z-order above the image (explicitly set or just put the Canvas element just after the image element). Then you can just use Canvas.SetLeft and Canvas.SetTop to place the user control where the user clicked, and to move it around. A lot easier than using a transform.
I'm developing an app for windows mobile (Compact Framework 2.0). It has a WinForms with a PictureBox.
I want to move the image of the PictureBox but I don't know how to do it so I choose to move the hole PictureBox.
To do it I use this event:
private void imagenMapa_MouseMove(object sender, MouseEventArgs e)
{
imagenMapa.Left = e.X;
imagenMapa.Top = e.Y;
this.Refresh();
}
But when I move the PictureBox it blinks and moves every where.
What I'm doing wrong?
Actual Code (Requires .NET Framework 3.5 and beyond, not sure if this is available in the Compact Framework)...
// Global Variables
private int _xPos;
private int _yPos;
private bool _dragging;
// Register mouse events
pictureBox.MouseUp += (sender, args) =>
{
var c = sender as PictureBox;
if (null == c) return;
_dragging = false;
};
pictureBox.MouseDown += (sender, args) =>
{
if (args.Button != MouseButtons.Left) return;
_dragging = true;
_xPos = args.X;
_yPos = args.Y;
};
pictureBox.MouseMove += (sender, args) =>
{
var c = sender as PictureBox;
if (!_dragging || null == c) return;
c.Top = args.Y + c.Top - _yPos;
c.Left = args.X + c.Left - _xPos;
};
The e.X and e.Y are relative to the picture box (e.g. if the mouse is in the upper left of the picture box, that's 0,0) .
The values for imagenMapa.Left and imagenMapa.Top are relative to the form (or whatever control contains imagenMapa)
If you try to mix values from these two systems without conversion, you're going to get jumps (like you're seeing).
You're probably better off converting the mouse position to the same coordinate system used by the thing that contains the picture box.
You could use imagenMapa.PointToScreen to get the mouse coordinates in screen coordinates (or Cursor.Position to get the position directly), and yourForm.PointToClient to get them back in the form coordinates.
Note that depending on your needs, you could accomplish "moving an image within a control" by overriding/handling the Paint event of a control and drawing the image yourself. If you did this, you could keep everything in the picturebox coordinates, since those are likely what you would use when you called graphicsObject.DrawImage.
e.X & e.Y is in the coordinate space of the pictureBox, imagenMapa.Left & imagenMapa.Top is in the coordinate space of the Form. :-)
Also don't forget to set your form to double buffered, that might help with the flickering, but for the actual positioning of it, I like Daniel L's suggestion
Embrace math!
control.Left = control.Left - (_lastMousePos.X - currentMousePos.X);
control.Top = control.Top - (_lastMousePos.Y - currentMousePos.Y);
Quick explanation:
You get the difference from the mouse positions and apply it to the object you want to move.
Example:
If the old mouse X position is 382, and the new one is 385, then the difference is -3. If the controls current X position is 10 then 10 - (-3) = 13
Why:
It works for anything, and is much cheaper than constantly converting coordinates back and forth.
Actually what you have done is correct. But you gave the MouseMove property to the picturebox. You should give that property to the Form(background).
ex:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
imagenMapa.Left = e.X;
imagenMapa.Top = e.Y;
}