Applies to: Nevron Chart for .NET

How to implement custom cursor snapping?

In many cases you may need to snap the axis cursors to nearest data point value or some other value which has some user defined meaning (for example an important value or threshold in data).



In such cases you need to implement custom snapping for the axis cursor. The following code example will snap a vertical axis cursor to randomly generated values of a line series:

[C#]

using System;
using System.Windows.Forms;
using Nevron.Chart;
using Nevron.Chart.WinForm;
using Nevron.GraphicsCore;
...
private void Form1_Load(object sender, EventArgs e)
{
    m_Chart = (NChart)nChartControl1.Charts[0];
    m_Chart.BoundsMode = BoundsMode.Stretch;
 
    Random random = new Random();
 
    // create a 2D line chart
    NLineSeries line = new NLineSeries();
    m_Chart.Series.Add(line);
    line.DataLabelStyle.Format = "<value>";
    line.MarkerStyle.PointShape = PointShape.Cylinder;
    line.MarkerStyle.Visible = true;
    line.MarkerStyle.PointShape = PointShape.Cross;
    line.Values.FillRandom(random, 17);
 
    // confiure the axis cursor tool
    ConfigureAxisCursorTool();
}
 
private void ConfigureAxisCursorTool()
{
    m_HorizontalAxisCursor = new NAxisCursor();
    m_HorizontalAxisCursor.BeginEndAxis = (int)StandardAxis.PrimaryY;
    m_HorizontalAxisCursor.ValueSnapper = null;
    m_HorizontalAxisCursor.ValueChanged += new EventHandler(OnValueChanged);
    m_HorizontalAxisCursor.SynchronizeOnMouseAction = MouseAction.Move;
 
    m_VerticalAxisCursor = new NAxisCursor();
    m_VerticalAxisCursor.BeginEndAxis = (int)StandardAxis.PrimaryX;
    m_VerticalAxisCursor.ValueSnapper = null;
    m_VerticalAxisCursor.ValueChanged += new EventHandler(OnValueChanged);
    m_VerticalAxisCursor.SynchronizeOnMouseAction = MouseAction.Move;
 
    m_Chart.Axis(StandardAxis.PrimaryX).Cursors.Add(m_HorizontalAxisCursor);
    m_Chart.Axis(StandardAxis.PrimaryY).Cursors.Add(m_VerticalAxisCursor);
 
    nChartControl1.Controller.Tools.Clear();
    nChartControl1.Controller.Selection.Clear();
    nChartControl1.Controller.Selection.Add(m_Chart);
    nChartControl1.Controller.Tools.Add(new NDataCursorTool());
}
 
private double SnapToValue(NDataSeriesDouble series, double value)
{
    if (series.Count == 0)
        return value;
 
    double snapValue = (double)series[0];
    double minDistance = Math.Abs(snapValue - value);
 
    for (int i = 1; i < series.Count; i++)
    {
        double currentSnapValue = (double)series[i];
        double currentDistance = Math.Abs(currentSnapValue - value);
 
        if (currentDistance < minDistance)
        {
            snapValue = currentSnapValue;
            minDistance = currentDistance;
        }
    }
 
    return snapValue;
}
 
private void OnValueChanged(object sender, EventArgs e)
{
    NLineSeries line = (NLineSeries)m_Chart.Series[0];
 
    m_VerticalAxisCursor.Value = SnapToValue(line.Values, m_VerticalAxisCursor.Value);
}
 
NChart m_Chart;
NAxisCursor m_HorizontalAxisCursor;
NAxisCursor m_VerticalAxisCursor;

[VB.NET]
Imports System
Imports System.Windows.Forms
Imports Nevron.Chart
Imports Nevron.Chart.WinForm
Imports Nevron.GraphicsCore
...
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    m_Chart = DirectCast(NChartControl1.Charts(0), NChart)
    m_Chart.BoundsMode = BoundsMode.Stretch
 
    Dim random As New Random()
 
    ' create a 2D line chart
    Dim line As New NLineSeries()
    m_Chart.Series.Add(line)
    line.DataLabelStyle.Format = "<value>"
    line.MarkerStyle.PointShape = PointShape.Cylinder
    line.MarkerStyle.Visible = True
    line.MarkerStyle.PointShape = PointShape.Cross
    line.Values.FillRandom(random, 17)
 
    ' confiure the axis cursor tool
    ConfigureAxisCursorTool()
End Sub
 
Private Sub ConfigureAxisCursorTool()
    m_HorizontalAxisCursor = New NAxisCursor()
    m_HorizontalAxisCursor.BeginEndAxis = CInt(StandardAxis.PrimaryY)
    m_HorizontalAxisCursor.ValueSnapper = Nothing
    AddHandler m_HorizontalAxisCursor.ValueChanged, AddressOf OnValueChanged
    m_HorizontalAxisCursor.SynchronizeOnMouseAction = MouseAction.Move
 
    m_VerticalAxisCursor = New NAxisCursor()
    m_VerticalAxisCursor.BeginEndAxis = CInt(StandardAxis.PrimaryX)
    m_VerticalAxisCursor.ValueSnapper = Nothing
    AddHandler m_VerticalAxisCursor.ValueChanged, AddressOf OnValueChanged
    m_VerticalAxisCursor.SynchronizeOnMouseAction = MouseAction.Move
 
    m_Chart.Axis(StandardAxis.PrimaryX).Cursors.Add(m_HorizontalAxisCursor)
    m_Chart.Axis(StandardAxis.PrimaryY).Cursors.Add(m_VerticalAxisCursor)
 
    nChartControl1.Controller.Tools.Clear()
    nChartControl1.Controller.Selection.Clear()
    nChartControl1.Controller.Selection.Add(m_Chart)
    nChartControl1.Controller.Tools.Add(New NDataCursorTool())
End Sub
 
Private Function SnapToValue(series As NDataSeriesDouble, value As Double) As Double
    If series.Count = 0 Then
        Return value
    End If
 
    Dim snapValue As Double = CDbl(series(0))
    Dim minDistance As Double = Math.Abs(snapValue - value)
 
    For i As Integer = 1 To series.Count - 1
        Dim currentSnapValue As Double = CDbl(series(i))
        Dim currentDistance As Double = Math.Abs(currentSnapValue - value)
 
        If currentDistance < minDistance Then
            snapValue = currentSnapValue
            minDistance = currentDistance
        End If
    Next
 
    Return snapValue
End Function
 
Private Sub OnValueChanged(sender As Object, e As EventArgs)
    Dim line As NLineSeries = DirectCast(m_Chart.Series(0), NLineSeries)
 
    m_VerticalAxisCursor.Value = SnapToValue(line.Values, m_VerticalAxisCursor.Value)
End Sub
 
Dim m_Chart As NChart
Dim m_HorizontalAxisCursor As NAxisCursor
Dim m_VerticalAxisCursor As NAxisCursor

Note that the SnapToValue function accepts as a parameter a NDataSeries object – this allows you use to the same function to snap to any value series (for example X values in a line series).

Article ID: 57, Created On: 10/6/2010, Modified: 6/20/2011