Imports C1.Silverlight.Chart
Imports System.Windows.Data
Imports System.Windows.Shapes

Partial Public Class MainPage
    Inherits UserControl

    Public Sub New()
        InitializeComponent()

        ''''''  Define DataSeries  ''''''
        Dim Ds As New XYDataSeries()
        Ds.ChartType = ChartType.Line

        Dim xValues As New List(Of Integer)()
        xValues.Add(1)
        xValues.Add(2)
        xValues.Add(3)
        xValues.Add(4)
        xValues.Add(5)
        xValues.Add(6)
        xValues.Add(7)
        xValues.Add(8)
        xValues.Add(9)

        Dim yValues As New List(Of Integer)()
        yValues.Add(200)
        yValues.Add(130)
        yValues.Add(303)
        yValues.Add(442)
        yValues.Add(190)
        yValues.Add(239)
        yValues.Add(400)
        yValues.Add(350)
        yValues.Add(9)

        Ds.XValuesSource = xValues
        Ds.ValuesSource = yValues

        chart.Data.Children.Add(Ds)


        ''''''  Add Vertical and Horizontal Markers  ''''''
        Dim pnl = New ChartPanel()
        AddHandler pnl.MouseMove, AddressOf Me.pnl_MouseMove

        Dim vMarker = CreateMarker(False, New Point())
        pnl.Children.Add(vMarker)
        vMarker.Action = ChartPanelAction.None

        Dim hMarker = CreateMarker(True, New Point())
        pnl.Children.Add(hMarker)
        hMarker.Action = ChartPanelAction.None

        chart.View.Layers.Add(pnl)

    End Sub

    Dim ellipseDataPoint As New Point()

    ''' <summary>
    ''' Event used to recreate Markers based on 
    ''' new mouse position
    ''' </summary>        
    Private Sub pnl_MouseMove(sender As Object, e As MouseEventArgs)

        Dim cnp As ChartPanel = TryCast(sender, ChartPanel)

        Dim pnt As Point = e.GetPosition(chart)

        Dim rect As Rect = chart.View.PlotRect

        If (rect.Contains(pnt)) Then
            cnp.Children.Clear()

        Dim vMarker = CreateMarker(False, pnt)
        cnp.Children.Add(vMarker)

        Dim hMarker = CreateMarker(True, pnt)
        cnp.Children.Add(hMarker)

        Dim ellipse = CreateEllipse(pnt)
        cnp.Children.Add(ellipse)
        End If

    End Sub

    Private Function CreateMarker(isHorizontal As Boolean, pnt As Point) As ChartPanelObject
        Dim obj = New C1.Silverlight.Chart.ChartPanelObject()

        Dim bdr = New Border() With {.BorderBrush = InlineAssignHelper(Background, New SolidColorBrush(Colors.Red)), .Padding = New Thickness(2)}

        If (isHorizontal) Then
            bdr.BorderThickness = New Thickness(0, 2, 0, 0)
            bdr.Margin = New Thickness(0, -1, 0, 0)
            obj.HorizontalContentAlignment = Windows.HorizontalAlignment.Stretch

            If (pnt.X = 0 AndAlso pnt.Y = 0) Then
                obj.DataPoint = New Point(Double.NaN, 0.5)
            Else
                '' Data Point where Mouse cursor is present
                Dim dpt As Point = chart.View.PointToData(pnt)

                '/'' Markers are created based on the Horizontal movement
                '/'' of mouse cursor. We have to track whether the mouse
                '/'' cursor moved towards left or right from the last position.
                '/'' For this we need to determine the two DataPoints plotted
                '/'' between which the current mouse position is placed.

                Dim val As Double
                '/'' Gets the index of the nearest data point
                Dim ptIndex As Integer = chart.View.DataIndexFromPoint(pnt, 0, MeasureOption.X, val)

                '/'' Use the above index to calculate the Coordinates of that nearest Data Point
                Dim closeScreenPoint As Point = chart.View.DataIndexToPoint(0, ptIndex)

                '/'' Get the location of the next data point depending on whether mouse was
                '/'' was moved towards left or right
                Dim awayScreenPoint As Point
                If (pnt.X > closeScreenPoint.X) Then
                    awayScreenPoint = chart.View.DataIndexToPoint(0, ptIndex + 1)
                Else
                    awayScreenPoint = chart.View.DataIndexToPoint(0, ptIndex - 1)
                End If

                '/'' Get coordinates where Vertical marker will intersect the chart line
                Dim crossY As Double = GetY(closeScreenPoint, awayScreenPoint, pnt)

                '/'' Get DataPoint for the above intersecting coordinates
                Dim closeDataPoint As Point = chart.View.PointToData(New Point(pnt.X, crossY))

                '/'' set the DataPoint as the Attached data source for the Horizontal marker
                obj.DataPoint = New Point(Double.NaN, closeDataPoint.Y)

                ellipseDataPoint.Y = closeDataPoint.Y

            End If
        Else
            bdr.BorderThickness = New Thickness(2, 0, 0, 0)
            bdr.Margin = New Thickness(-1, 0, 0, 0)
            obj.VerticalContentAlignment = Windows.VerticalAlignment.Stretch

            Dim dpt As Point = chart.View.PointToData(pnt)
            obj.DataPoint = New Point(dpt.X, Double.NaN)
            ellipseDataPoint.X = dpt.X
        End If

        bdr.IsHitTestVisible = False
        obj.Content = bdr

        Return obj

    End Function


    ''' <summary>
    ''' Draw Ellipse around the Intersection of 
    ''' Horizontal and Vertical markers
    ''' </summary>
    ''' <param name="pnt"></param>        
    Private Function CreateEllipse(pnt As Point) As ChartPanelObject
        Dim sp As New Border()
        Dim obj = New C1.Silverlight.Chart.ChartPanelObject()
        Dim el As New Ellipse()
        el.Height = 50
        el.Width = 50
        el.StrokeThickness = 2
        el.Stroke = New SolidColorBrush(Colors.Red)

        sp.Child = el
        Dim dPoint As Point = chart.View.PointToData(pnt)

        'offset to center ellipse
        obj.DataPoint = New Point(ellipseDataPoint.X - 0.195, ellipseDataPoint.Y + 13)
        obj.Content = sp

        Return obj
    End Function

    '/// <summary>
    '/// Returns the Y value for the coordinate where Horizontal
    '/// marker intersects the Chart line. 
    '/// It uses Line theorem to calculate the point. It takes the
    '/// coodinates for two datapoints as parameters. Line between these
    '/// two datapoints covers the intersection line.
    '/// </summary>
    '/// <param name="p1">DataPoint nearest to the intersection coordinate</param>
    '/// <param name="p2">DataPoint away from the intersection coordinate</param>
    '/// <param name="pnt">Current DataPoint to get the X Value</param>
    '/// <returns></returns>
    Private Function GetY(p1 As Point, p2 As Point, pnt As Point) As Double
        Dim m As Double = (p1.Y - p2.Y) / (p1.X - p2.X)
        Dim c As Double = (p1.Y) - (p1.X * m)

        Dim y As Double = (m * pnt.X) + c

        Return y
    End Function

    Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
        target = value
        Return value
    End Function

End Class