RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Use Transformations to Draw Your Own Great Graphs : Page 6

Use .NET to build your own graphing control that displays bar, line, and point data either on its own surface, in a printout, or in an image file.

Terrific Tooltips
When you move the mouse over a data point, the control can display a tooltip that shows the point's value. If the user is allowed to move the point, the control also displays an appropriate mouse cursor.

You can probably guess that these features begin in the control's mouse event handlers. Unfortunately the mouse event handlers tell you where the mouse is in the control's device coordinates. If you want to see whether a data point is beneath the mouse, you must somehow convert the device coordinates into world coordinates so you can compare the mouse's position to the data points.

You could go back to the definitions of the transformation used to convert world coordinates to device coordinates and reverse the process with some fairly intense mathematics. Or you can take the easy way out and let the original transformation Matrix do the work for you.

The DeviceToWorld method shown below converts a point from device to world coordinates. It first creates a Matrix to transform points from world to device coordinates exactly as the control's DrawToGraphics method does. It then calls the Matrix's Invert method, which turns the Matrix into its inverse. The new Matrix represents the reverse of the operations represented by the original Matrix—meaning that instead of mapping world to device coordinates, the inverted matrix maps device to world coordinates.

The subroutine next builds an array containing the point that it should convert. It then calls the inverted Matrix's TransformPoints method to apply the Matrix to the point. Finally the routine saves the resulting X and Y coordinates in its ByRef parameters for return:

   ' Convert a point from device to world coordinates.
   Friend Sub DeviceToWorld(ByVal pt As PointF, _
      ByRef x As Single, ByRef y As Single)
      ' Make a transformation to map
      ' world to device coordinates.
      Dim world_rect As New RectangleF( _
         Wxmin, Wymax, Wxmax - Wxmin, Wymin - Wymax)
      Dim client_points() As PointF = { _
         New PointF(Me.ClientRectangle.Left, _
            Me.ClientRectangle.Top), _
         New PointF(Me.ClientRectangle.Right, _
            Me.ClientRectangle.Top), _
         New PointF(Me.ClientRectangle.Left, _
            Me.ClientRectangle.Bottom) _
      Dim trans As Matrix = New Matrix(world_rect, client_points)
      ' Invert the transformation.
      ' Get the mouse's position in screen coordinates
      ' and convert into control (device) coordinates.
      Dim pts() As PointF = {pt}
      ' Convert into world coordinates.
      ' Set the results.
      x = pts(0).X
      y = pts(0).Y
   End Sub
After you convert the mouse's position into world coordinates, the rest is relatively straightforward. For example, here's the GreatGraph control's MouseMove event handler. It converts the mouse's current position into world coordinates. It then calls subroutines SetTooltip and DisplayMoveCursor to display the tooltip and display a "move" cursor if appropriate:

   ' Display a tooltip if appropriate.
   ' Display a point move cursor if appropriate.
   Private Sub GreatGraph_MouseMove(ByVal sender As Object, _
      ByVal e As System.Windows.Forms.MouseEventArgs) _
      Handles Me.MouseMove
      ' Convert the point into world coordinates.
      Dim x, y As Single
      DeviceToWorld(Me.PointToClient(Control.MousePosition), x, y)
      ' Display a tooltip if appropriate.
      SetTooltip(x, y)
      ' Display a point move cursor if appropriate.
      DisplayMoveCursor(x, y)
   End Sub
The SetTooltip method shown in the following code loops through the DataSeries objects calling the ShowDataTip method on each one. The ShowDataTip method determines whether the point is over a data point and, if it is, displays the tooltip and returns True. The GreatGraph control's SetTooltip method keeps calling the DataSeries objects' ShowDataTip methods until one returns True:

   ' Display a tooltip if appropriate.
   Private Sub SetTooltip(ByVal x As Single, ByVal y As Single)
      ' See if a DataSeries object can display a tooltip.
      For Each obj As DataSeries In m_GraphObjects
         If obj.ShowDataTip(x, y) Then Exit Sub
      Next obj
      ' No DataSeries can display a tooltip.
      ' Remove any previous tip.
      tipData.SetToolTip(Me, "")
   End Sub
Similarly, the control's DisplayMoveCursor calls the DataSeries objects' DisplayMoveCursor method. That method decides whether the mouse is over a moveable point and, if it is, displays a data move cursor and returns True.

Download the example code for additional details.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date