Home > WPF, WPF Controls > Quick and Dirty (but nice!) ToolTips via Markup Extensions

Quick and Dirty (but nice!) ToolTips via Markup Extensions

April 19th, 2009

Note: There’s a follow-up posting to that one that relies on Blend Behaviors.

 

NetDrives does not rely on styles when it comes to displaying ToolTips, but uses a simple markup extension, that provides the following functionality:

  • Displays a nice looking ToolTip rather than just plain text.
  • Optional lookup of strings in a resource dictionary to simplify localization.
  • Optional title text.
  • Very simple declaration.

Here’s what the XAML looks like:

<Image
  Source="..SharedImagesHelpSmall.png"
  ToolTip="{ext:Info Title=ShareNameTitleToolTip, Body=ShareNameInfoToolTip}"
  />

 

…and this is the output:

image

Creating the Popup Control

The first step is to create a simple control that contains the ToolTip. Basically, this is a simple UserControl with the following features:

  • A grid with two rows. The first row contains just the title and is auto-sized to make sure it collapses if the title is not used at all.
  • Two TextBlocks for title and body text.
  • An image (I didn’t make that one bindable, I always use the same one).
  • Two dependency properties that provide binding capabilities for header and body text.

Here’s what the control looks like in Blend:

image

Of course, you can easily use this user control through styles and/or directly declared in XAML whenever you need:

<Image Source="..SharedImagesHelpSmall.png">
  <Image.ToolTip>
    <MyToolTipControl Header="My Title" Body="This is a ToolTip" />
  </Image.ToolTip>
</Image>

However, this would still take a style to hide the tooltip border and you are lacking resource file lookups. The markup extension makes this way easier…

Implementing the Markup Extension

MarkupExtension is one of the lesser known stars in WPF – it requires a little coding, but once in place, it greatly simplify things for you. A simple yet brilliant example is Dr. WPF’s ValueConverter extension, and I already blogged a few times about other applications.

This Info markup extension basically provides the following:

  • Two properties (Title and Body)
  • Resource lookup with fallback mechanism (string is used directly if it’s not a resource key)
  • ToolTip creation

 

/// <summary>
/// A markup extension that returns a
/// <see cref="InfoPopup"/> control preconfigured
/// with header and text information according to the
/// <see cref="Title"/> and <see cref="Body"/>
/// properties.
/// </summary>
public class Info : MarkupExtension
{
  /// <summary>
  /// Either a title text or a resource key that can be used
  /// to look up the title.
  /// </summary>
  public string Title { get; set; }

  /// <summary>
  /// Either a tooltips' main text or a resource key that can be used
  /// to look up the text.
  /// </summary>
  public string Body { get; set; }

  /// <summary>
  /// Empty default constructor.
  /// </summary>
  public Info()
  {
  }

  /// <summary>
  /// Inits the <see cref="Info"/> markup extension
  /// with the title and body.
  /// </summary>
  public Info(string title, string body)
  {
    Title = title;
    Body = body;
  }

  /// <summary>
  /// Performs a lookup for the defined <see cref="Title"/> and
  /// <see cref="Info"/> and creates the tooltip control.
  /// </summary>
  /// <returns>
  /// A tooltip control.
  /// </returns>
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    //create the user control that 
    InfoPopup popup = new InfoPopup();

    if (!String.IsNullOrEmpty(Title))
    {
      //look up title - if the string is not a
      //resource key, use it directly
      var result = Resources.ResourceManager.GetObject(Title) ?? Title;
      popup.HeaderText = (string)result;
    }

    if (!String.IsNullOrEmpty(Body))
    {
      //look up body text - if the string is not a
      //resource key, use it directly
      var result = Resources.ResourceManager.GetObject(Body) ?? Body;
      popup.BodyText = (string)result;
    }

    //create tooltip and make sure only the content is visible
    ToolTip tt = new ToolTip();
    tt.HasDropShadow = false;
    tt.BorderThickness = new Thickness(0);
    tt.Background = Brushes.Transparent;
    tt.Content = popup;

    return tt;
  }
}

Conclusion and Sample

This is only one of several ways to tackle the problem, but I really like that it only takes a single line to have a rich ToolTip in place. The implementation provided here may not completely suit your requirements, but it can easily be tailored to your needs.

The link below points to a sample project that contains both the markup extension and ToolTip control. Enjoy :-)

tooltip-extension.zip

 

kick it on DotNetKicks.com


  1. April 20th, 2009 at 14:18 | #1

    Thx for this article, very interesting !

  2. April 21st, 2009 at 13:52 | #2

    Nice Blog…

  3. Loic Berthollet
    May 19th, 2009 at 21:43 | #3

    Very nice article, as usual…
    Be carefull, that is become to going a practice!

    But, this time, there is a little joke: the tooltip extension isn’t use in the sample window !

    So, I done it by myself, and finally, that was a good excercice!

    Loic

  4. May 19th, 2009 at 21:56 | #4

    Holy cow, Loic!
    I must have uploaded the wrong sample. Fixed now – thanks for telling me!

  5. Jeffrey T. Whitney
    July 16th, 2009 at 20:10 | #5

    Question: Is there any way to do this without having to hard-code the resource file? I have a composite application that is really a set of different apps. I would like to put the control in a UI.Common assembly and be able to set the resource file to use for each application.

  6. lauri
    April 8th, 2010 at 09:26 | #6

    It would be even more nicer if tooltip would have pointing arrows

  7. Legator
    September 2nd, 2010 at 18:07 | #7

    Hello Philipp,
    I Have a Litte Questen.

    You write in ur article, that it’s possbile to view the tooltip this Way.

    But the XAML Keyword “MyToolTipControl” is unknown.

    Hope you can help me.

    Greeting

  1. April 21st, 2009 at 13:32 | #1
  2. May 5th, 2009 at 23:13 | #2
  3. January 1st, 2010 at 05:28 | #3
  4. November 12th, 2013 at 16:15 | #4