I have an app that displays spectrograph charts - the X axis is frequency (typically in megahertz), the Y axis is amplitude (typically in dB). Sometimes the X axis is logarithmic, sometimes numeric. What I want is to display the frequency and amplitude value next to the cursor as the mouse is rolled over the chart. I have it nearly working, but I cannot get it to position consistently - the result I get seems to vary depending on the axis scales. I got it all working OK on a chart with numerix X-axis, and thought I was done, but then it didn't quite work on one with a logarithmic X-axis.
I am using the OnDataCursorChange event to do this. I have a small window with a transparent background and 2 labels to contain the values. In the OnDataCursorChange event, I do the following:
1. Get the min and max values for the X and Y axes. If the cursor is outside these limits I don't want the values to show, so I hide the small window.
2. If the scale values in the DataCursorEventArgs object are within axis limits, I use ConvertScaleToModelCoordinate to convert the X and Y values to model coordinates. I then use ConvertModelToClientCoordinate to get client coordinates, and adjust slightly so that the window appears just above and to the right of the cursor. I have also tried using ConvertModelToViewport, but could see little or no difference in the result. I also get the model and client coordinate of the top of the Y axis, because I seemed to have to add that on to position the window properly in the Y direction.
My questions are:
1. Am I re-inventing a wheel here? Does the chart object already have something that will do this?
2. The difference between client coordinates and viewport coordinates is not clear to me - the documentation is not helpful. Can someone clarify?
4. What do I need to do to get a consistent location next to the cursor? Clearly I have something not right.
Please note I am using a fairly old version of the chart - 4.0.100.0
This is the code for the OnDataCursorChange event. fFollower is the instance of the small window that displays the Freq and Amplitude. It is declared at form scope level.
Private Sub OnDataCursorChange(ByVal sender As Object, ByVal eventargs As EventArgs)
Dim Xval As Double, Yval As Double
Dim Xmin As Double, Xmax As Double, Ymin As Double, Ymax As Double
Dim sText As String
Dim dcea As DataCusorToolEventArgs = CType(eventargs, DataCusorToolEventArgs)
Dim vcModel As New Vector, vcClient As New Vector, vcChartTop As New Vector
' Get the axis bounds
With chtGraph.Charts(0)
If .Axis(StandardAxis.PrimaryY).ScaleMode = AxisScaleMode.Logarithmic Then
Else ' Y axis always numeric
Ymin = chtGraph.Charts(0).Axis(StandardAxis.PrimaryY).NumericScale.Min
Ymax = chtGraph.Charts(0).Axis(StandardAxis.PrimaryY).NumericScale.Max
End If
If .Axis(StandardAxis.PrimaryX).ScaleMode = AxisScaleMode.Logarithmic Then
Xmin = chtGraph.Charts(0).Axis(StandardAxis.PrimaryX).LogarithmicScale.Min
Xmax = chtGraph.Charts(0).Axis(StandardAxis.PrimaryX).LogarithmicScale.Max
Else
Xmin = chtGraph.Charts(0).Axis(StandardAxis.PrimaryX).NumericScale.Min
Xmax = chtGraph.Charts(0).Axis(StandardAxis.PrimaryX).NumericScale.Max
End If
Xval = dcea.HorizontalAxisValue
Yval = dcea.VerticalAxisValue
If Xval >= Xmin And Xval <= Xmax And Yval >= Ymin And Yval <= Ymax Then
vcModel.x = .Axis(StandardAxis.PrimaryX).ConvertScaleToModelCoordinate(False, Xval)
vcModel.y = .Axis(StandardAxis.PrimaryY).ConvertScaleToModelCoordinate(False, Yval)
.ConvertModelToClientCoordinate(vcModel, vcClient)
' Get the client coord of the top of the plot space (Ymax)
vcModel.y = .Axis(StandardAxis.PrimaryY).ConvertScaleToModelCoordinate(False, Ymax)
.ConvertModelToClientCoordinate(vcModel, vcChartTop)
fFollower.Top = vcClient.y + vcChartTop.y - 5
' Make sure the small window doesn't go off the right side of the chart window
If (vcClient.x + fFollower.Width + 10) > Me.Right Then
fFollower.Left = Me.Right - fFollower.Width
Else
fFollower.Left = vcClient.x + 10
End If
Cursor = Cursors.Cross
fFollower.lblFrequency.Text = "Frq: " & gFormatFrequency(Xval)
fFollower.lblAmplitude.Text = "Amp: " & Yval.ToString("#0.00")
If Not fFollower.Visible Then fFollower.Show()
Else
fFollower.Hide()
Cursor = Cursors.Default
End If
End With
End Sub