see values of chart points when the mouse is on points - c#

I have a chart and I want the user to see the values when the pointer is on the points.
By using digEmAll's help in the page finding the value of the points in a chart ,I could write the following code:
Point? prevPosition = null;
ToolTip tooltip = new ToolTip();
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
if (prevPosition.HasValue && pos == prevPosition.Value)
return;
tooltip.RemoveAll();
prevPosition = pos;
var results = chart1.HitTest(pos.X, pos.Y, false, ChartElementType.PlottingArea);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.PlottingArea)
{
chart1.Series[0].ToolTip = "X=#VALX, Y=#VALY";
}
}
}
by the above code,the user can see the values when the pointer is near to a series.But now How can I let the user to see the values only when the pointer is on the points?
I replaced
int k = result.PointIndex;
if (k >= 0)
{
chart1.Series[0].Points[k].ToolTip = "X=#VALX, Y=#VALY";
}
instead of
chart1.Series[0].ToolTip = "X=#VALX, Y=#VALY";
to solve my problem.But It wasn't usefull.

You should modify the code in this way:
Point? prevPosition = null;
ToolTip tooltip = new ToolTip();
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
if (prevPosition.HasValue && pos == prevPosition.Value)
return;
tooltip.RemoveAll();
prevPosition = pos;
var results = chart1.HitTest(pos.X, pos.Y, false,
ChartElementType.DataPoint);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.DataPoint)
{
var prop = result.Object as DataPoint;
if (prop != null)
{
var pointXPixel = result.ChartArea.AxisX.ValueToPixelPosition(prop.XValue);
var pointYPixel = result.ChartArea.AxisY.ValueToPixelPosition(prop.YValues[0]);
// check if the cursor is really close to the point (2 pixels around the point)
if (Math.Abs(pos.X - pointXPixel) < 2 &&
Math.Abs(pos.Y - pointYPixel) < 2)
{
tooltip.Show("X=" + prop.XValue + ", Y=" + prop.YValues[0], this.chart1,
pos.X, pos.Y - 15);
}
}
}
}
}
The idea is to check if the mouse is very close to the point e.g. 2 pixels around it (because is really unlikely to be exactly on the point) and show the tooltip in that case.
Here's a complete working example.

I would take this solution:
Add custom tooltip event handler:
this.chart1.GetToolTipText += this.chart1_GetToolTipText;
Implement event handler:
private void chart1_GetToolTipText(object sender, ToolTipEventArgs e)
{
// Check selected chart element and set tooltip text for it
switch (e.HitTestResult.ChartElementType)
{
case ChartElementType.DataPoint:
var dataPoint = e.HitTestResult.Series.Points[e.HitTestResult.PointIndex];
e.Text = string.Format("X:\t{0}\nY:\t{1}", dataPoint.XValue, dataPoint.YValues[0]);
break;
}
}

Consider the following as a possible better option than tooltips...use the label feature of the chart control.
DataPoint _prevPoint;
void chart1_MouseMove(object sender, MouseEventArgs e)
{
// this if statement clears the values from the previously activated point.
if (_prevPoint) {
_prevPoint.MarkerStyle = MarkerStyle.None;
_prevPoint.IsValueShownAsLabel = false;
}
var result = chart1.HitTest(e.X, e.Y, ChartElementType.DataPoint);
if (result.ChartElementType == ChartElementType.DataPoint)
{
var prop = result.Object as DataPoint;
if (prop != null)
{
prop.IsValueShownAsLabel = true;
prop.MarkerStyle = MarkerStyle.Star4;
}
}
}
I've tested this and i'm using it currently. It's very nice on charts with a lot of points since it shows the marker on the chart as well.

Related

How to select topmost element in a helix-toolkit sharpDX viewport?

I'm working with the sharpDX branch of the helix-toolkit library for a project at my university (HelixToolKit Library)
Currently I'm searching for a way to correctly select elements in a viewport.
I found a helpful example in the source code of the helixtoolkit library:example code on GitHub
public class MyLineGeometryModel3D : LineGeometryModel3D
{
private Color? initialColor = null;
public override bool HitTest(Ray rayWS, ref List<HitTestResult> hits)
{
if (initialColor == null)
{
initialColor = this.Color;
}
var result = base.HitTest(rayWS, ref hits);
var pressedMouseButtons = Viewport3DX.GetPressedMouseButtons();
if (pressedMouseButtons == 0 || pressedMouseButtons.HasFlag(MouseButtons.Left))
{
this.Color = result ? Color.Red : this.initialColor.Value;
}
return result;
}
}
I managed to get this running in my application. However instead of selecting only the topmost element, all elements intersected by the ray are selected.
Some kind of handler function is probably needed to highlight the element with the shortest distance?
I was checking some of the standard WPF solutions for that and they often use an eventhandler. ( e.g. 3D Hit Testing in WPF )
private void m_viewport3d_MouseDown(object sender, MouseButtonEventArgs e)
{
Point mousePos = e.GetPosition(m_viewport3d);
PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
HitTestResult result = VisualTreeHelper.HitTest(m_viewport3d, mousePos);
RayMeshGeometry3DHitTestResult rayMeshResult = result as RayMeshGeometry3DHitTestResult;
if (rayMeshResult != null)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex1]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex2]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex3]);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
GeometryModel3D marker = new GeometryModel3D(mesh, new DiffuseMaterial(Brushes.Blue));
//...add marker to the scene...
}
}
Is using an eventhandler a sensible solution ? And if yes how to get a ray element for calling the HitTest function in the eventhandler?
Use the Viewport method FindNearest:
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
Viewport3DX vp = e.Source as Viewport3DX;
Point3D p;
Vector3D v;
Model3D m;
if (vp.FindNearest(e.GetPosition(vp), out p, out v, out m))
{
//Do something with the found object
}
}
So, I actually found a solution myself, which is probably not perfect. But maybe this will be of use for somebody.
private void ViewPort3D_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Ray ray = this.ViewPort3D.UnProject(new Vector2((float)e.GetPosition(ViewPort3D).X, (float)e.GetPosition(ViewPort3D).Y));
var hits = new List<HitTestResult>();
// dictionary for connecting the id of the specific element and its distance
var hitElements = new Dictionary<int, double>();
// loop over all MeshGeometryModel3D elements
foreach (var geometry in Geometrys)
{
var isHit = geometry.Model3D.HitTest(ray, ref hits);
if (isHit)
{
hitElements.Add(geometry.Id, hits[hits.Count - 1].Distance);
}
}
if (hits.Count > 0)
{
double minDistance = hitElements.First().Value;
int id_of_hit_element = hitElements.First().Key;
foreach (var hit in hitElements)
{
if (hit.Value < minDistance)
{
minDistance = hit.Value;
id_of_hit_element = hit.Key;
}
}
var topElement = Geometrys.Find(geometry => geometry.Id == id_of_hit_element);
// do something with top element
}
}
P.S. Not a computer scientist btw, just a civil engineering student trying his best xD

TeeChart: Expand the "clickable" width of a Line/FastLine

I have a WinForms application where a number of lines are drawn in a TeeChart component. It is requested that it shall be possible to delete a line by right-clicking it.
Everything works fine, the clickseries event is captured and so on, but the user finds it difficult to hit the line on right click. The question is, is it possible to increase the region where the Line/FastLine object is sensible for clicking? That is, make the line wider without drawing the line any wider on the screen.
Tnx in advance
Yes, this is possible. The key to achieve that is PointInLineTolerance method. To achieve what you request you can combine it with NearestPoint's tool GetNearestPoint method as shown in this example:
public Form1()
{
InitializeComponent();
InitializeChart();
}
private void InitializeChart()
{
tChart1.Aspect.View3D = false;
tChart1.Series.Add(new Steema.TeeChart.Styles.Line()).FillSampleValues();
tChart1.MouseMove += TChart1_MouseMove;
}
private void TChart1_MouseMove(object sender, MouseEventArgs e)
{
var nearestPoint = new Steema.TeeChart.Tools.NearestPoint(tChart1[0]);
nearestPoint.Active = false;
var p = new Point(e.X, e.Y);
var index = nearestPoint.GetNearestPoint(p);
if (index != -1)
{
const int tolerance = 10;
var px = tChart1[0].CalcXPos(index);
var py = tChart1[0].CalcYPos(index);
var index2 = (index == tChart1[0].Count - 1) ? index - 1 : index + 1;
var qx = tChart1[0].CalcXPos(index2);
var qy = tChart1[0].CalcYPos(index2);
if (Steema.TeeChart.Drawing.Graphics3D.PointInLineTolerance(p, px, py, qx, qy, tolerance))
{
tChart1.Header.Text = "point " + index.ToString() + " clicked";
}
else
{
tChart1.Header.Text = "No point";
}
}
An alternative could be using an invisible fake series with same data as the original series.

Chart, showing date/time with mouse over

I am currently attempting to show the x,y values whenever I mouse over a point on the chart but for the x axis is in a date/time format, and I want to display the date/time instead of the actual pixel value.
What I am currently using is this following code for the mouse over event
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
if (prevPosition.HasValue && pos == prevPosition.Value)
return;
tooltip.RemoveAll();
prevPosition = pos;
var results = chart1.HitTest(pos.X, pos.Y, false,
ChartElementType.DataPoint);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.DataPoint)
{
var prop = result.Object as DataPoint;
if (prop != null)
{
var pointXPixel = result.ChartArea.AxisX.ValueToPixelPosition(prop.XValue);
var pointYPixel = result.ChartArea.AxisY.ValueToPixelPosition(prop.YValues[0]);
// check if the cursor is really close to the point (2 pixels around the point)
if (Math.Abs(pos.X - pointXPixel) < 2 &&
Math.Abs(pos.Y - pointYPixel) < 2)
{
tooltip.Show("X=" + prop.XValue + ", Y=" + prop.YValues[0], this.chart1,
pos.X, pos.Y - 15);
}
}
}
}
}
In terms of the formatting of the x axis goes, I have this
chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Format = "MMM.dd.yyyy HH:mm:ss";
chart1.Series["Series1"].XValueType = ChartValueType.DateTime;
I was wondering if there was a simple way to convert the point into a date/time value.
Thank you

How can I perform a DragDrop/swap of a ChartControl?

I'm trying to allow my users to swap two DevExpress chart controls (although I believe pretty much any control should work...), by dragging one over the top of the other. I have done this for my TabControl (to allow swapping/moving of tabs), but for some reason I appear to be missing something here which is stopping me doing the same with my ChartControl.
It "should" draw a grey-ish box over the chartcontrol and allow the user to drag it to wherever they like, but I just get a black circle with a stripe through it.
Here is the code I have written so far, hopefully one of you will be able to spot the mistake and I can stop pulling my hair out! :)
private void ChartControlMouseMove(object sender, MouseEventArgs e)
{
// Handle Mouse move only if left button is pressed.
if (e.Button == MouseButtons.Left)
{
var chartControl = (ChartControl)sender;
// If the mouse moves outside the rectangle, start the drag.
if (!rectDragBoxFromMouseDown.Equals(Rectangle.Empty)
& !rectDragBoxFromMouseDown.Contains(e.X, e.Y))
{
Invalidate();
DoDragDrop(chartControl, DragDropEffects.Move);
CalcRectDragBox(e.X, e.Y);
Invalidate();
}
}
}
private void ChartControlMouseDown(object sender, MouseEventArgs e)
{
CalcRectDragBox(e.X, e.Y);
}
private void CalcRectDragBox(int x, int y)
{
// Remember the point where the mouse down occurred. The DragSize indicates
// the size that the mouse can move before a drag event should be started.
var dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
rectDragBoxFromMouseDown = new Rectangle(
new Point(x - (dragSize.Width/2), y - (dragSize.Height/2)), dragSize);
}
private void ChartControlDragOver(object sender, DragEventArgs e)
{
var chartControl = (ChartControl)sender;
// get the control we are hovering over.
var hitInformation = chartControl.CalcHitInfo(chartControl.PointToClient(new Point(e.X, e.Y)));
if ((hitInformation != null))
{
//ChartHitInfo hoverTab = hitInformation;
if (e.Data.GetDataPresent(typeof(ChartControl)))
{
e.Effect = DragDropEffects.Move;
var dragTab = (ChartControl)e.Data.GetData(typeof(ChartControl));
if (dragTab != chartControl)
{
for (int i = 0; i < layoutControlGroupDashboard.Items.Count; i++)
{
var layoutControlItem = layoutControlGroupDashboard.Items[i] as LayoutControlItem;
if (layoutControlItem != null && layoutControlItem.Control == chartControl)
{
for (int j = 0; j < layoutControlGroupDashboard.Items.Count; j++)
{
var controlItem = layoutControlGroupDashboard.Items[j] as LayoutControlItem;
if (controlItem != null && controlItem.Control == dragTab)
{
if (!_ignoreNextDrag)
{
_ignoreNextDrag = true;
layoutControlGroupDashboard.BeginInit();
var layoutControlItemi = layoutControlGroupDashboard.Items[i] as LayoutControlItem;
if (layoutControlItemi != null)
{
Control tempControlI =
layoutControlItemi.Control;
var layoutControlItemj = layoutControlGroupDashboard.Items[j] as LayoutControlItem;
if (layoutControlItemj != null)
{
layoutControlItemi.BeginInit();
layoutControlItemj.BeginInit();
Control tempControlJ =
layoutControlItemj.Control;
layoutControlItemi.Control =
null;
layoutControlItemj.Control =
null;
layoutControlItemi.Control =
tempControlJ;
layoutControlItemj.Control =
tempControlI;
layoutControlItemi.EndInit();
layoutControlItemj.EndInit();
}
}
layoutControlGroupDashboard.EndInit();
break;
}
else
{
_ignoreNextDrag = false;
break;
}
}
}
}
}
}
}
}
else
{
e.Effect = DragDropEffects.None;
}
}
Again, the idea is to allow the user to swap the controls around just click click-dragging things around... Hopefully it's just something simple I'm missing, but I can't see it for the life of me!
Edit: This is something I tried (adding my chart to a panel first...)
Panel panel = new Panel();
panel.Name = Guid.NewGuid().ToString();
panel.Controls.Add(chartControl);
var dashboardItem = new LayoutControlItem(layoutControlDashboard, panel)
{
Padding = new DevExpress.XtraLayout.Utils.Padding(0),
Spacing = new DevExpress.XtraLayout.Utils.Padding(0),
SizeConstraintsType = SizeConstraintsType.Custom
};
Here is the modified ChartControlDragOver method which work in case the ChartControl is placed in the LayoutControl:
private void ChartControlDragOver(object sender, DragEventArgs e) {
var chartControl = (ChartControl)sender;
// get the control we are hovering over.
var hitInformation = chartControl.CalcHitInfo(chartControl.PointToClient(new Point(e.X, e.Y)));
if ((hitInformation != null)) {
//ChartHitInfo hoverTab = hitInformation;
if (e.Data.GetDataPresent(typeof(ChartControl))) {
e.Effect = DragDropEffects.Move;
var dragTab = (ChartControl)e.Data.GetData(typeof(ChartControl));
if (dragTab == chartControl) return;
InsertType insertType = InsertType.Left;
Point hitPoint = chartControl.Parent.PointToClient(new Point(e.X, e.Y));
if (dragTab.Bounds.Left < hitPoint.X && dragTab.Bounds.Right > hitPoint.X) {
if (dragTab.Bounds.Top > hitPoint.Y)
insertType = InsertType.Top;
else if (dragTab.Bounds.Bottom < hitPoint.Y)
insertType = InsertType.Bottom;
} else if (dragTab.Bounds.Right < hitPoint.X)
insertType = InsertType.Right;
else if (dragTab.Bounds.Left > hitPoint.X)
insertType = InsertType.Left;
LayoutControl layout = (LayoutControl)chartControl.Parent;
layout.GetItemByControl(dragTab).Move(layout.GetItemByControl(chartControl), insertType);
}
} else {
e.Effect = DragDropEffects.None;
}
}

finding the value of the points in a chart

I have made a chart on my form.
I want the user to see the value, x_value and y_value of each part in a balloon by clicking on that part.
The ballon shoud disappear when the user moves the mouse.
How can I do that?
You could do something like this:
ToolTip tooltip = new ToolTip();
Point? clickPosition = null;
void chart1_MouseMove(object sender, MouseEventArgs e)
{
if (clickPosition.HasValue && e.Location != clickPosition)
{
tooltip.RemoveAll();
clickPosition = null;
}
}
void chart1_MouseClick(object sender, MouseEventArgs e)
{
var pos = e.Location;
clickPosition = pos;
var results = chart1.HitTest(pos.X, pos.Y, false,
ChartElementType.PlottingArea);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.PlottingArea)
{
var xVal = result.ChartArea.AxisX.PixelPositionToValue(pos.X);
var yVal = result.ChartArea.AxisY.PixelPositionToValue(pos.Y);
tooltip.Show("X=" + xVal + ", Y=" + yVal,
this.chart1, e.Location.X,e.Location.Y - 15);
}
}
}
Result:
EDIT :
to show the tooltip whenever the mouse move, you can use the following code:
Point? prevPosition = null;
ToolTip tooltip = new ToolTip();
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
if (prevPosition.HasValue && pos == prevPosition.Value)
return;
tooltip.RemoveAll();
prevPosition = pos;
var results = chart1.HitTest(pos.X, pos.Y, false,
ChartElementType.PlottingArea);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.PlottingArea)
{
var xVal = result.ChartArea.AxisX.PixelPositionToValue(pos.X);
var yVal = result.ChartArea.AxisY.PixelPositionToValue(pos.Y);
tooltip.Show("X=" + xVal + ", Y=" + yVal, this.chart1,
pos.X, pos.Y - 15);
}
}
}
Note that this shows the tooltip on any position of the chart. If you want to show it only when the mouse is near to a series point, you can use a mschart functionality e.g. :
yourSeries.ToolTip = "X=#VALX, Y=#VALY";
(further examples here)

Categories

Resources