Mouse MOve event - Show values in a MS Chart Control

Devon Nullman 21 Reputation points
2021-12-25T19:43:02.377+00:00

Below is sample code, with sample values. Uses a Form with a Chart object ("Chart1") X-Axis is the date and there are 2 Y values. If the mouse passes over the line(s) I want to display which series, the value and the date, several show as "Unknown" because the value passed to the Function is slightly off the actual value. I'd rather NOT use the function I created anyway, I would rather get it direct from the HitTest results if that is possible.

Option Strict On
Imports System.Windows.Forms.DataVisualization.Charting

Public Class Form1
    Private TheDate As New List(Of Date)
    Private Series1 As New List(Of Integer)
    Private Series2 As New List(Of Integer)

    Private Function DateFromOtherData(SName As String, Data As Integer) As String
        Dim DataIndex As Integer = -1
        If SName = "Series1" Then
            DataIndex = Series1.IndexOf(Data)
        Else
            DataIndex = Series2.IndexOf(Data)
        End If
        If DataIndex > -1 Then
            Return TheDate(DataIndex).ToShortDateString
        Else
            Return "Unknown"
        End If

    End Function
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Add some saple data
        Dim Temp As New List(Of String)
        Temp.Add("2020-03-07")
        Series1.Add(428)
        Series2.Add(19)
        Temp.Add("2020-03-31")
        Series1.Add(188461)
        Series2.Add(4304)
        Temp.Add("2020-04-24")
        Series1.Add(909853)
        Series2.Add(51360)
        Temp.Add("2020-05-18")
        Series1.Add(1515593)
        Series2.Add(90414)
        Temp.Add("2020-06-11")
        Series1.Add(2036500)
        Series2.Add(113980)
        Temp.Add("2020-07-05")
        Series1.Add(2910782)
        Series2.Add(129941)
        Temp.Add("2020-07-29")
        Series1.Add(4433633)
        Series2.Add(151172)
        Temp.Add("2020-09-15")
        Series1.Add(6614318)
        Series2.Add(195689)
        Temp.Add("2020-10-09")
        Series1.Add(7719211)
        Series2.Add(213595)
        Temp.Add("2020-11-02")
        Series1.Add(9377208)
        Series2.Add(231480)
        Temp.Add("2020-11-26")
        Series1.Add(12954294)
        Series2.Add(263339)
        Temp.Add("2020-12-20")
        Series1.Add(17880478)
        Series2.Add(317810)
        Temp.Add("2021-01-13")
        Series1.Add(23135194)
        Series2.Add(384824)
        Temp.Add("2021-02-06")
        Series1.Add(26958807)
        Series2.Add(462052)
        Temp.Add("2021-03-02")
        Series1.Add(28738501)
        Series2.Add(515710)
        Temp.Add("2021-03-26")
        Series1.Add(30172762)
        Series2.Add(547621)
        Temp.Add("2021-04-19")
        Series1.Add(31754642)
        Series2.Add(567314)
        Temp.Add("2021-05-13")
        Series1.Add(32868084)
        Series2.Add(583991)
        Temp.Add("2021-06-06")
        Series1.Add(33329413)
        Series2.Add(596803)
        Temp.Add("2021-06-30")
        Series1.Add(33639764)
        Series2.Add(604446)
        Temp.Add("2021-07-24")
        Series1.Add(34469200)
        Series2.Add(610400)
        Temp.Add("2021-08-17")
        Series1.Add(37133674)
        Series2.Add(623237)
        Temp.Add("2021-09-10")
        Series1.Add(40914456)
        Series2.Add(658865)
        Temp.Add("2021-10-04")
        Series1.Add(43826566)
        Series2.Add(703362)
        Temp.Add("2021-10-28")
        Series1.Add(45797078)
        Series2.Add(743050)
        Temp.Add("2021-11-21")
        Series1.Add(47692614)
        Series2.Add(771871)
        Temp.Add("2021-12-15")
        Series1.Add(50346987)
        Series2.Add(801037)
        'For i = 0 To Series1.Count - 1         'This lowers each value in Series1
        'Series1(i) = CInt(Series1(i) / 50)     'and makes getting the date work
        'Next                                   'Not acceptable, just an example
        For Each S As String In Temp
            TheDate.Add(CDate(S))
        Next
    End Sub

    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        Chart1.Series.Clear()
        Chart1.Series.Add("Series1")
        Chart1.Series.Add("Series2")
        Chart1.Series(0).YAxisType = AxisType.Primary
        Chart1.Series(1).YAxisType = AxisType.Secondary
        Chart1.ChartAreas(0).AxisY.MajorGrid.LineWidth = 0
        Chart1.ChartAreas(0).AxisY2.MajorGrid.LineWidth = 0
        Chart1.ChartAreas(0).AxisX.MajorGrid.LineWidth = 0
        Chart1.ChartAreas(0).AxisX2.MajorGrid.LineWidth = 0
        Chart1.ChartAreas(0).AxisX.Interval = 21
        Chart1.ChartAreas(0).AxisX2.Interval = 21
        Chart1.ChartAreas(0).AxisY.LabelStyle.ForeColor = Color.Blue
        Chart1.ChartAreas(0).AxisY.LabelStyle.Font = New Font("Arial", 12, FontStyle.Bold)
        Chart1.ChartAreas(0).AxisY2.LabelStyle.ForeColor = Color.Red
        Chart1.ChartAreas(0).AxisY2.LabelStyle.Font = New Font("Arial", 12, FontStyle.Bold)

        Chart1.Series(0).Name = "Series1"
        Chart1.Series(0).Points.DataBindXY(TheDate.ToArray, Series1.ToArray)
        Chart1.Titles.Clear()
        Chart1.Series(1).Name = "Series2"
        Chart1.Series(1).Points.DataBindXY(TheDate.ToArray, Series2.ToArray)
        Chart1.Series(0).ChartType = SeriesChartType.Line
        Chart1.Series(0).BorderWidth = 5
        Chart1.Series(1).ChartType = SeriesChartType.Line
        Chart1.Series(1).BorderWidth = 2

        Chart1.Series(0).Color = Color.Blue
        Chart1.Series(1).Color = Color.Red

        Chart1.Visible = True
    End Sub

    Private Sub Chart1_MouseMove(sender As Object, e As MouseEventArgs) Handles Chart1.MouseMove

        Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)
        Dim Ndx As Integer
        Dim thisPt As New PointF
        Dim ta As New CalloutAnnotation
        Dim TheText As String = ""

        Chart1.Annotations.Clear()
        If result.ChartElementType = ChartElementType.DataPoint Then

            Dim SName As String = result.Series.Name
            If SName = "Series1" Then
                Ndx = 0
            Else
                Ndx = 1
            End If
            thisPt = New PointF(CSng(Chart1.Series(Ndx).Points(result.PointIndex).XValue),
                                CSng(Chart1.Series(Ndx).Points(result.PointIndex).YValues(0)))

            ta.CalloutStyle = CalloutStyle.Rectangle
            ta.AnchorDataPoint = Chart1.Series(Ndx).Points(result.PointIndex)
            ta.Alignment = ContentAlignment.MiddleLeft
            ta.TextStyle = TextStyle.Default
            ta.Font = New Font("Arial", 10, FontStyle.Bold)

            TheText &= SName & ": " & vbLf &
                "Value: " & thisPt.Y.ToString("F0") & vbLf &
                "Date: " & DateFromOtherData(SName, CInt(thisPt.Y))
            ta.Text = TheText
            Chart1.Annotations.Add(ta)
            Chart1.Invalidate()
            Me.Text = "ThisPt.X = " & thisPt.X.ToString
        End If
    End Sub
End Class
Developer technologies | VB
{count} votes

Accepted answer
  1. Viorel 122.6K Reputation points
    2021-12-27T07:01:23.857+00:00

    Try this modification:

    TheText &= SName & ": " & vbLf &
                    "Value: " & thisPt.Y.ToString("F0") & vbLf &
                    "Date: " & Date.FromOADate(thisPt.X) 
    
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Jiachen Li-MSFT 34,221 Reputation points Microsoft External Staff
    2021-12-27T08:14:39.803+00:00

    Hi @Devon Nullman
    The problem occurs in lines 165 and 166 of your code.
    When DataPoint.XValue is converted from double type to single type, the precision is lost and the data is distorted.
    You can choose to use List(Of Double) to avoid this problem.
    Here is the code I modified based on your code.

        Private Series1 As New List(Of Double)  
        Private Series2 As New List(Of Double)  
      
        Private Function DateFromOtherData(SName As String, Data As Double) As String  
            Dim DataIndex As Integer = -1  
            If SName = "Series1" Then  
                DataIndex = Series1.IndexOf(Data)  
            Else  
                DataIndex = Series2.IndexOf(Data)  
            End If  
            If DataIndex > -1 Then  
                Return TheDate(DataIndex).ToShortDateString  
            Else  
                Return "Unknown"  
            End If  
      
        End Function  
      
        Private Sub Chart1_MouseMove(sender As Object, e As MouseEventArgs) Handles Chart1.MouseMove  
      
            Dim result As HitTestResult = Chart1.HitTest(e.X, e.Y)  
            Dim Ndx As Integer  
            Dim ta As New CalloutAnnotation  
            Dim TheText As String = ""  
      
            Chart1.Annotations.Clear()  
            If result.ChartElementType = ChartElementType.DataPoint Then  
      
                Dim SName As String = result.Series.Name  
                If SName = "Series1" Then  
                    Ndx = 0  
                Else  
                    Ndx = 1  
                End If  
      
                ta.CalloutStyle = CalloutStyle.Rectangle  
                ta.AnchorDataPoint = Chart1.Series(Ndx).Points(result.PointIndex)  
                ta.Alignment = ContentAlignment.MiddleLeft  
                ta.TextStyle = TextStyle.Default  
                ta.Font = New Font("Arial", 10, FontStyle.Bold)  
      
                TheText &= SName & ": " & vbLf &  
                    "Value: " & Chart1.Series(Ndx).Points(result.PointIndex).YValues(0) & vbLf &  
                    "Date: " & DateFromOtherData(SName, Chart1.Series(Ndx).Points(result.PointIndex).YValues(0))  
                ta.Text = TheText  
                Chart1.Annotations.Add(ta)  
                Chart1.Invalidate()  
                Me.Text = "ThisPt.X = " & Chart1.Series(Ndx).Points(result.PointIndex).XValue  
            End If  
        End Sub  
    

    Hope the code above could be helpful.
    Best Regards.
    Jiachen Li

    ----------

    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.