Task-based Tutorials - How to use dynamically created Images with ImageToolTips

This article shows, how dynamically created images can be used with the ToolTipsFactory ImagetToolTip component.

Actually, it makes no difference, if the image to be displayed by the ImageToolTip of a control is loaded from file at design-time (see the example in the "Quick-start tutorial - Part III"), loaded from file at run-time (see the task-based tutorial on "How to use ToolTips with the Microsoft DataGrid") or dynamically created by the application itself at run-time. The latter approach opens the possibility for application developers, to create any kind of content to be assigned to the ImageToolTip of a control. Examples are dynamically created charts or diagrams, to visualize information gathered or computed while the application is running, specially rendered text, such as 3D-text, distorted or vertical text, or graphical details of the information hold or displayed by the control for which the ImageToolTip is displayed.

In this article we are going to look into how the ImageToolTip can be used to display dynamically rendered strings for a control. The sample-application renders a given text in a way, which is not supported by the Single- and MultiLineToolTips, by drawing the text directly on a new empty image. This opens way to make use of all the powerful features provided by GDI+.

The source-code and the corresponding Visual Studio project for this sample can be found in the "<ToolTipsFactoryHomeDir>.\Samples\DynamicImageToolTip1"-directory.

This sample-application shows, how a text can be rendered along a dynamically computed path and how the resulting image is assigned to the control, which should display the given text (or better, image) as tooltip. The front-end of the sample application lets you play around with the various settings (tooltip-width, font and text) and check the result instantaneously over the testarea-panel (Panel1), after the new settings have been committed through the Apply-button.

How new content (in this case an Image) is assigned to the tooltip of a specific control has been extensively covered in other sections of the ToolTipsFactory documentation. The only difference in the sample is that the image is not loaded from a file (or from the application resources) but generated at run-time by a function. 

The central part of the application, where all this happens is listed below:

Private Sub cmdApply_Click(ByVal sender As System.Object, _ 
ByVal e As System.EventArgs) _ 
Handles cmdApply.Click

    'This procedure calls a function to render a new image,
    'based on the current settings of the front-end form and
    'assigns this image to the provided tooltip property of
'the control, which should display the new image as a tooltip.


    'get the desired tooltip-width from the corresponding field.
    Dim width As Integer = CInt(Me.txtWidth.Text)

    'Because the text will be rendered as Sine-curve, the height
'the resulting image must be a multiple of the selected
Dim height As Integer = mSelectedFont.Size * 3


    'This defines a nice linear gradient brush....
    Dim ttRect As RectangleF = New RectangleF(0, 0, width, height)
    Dim br As Brush = New LinearGradientBrush(ttRect, Color.Blue, _ 


    'This is where the new image is created and assigned to the
    'tooltip-property of Panel1..
    Dim img As Image
    img =
DrawTextAsWave(Me.txtTooltipText.Text, mSelectedFont, _
                             br, width, height)
.ImageTT.GetImageToolTip(Panel1).Image = img


    'By assigning the known width of the image to be displayed
'as an Override-value for the ImageToolTip of Panel1, we
    'make sure, that the image is not scaled down to the DefaultWidth
'specified for ImageTT at the component-level.
Me.ImageTT.GetImageToolTip(Panel1).Override.DefaultWidth = width

End Sub

Listing 1

The code in Listing 1 is executed with every click on the Apply-button. The important line is, where the function-call occurs, to create the new image based on the current values of the fields in Form1: The called function DrawTextAsWave() returns a new image with the text (txtTooltipText.Text) rendered along a sinoidal path with the selected font (mSelectedFont) and brush (br).

The resulting image is then directly assigned as content for the ImageToolTip of Panel1. The final - but important - step is, to ensure that the DefaultWidth  specified at the component-level for ImageTT is overridden with the width of the new image (img), if you don't want it do be scaled up or down to the value defined as DefaultWidth

As can be seen from the screen-shot of the sample-application, the drawn text is really following a sinoidal path. This effect is generated in the function DrawTextAsWave() that creates the new image:

Function DrawTextAsWave(ByVal text As String, _
ByVal font As Font,
ByVal brush As Brush, _
ByVal width As Integer, _
ByVal height As Integer) As Image

    'Create a new empty bitmap with the desired dimensions...
    Dim img As Image
    img = New Bitmap(width, height, PixelFormat.Format32bppArgb)


    'Get a reference to its graphics-context
Dim gr As Graphics = Graphics.FromImage(img)


    'Create a new GraphicsPath
Dim path As GraphicsPath = New GraphicsPath()
Dim hScale As Single, posY As Single
Dim vScale As Single
Dim i As Integer
Dim ptsWave() As PointF


    'we leave 5% of free space to both sides of the rendered text.
Dim waveWidth As Integer = width - (width / 10)


    'This initiates the path, we are going to transform....
path.AddString(text, font.FontFamily, font.Style, _
                  font.Size, New PointF(0, 0), New StringFormat())

    'Get the points-array of the initialized path
ptsWave = path.PathPoints

    'This is needed to equaly distribute all points of the path
'along the x-axis of the image...
hScale = waveWidth / path.GetBounds.Width


    'In this loop each point of the path is mapped onto
'a calculated sine-curve and the position scaled according
'to the image width and height...
For i = 0 To ptsWave.Length - 1
     ptsWave(i).X *= hScale
     posY = height/4 *(Math.Sin(((ptsWave(i).X)/waveWidth)*(4*Math.PI)))
     ptsWave(i).Y += posY
Next i

    'Make the image-background transparent..

    'Transform the initial path...
path = New GraphicsPath(ptsWave, path.PathTypes)
gr.InterpolationMode = InterpolationMode.HighQualityBicubic
gr.TextRenderingHint = TextRenderingHint.SystemDefault
gr.TranslateTransform(0, height / 4)

    '...and draw the path-object with the selected brush
gr.FillPath(brush, path)

    'Release all resources...

    'Return the rendered image
Return img

End Function

Listing 2

The code in Listing 2 is provided to give you an idea of all the nice things that can be done by resorting to the magic power of GDI+. Basically, as soon as you create a new blank image, you can do whatever you like on this canvas: You can draw, write or mix images with text at will. The resulting peace of art can then be assigned as tooltip-content for the ImageToolTip of a control.

Maybe one last thing about the sample-application should be pointed out. In the resulting tooltip, the rendered text seems to float over the desktop and the applications underneath. How this effect can be achieved, has been covered in other sections and examples for the Animation-, and the SingleLine- and MultiLineToolTips. The crucial point here is, that the approach described for the other tooltip components does not work for the ImageToolTip, because the ImageToolTip component does not provide any properties to configure the tooltip-background. Therefore it is not possible to define a transparent background. To just create the image to be displayed with a transparent background  (as in the sample above) will not solve the problem. In this case the color of the tooltip-canvas will shine through the transparent areas of the image.

Now, the canvas of the tooltip itself is defined by the Border, and the properties associated with each BorderStyle. This settings define the background of the tooltip, even if Border is set to None and BorderWidth=0. (You can try this, by playing around with the border-layout of the ImageTT-component in the sample-project.) This leads to the conclusion, in order to get a really transparent background for the created image, not only the image-background has to be transparent, but also the border of the ImageToolTip must be made transparent.