Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building a BallonToolTip provider in C#

0.00/5 (No votes)
3 Mar 2004 4  
Shows how to create a ToolTip provider that supports Balloon Tooltips, including issues related to creating extender properties and using the NativeWindow class.

Sample Image - BallonTipExample.jpg

Introduction

The System.Windows.Forms namespace includes a built-in class for displaying tooltips on a control. However, this ToolTip control does not support creating the cute balloon tooltips that are available with IE 3.0 and above. This class provides a managed wrapper for the Win32 Tooltip common control that supports displaying Balloon Tool Tips.

Using the Control

The control provides functionality exactly like the current ToolTip provider that ships with .NET. To use it on a form or control, you simply drag an instance of the control onto the form from the Visual Studio .Net toolbox. Once added, each control on the form will have a new ToolTip property that you can set for the particular object. Visual Studio will automatically add the needed code to hook the control's tooltip to the BallonToolTip provider.

API

The API consists of the same properties and methods as the standard ToolTip class that the .NET Framework provides. Each instance of the BallonToolTip class can support tool tips for several controls. To set the tooltip for a control, you use the SetToolTip method:

public void SetToolTip(Control control, string tooltip)

Similarly, to retrieve the tooltip for a given control, use the GetToolTip method:

public string GetToolTip(Control control)

Designer Support

Like the ToolTip class, the BallonToolTip class includes support for setting tooltips at design time. Adding this support for any property is relatively easy. First, the class must derive from System.ComponentModel.Component. Second, the class must be decorated with ProvideProperty attribute and implement the IExtenderProvider interface. The ProvideProperty attribute provides the designer with the name and type of the property that should be added to any control that the IExtenderProvider.CanExtend method returns true for. These two elements work together to support providing a ToolTip property for each control on a form. The BallonToolTip class is declared as follows:

[ProvideProperty("ToolTip", typeof(Control))]

public class BallonToolTip : Component, IExtenderProvider

There are a few tricks to using the ProviderProperty attribute. For one, the class it is added to must provide both GetX and SetX methods, where X is the name of the property passed to the ProvideProperty attribute. Also, your GetX method should be decorated with a DefaultValue attribute to reduce the amount of code the designer has to create. In the case of the BallonToolTip provider, the GetToolTip method is decorated as follows:

[DefaultValue("")]
public string GetToolTip(Control control)

Annoyingly, when SetToolTip is called from the InitializeComponent method of the class the tooltip is present on, the controls may not yet have handles. To handle this case, the BallonToolTip control hooks the HandleCreate event of the Control base class when any control is passed to SetToolTip. When this event fires, the tooltip provider automatically updates the underlying Win32 tooltip window with the information for the newly created control. For completeness, the SetToolTip method also hooks the HandleDestroyed event to delete the native tooltips when a control is destroyed (in a typical form, this is not really an issue, since the control will be destroyed more or less at the same time as the provider).

Using the NativeWindow class

A little known class in the System.Windows.Forms namespace is the NativeWindow class. This class' one and only goal is to provide a managed wrapper around the Win32 CreateWindowEx function. In the BallonToolTip method, I have created a derived NativeWindow class (NativeTooltipWindow) that provides a few useful wrappers around this class. For example, NativeTooltipWindow builds the CreateParams structure used to create the native ToolTip provider without input from outside classes. This way, knowledge about how to create the window is enclosed in the class that actually does the creation (see the source code for additional comments and information).

Supporting Win9X

When sending messages to the underlying Win32 Tooltip window, some messages are required to send different values based on whether we are running on a Windows 9x machine (which uses ASCII based text) versus a NT based system (which uses Unicode). During the construction of the class, we set up the readonly ints to make dealing with this difference easier in the code:

private const int TTM_ADDTOOLA = 1028;
private const int TTM_ADDTOOLW = 1074;
private const int TTM_UPDATETIPTEXTA = 1036;
private const int TTM_UPDATETIPTEXTW = 1081;
private const int TTM_DELTOOLA = 1029;
private const int TTM_DELTOOLW = 1075;

private readonly int TTM_ADDTOOL;
private readonly int TTM_UPDATETIPTEXT;
private readonly int TTM_DELTOOL;

/// <SUMMARY>

/// Initializes a new instance of the

/// <SEE cref="ToolTipLibrary.BallonToolTip" /> class.

/// </SUMMARY>

public BallonToolTip()
{    
    m_controls = new Hashtable();
    m_active = true;
    m_showAlways = false;

    //Create a new native window.

    m_window = new NativeTooltipWindow();

    if (Marshal.SystemDefaultCharSize == 1)
    {
        //Win9x machines

        TTM_ADDTOOL = TTM_ADDTOOLA;
        TTM_UPDATETIPTEXT = TTM_UPDATETIPTEXTA;
        TTM_DELTOOL = TTM_DELTOOLA;
    }
    else
    {
        //WinNT machines

        TTM_ADDTOOL = TTM_ADDTOOLW;
        TTM_UPDATETIPTEXT = TTM_UPDATETIPTEXTW;
        TTM_DELTOOL = TTM_DELTOOLW;
    }

    InitializeComponent();
}

In the code, we never use the constants above. Instead, we always reference the readonly member variables to ensure we are sending the right message for the platform.

Changes

3/4/2004 - Fixed a bug in the CreateHandle method to create the tooltip handle when it does not exist.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here