Display tooltips on a graph of an equation entered by the user in C#


[tooltip]

This example draws a graph of an equation entered by the user and the displays a tooltip on it when you move the mouse over the graph. See the post Graph equations entered by the user in C# for information about how the program draws the graph.

The idea isn’t too difficult. When the mouse moves over the graph, see what point it is over in world coordinates. If that point is close to the curve, display a tooltip. The details are a bit trickier. To make the new example, I made the following changes.

First, I added a ToolTip control named tipPoint to the form. That control provides the ability to display tooltips for other controls.

The original program uses a transformation and its inverse to map points between world coordinates and the device coordinates used by the PictureBox. This example needs to use those transformations later so it can convert the mouse’s location into a point on the graph. To do that, I modified the code so it saves the transformation and its inverse in class-level variables. The following code shows how they are declared.

// The transformation and inverse.
private Matrix Transform = null, Inverse = null;

Later, when the program draws the graph, the following lines save those matrices.

Transform = gr.Transform;
Inverse = gr.Transform;
Inverse.Invert();

Similarly the tooltip code will need to be able to call the compiled function entered by the user. The following code shows how the program declares the function’s MethodInfo object at the class level.

// The compiled function.
private MethodInfo Function = null;

When it draws the graph, the program uses the following statement to save the compiled function’s information in this variable.

Function = CompileFunction(txtEquation.Text);

Now the program can use the function, the world-to-device transformation, and the inverse transformation.

When the mouse moves over the graph PictureBox, the following event handler executes.

// If the mouse is over the curve,
// display a tooltip showing the curve's value.
private void picGraph_MouseMove(object sender, MouseEventArgs e)
{
    // Get the tooltip that we should display.
    string tooltip = GetGraphToolTip(e.Location);

    // See if the tooltip has changed.
    string old_tooltip = tipPoint.GetToolTip(picGraph);
    if (old_tooltip == tooltip) return;

    // Display the new tooltip.
    tipPoint.SetToolTip(picGraph, tooltip);
}

This code calls the GetGraphToolTip method described shortly to get the tooltip that it should display. It then uses the ToolTip object’s GetToolTip method to get the PictureBox control’s current tooltip and compares them. If the tooltip hasn’t changed, the method exits.

If the tooltip has changed, the code uses the ToolTip control’s SetToolTip method to give the PictureBox the new tooltip.

The following code shows the GetGraphToolTip method.

// Get the tooltip for the point in device coordinates.
// Return null if the point isn't on the curve.
private string GetGraphToolTip(Point point)
{
    if (Function == null) return null;

    // Convert the mouse's location into world coordinates.
    PointF[] world_points = { point };
    Inverse.TransformPoints(world_points);

    // Find the Y coordinate in device coordinates.
    float x = world_points[0].X;
    float y = F(Function, x);
    PointF[] device_points = { new PointF(x, y) };
    Transform.TransformPoints(device_points);

    // See if the mouse's position is within
    // five pixels of this point's location.
    if (Math.Abs(point.Y - device_points[0].Y) > 10) return null;

    // Compose the tooltip.
    return "(" + x.ToString("0.00") +
        ", " + y.ToString("0.00") + ")";
}

If Function is null, then the user has not yet entered and compiled a function so the method exits.

The method then makes an array containing the mouse’s position. It uses the Inverse transformation matrix to map the device coordinates on the PictureBox into world coordinates.

Next, the code passes the world coordinate’s X value into the function to get the corresponding Y value. This gives a point on the curve at the mouse’s X coordinate.

The method then uses the Transform transformation to map the point on the curve back to device coordinates. Now the method has the mouse’s original location and the location of a point on the curve in device coordinates. It compares the Y coordinates of the points to see if they are within 10 pixels of each other. If the points are within 10 pixels, the method returns a tooltip giving the world coordinates of the point on the curve.

See the previous example for more details.

There are other approaches that you could take to this problem. For example, you could save all of the points generated on the curve and then compare the mouse’s position to them (in world coordinates) to see which was closest. That would probably provide a better result, particularly in places where the graph is mostly vertical. That would have been a bigger change from the previous example, however, so I used this version.

You could also convert the curve’s points into device coordinates before drawing and then compare the mouse position to the converted points. See the example Draw a labeled line graph that displays value tooltips in C# for information on that approach.

Another approach would be to track the mouse’s X coordinate and display the corresponding Y coordinate.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawing, graphics, mathematics, reflection and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *