Welcome to AddressOf.com Sign in | Help

ToolTip Voodoo

by Cory Smith

Introduction

ToolTips can be used in a variety of ways to provide additional information to the user.  Now, understand, sometimes the usage of these can be abused and misused.  However, used correctly, ToolTips can improve the UI of your application tremendously.

What will this article do for you?

I'm going to state up front that this article is not about how to correctly use tooltips; rather, it arm you with the necessary information to create custom tooltips that can present additional information above and beyond a simple line of basic text in a yellow box.  Yes, this means that I'll be arming you with the information to potentially create one of the most gawd-awful UI's you've ever seen.  But, use appropriately, you can create extremely compelling tooltips that can significantly enhance your application.

Initial Setup of our Project Environment

  • Create a new Windows Form project (CustomToolTips).
  • Add a ToolTip control to Form1.
  • Add a Label control to Form1 (Name = “Label1“, Text = “Default ToolTip“, ToolTip on ToolTip1 = “This is the ToolTip for this Label.“).
  • Add a Button control to Form1 (Name = “Button1“, Text = “Custom ToolTip“).

First Things First

At this point, you can execute the application and you will find that moving your mouse over the label that a tooltip will become visible.  It's the normal tooltip you've come to love (and hate).Saving configuration is something that nearly every application needs to do, so let's use that as our sample to build upon.  First, let's look at how you might have persisted this information in the past.

Now that you've done this; you might be asked, “Hey, can we spice it up a bit?“

Spicing it up a little...

 

The first step to controlling how tooltips are drawn is to set the OwnerDraw boolean property to True.  Now the Draw event will be activated and you can now control how the tooltip is drawn.

 

The first thing we will do is reproduce the drawing behavior of the default tooltip so that we can modify it from there.  To do this, you should have a Draw event that looks as follows:

 

Private Sub ToolTip1_Draw(ByVal sender As Object, ByVal e As DrawToolTipEventArgs) Handles ToolTip1.Draw

  e.DrawBackground()
  e.DrawBorder()
  e.DrawText()

End Sub

If you run the application again, you'll find that the tooltip looks like the first time you ran the application.  Not to exciting, but as you can see that the build in functionality of the ToolTip control has already broken down portions of the drawing into individual segments.  Specifically, the DrawBackground (draws the yellow background), DrawBorder (draws the black single line box that borders the yellow area) and DrawText (that draws the text using the default formatting used by the ToolTip control) methods are available to you.  So, let's take it one step further and replace the dull single color yellow background with a gradient of some sort.  In this example, we will just start with white to a yellow gradient.

Add the following import so we can save ourselves a bit of typing:

Imports System.Drawing.Drawing2D

Modify the Draw event as follows:

Private Sub ToolTip1_Draw(ByVal sender As Object, ByVal e As DrawToolTipEventArgs) Handles ToolTip1.Draw

 

  ' Draw the custom background (replacing e.DrawBackground()).
  Using brush As New LinearGradientBrush(e.Bounds, Color.AntiqueWhite, Color.Yellow, LinearGradientMode.Vertical)
    e.Graphics.FillRectangle(brush, e.Bounds)
  End Using
  

  e.DrawBorder()
  e.DrawText()

 

End Sub

As you can see, pretty simple to change out the default drawing of the background with your own with just a couple of lines of code.  So now your tooltip, although not really too much different than the default, is spiced up a bit; of course, you could use any colors you wish based on the overall look and feel of your application.

One of the key factors with this particular example is the usage of the Bounds property.  This property is similar to a Control's ClientRectangle property and defines the boundary of the ToolTip's drawing area.  Utilizing this, we can draw the background accordingly so that it's the correct size.

Before taking it to the next level...

At this point, every control that is utilizing ToolTip1 will be drawn in this manner.  However, if you'd like to control this behavior and have some controls drawn one way and other drawn another, you can approach this two ways.  The simplest method is just to utilize more than one ToolTip control.  By setting the ToolTip text for the control and ToolTip in question, the tooltip will be handled accordingly.  However, if you'd like to control it yourself, you could use a single ToolTip control and handle the drawing based on the control that raised the Draw event.  Here's an example will only draw the custom version if the control is Button1 and any other controls will be drawn using the default behavior.

Private Sub ToolTip1_Draw(ByVal sender As Object, ByVal e As DrawToolTipEventArgs) Handles ToolTip1.Draw

  If e.AssociatedControl Is Button1 Then

 

    ' Draw the custom background (replacing e.DrawBackground()).
    Using brush As New LinearGradientBrush(e.Bounds, Color.AntiqueWhite, Color.Yellow, LinearGradientMode.Vertical)
      e.Graphics.FillRectangle(brush, e.Bounds)
    End Using
  

    e.DrawBorder()
    e.DrawText()

  Else

    ' Any other control utilizing ToolTip1.
    e.DrawBackground()
    e.DrawBorder()
    e.DrawText()

  End If

End Sub

 

Which approach is better?  I don't really know which would be better; I suspect it's a matter of preference.  I chose to use a single ToolTip; but you could approach this in either manner you choose.

Taking it to the next level...

Let's control everything; let's draw a custom background, handle the way text is drawn, add a divider and show an image.  Let's jump into the code:

Private Sub ToolTip1_Draw(ByVal sender As Object, ByVal e As DrawToolTipEventArgs) Handles ToolTip1.Draw

  If e.AssociatedControl Is Button1 Then

    Dim x As Integer
    Dim y As Integer

    ' Draw the custom background.
    Using brush As New LinearGradientBrush(e.Bounds, Color.AntiqueWhite, Color.CornflowerBlue, LinearGradientMode.ForwardDiagonal)
      e.Graphics.FillRectangle(brush, e.Bounds)
    End Using

    ' Draw the border (default).
    e.DrawBorder()

    ' Draw the "title" text.
    Dim titleHeight As Integer = 40
    e.Graphics.DrawLine(Pens.Black, 0, titleHeight, e.Bounds.Width, titleHeight)
    Dim text As String = "Now this is a TOOLTIP!"
    Using font As New Font(e.Font, FontStyle.Bold)
      x = (e.Bounds.Width - e.Graphics.MeasureString(text, font).Width) \ 2
      y = (titleHeight - e.Graphics.MeasureString(text, font).Height) \ 2
      e.Graphics.DrawString(text, font, Brushes.Black, x, y)
    End Using

    ' Draw an image.
    Dim image As New Bitmap(My.Resources.CorySmith)
    Dim w As Integer = image.Width \ 4
    Dim h As Integer = image.Height \ 4
    x = (e.Bounds.Width - w) \ 2
    y = titleHeight + (((e.Bounds.Height - titleHeight) - h) \ 2)
    e.Graphics.DrawImage(image, x, y, w, h)

  Else

    ' Any other control utilizing ToolTip1.
    e.DrawBackground()
    e.DrawBorder()
    e.DrawText()

  End If

End Sub

The above code assumes that you've added a new resource item (a valid image file) to the project called CorySmith.  (The attached project has this done already.)   When you run this version, you'll see that whenever you move your mouse over the Label, it shows the default tooltip version.  Moving over the Button control displays some pinkish colored image, not what we were wanting.  The issue is that when the ToolTip is drawn, it's initial size is calculated based on the current ToolTip text value.  So what we need to do is intercept the initial drawing and set the size that we need.  One way (and it's the wrong way for this example) would be to set the text value (with possible padding) so that the control knows how to draw.  In this example, this would not work since we are drawing text and an image and completely formatting all of the elements with custom spacing between the elements.

Controlling the size...

It would seem that we could just set the Bounds property in the EventArgs, but this value is read-only.  However, the ToolTip control does allow you to intercept the Draw event before it occurs and control the size of the bounding rectangle.  This event is called Popup.  In this event, you can specify the ToolTipSize that will be utilized during the Draw event.

Private Sub ToolTip1_Popup(ByVal sender As Object, ByVal e As PopupEventArgs) Handles ToolTip1.Popup
  If e.AssociatedControl Is Button1 Then
    e.ToolTipSize = New Size(400, 300)
  Else
    ' Do nothing.
  End If
End Sub

So if the control is Button1, then we'll set the size (fixed in this example) to 400x300 pixels.   For all other controls, we aren't modifying the default behavior.

Now when we run our example, the Button1 tooltip is drawn in the manner we were expecting.

Conclusion

As you can see, it's not really all that difficult to create feature rich owner drawn.  Now I'm only demonstrating showing some simple text and an image on a gradient background, but you could show almost anything.  The only limitiation is your imagination.

Download(s)

CustomToolTips.zip

Published Thursday, May 11, 2006 6:22 PM by CorySmith
Filed under: ,

Comments

# re: ToolTip Voodoo

Wednesday, May 31, 2006 2:32 AM by paully
i was wondering how you would do an autosize?

the properties e.ToolTipSize is in tooltip_PopUP

and tooltip_Draw has e.Font and e.Graphics

I can't figure out what the best way to use both in a single routine.

I'm trying to use to set the width to a fixed value and automeasure the height with graphics.measurestring, however I think I'll have to use some private variables to make it work between the 2 event handlers.

There has to be a simplier way tho.

cheers

Paully

# re: ToolTip Voodoo

Wednesday, September 20, 2006 4:54 PM by Martin
Very nice!

One oddity I noticed...
Mouse over the label and the custom tooltip appears. Keep the mouse there and the tooltip will eventually fade away. The tooltip doesn't seem to return after it has faded away...

# re: ToolTip Voodoo

Tuesday, October 03, 2006 1:12 PM by PQSIK
Just a quick question.

Would you be able to use a textbox to view some text that replaces the image?

If so could you show me some code please

# re: ToolTip Voodoo

Friday, December 15, 2006 2:58 AM by muthu

Nice article. How can I adjust the position of tooltip?

Please tell me.

# re: ToolTip Voodoo

Tuesday, January 23, 2007 5:51 AM by Jim

I am invoking set

# re: ToolTip Voodoo

Friday, August 10, 2007 8:10 AM by Sameer Y S
Wonderful. Can I change the Shape also.

# re: ToolTip Voodoo

Wednesday, November 14, 2007 10:47 AM by HESURU
Informative article on .NET ToolTips.
Anonymous comments are disabled