Infosys Microsoft Alliance and Solutions blog

« Silverlight - missed compiler error | Main | Webinar - Modernizing from Mainframes to Next-generation IT Systems »

Silverlight - Getting Tooltip to work for individual ListBox Items

 The other day for a project work that we were doing in Silverlight 2.0 Beta 2, I was trying to get a tooltip to display for each item in the ListBox control. Things seemed to work fine if the content to be displayed in the tooltip was pretty much static or not dependant on individual items. I mean, I had a need that the tooltip should display different text per item, essentially data bind to some value for each item.

Does this makes sense? I guess it does to me, but you may be wondering about what exactly am I trying to achieve? Let me explain that with sample code. Below is the trivialized version of the code I was working on. There is a list of employees, who I am displaying in a ListBox.

The C# code is

namespace MTCTrackerApp

{

    public partial class UserControl1 : UserControl

    {

        public UserControl1()

        {

            // Required to initialize variables

            InitializeComponent();

        }

    }

 

    public class Employees : List<Employee>

    {

        public Employees()

        {

            Add(new Employee { FirstName = "Atul", LastName = "Gupta", Designation = "PTA" });

            Add(new Employee { FirstName = "Chandan", LastName = "Gokhale", Designation = "PTA" });

            Add(new Employee { FirstName = "Raghu", LastName = "Vempati", Designation = "TS" });

        }

    }

 

    public class Employee

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Designation { get; set; }

    }

}

and XAML is

<UserControl

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d"

   xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows"

    x:Class="MTCTrackerApp.UserControl1" xmlns:local="clr-namespace:MTCTrackerApp"

    Width="640" Height="480" >

    <UserControl.Resources>

        <local:Employees x:Key="emps" />

    </UserControl.Resources>

 

    <Grid x:Name="LayoutRoot" Background="White" >

        <ListBox Margin="120,59,0,84" Height="150" HorizontalAlignment="Left" ItemsSource="{Binding '',Source={StaticResource emps}}" VerticalAlignment="Stretch" Width="200">

            <ListBox.ItemContainerStyle>

                <Style TargetType="ListBoxItem">

                    <Setter Property="Background" Value="Gray" />

                </Style>

            </ListBox.ItemContainerStyle>

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <Grid Width="192" HorizontalAlignment="Stretch" Height="35" Background="Transparent" >

                        <controls:ToolTipService.ToolTip>

                            <ToolTip Content="Technical" />

                        </controls:ToolTipService.ToolTip>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="*" />

                            <ColumnDefinition Width="*" />

                        </Grid.ColumnDefinitions>

                        <TextBlock Margin="2" Text="{Binding FirstName}" VerticalAlignment="Center" />

                        <TextBlock Margin="2" Grid.Column="1" Text="{Binding LastName}" VerticalAlignment="Center"/>

                    </Grid>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

    </Grid>

</UserControl>

When you run this, the output looks something like the following picture. Don't worry about why selected the item container style and the data template for items and definitely not if they are best for this scenario. They are only to help show the issue I faced and how I solved it. The Grid in the data template is marked with background as Transparent to become hit test visible. 

constanttip.jpg 

As is seen in the picture, the tooltip displays just fine, but this is static content and it displays the same value for all items. Now, instead of this, If I want to display say the Desgination of each Employee in the tooltip, I will tend to use code like the following. I am only showing the modified data template (the rest of the code is the same).

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <Grid Width="192" HorizontalAlignment="Stretch" Height="35" Background="Transparent" >

                        <controls:ToolTipService.ToolTip>

                            <ToolTip Content="{Binding Designation}" />

                        </controls:ToolTipService.ToolTip>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="*" />

                            <ColumnDefinition Width="*" />

                        </Grid.ColumnDefinitions>

                        <TextBlock Margin="2" Text="{Binding FirstName}" VerticalAlignment="Center" />

                        <TextBlock Margin="2" Grid.Column="1" Text="{Binding LastName}" VerticalAlignment="Center"/>

                    </Grid>

                </DataTemplate>

            </ListBox.ItemTemplate>

With this change, the tooltip now actually doesn't displays anything as is seen in the figure below. It clearly means that the data binding isn't working corrently. Maybe the tooltip is created only when I hover on an item and at that time, there is no way the value of Degination can be obtained since there will be no indiciation of which Employee's Designation to pick up. Like in WPF, I don't know if there is a way to debug data bindings in Silverlight (if you know, do write back)

blanktip.jpg

After playing around with the idea for a while and almost on the verge of abandoning the idea of having item specific tooltip, I hit upon the following solution. See the modified XAML and code behind below.

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <Grid Width="192" HorizontalAlignment="Stretch" Height="35" Background="Transparent" Loaded="Grid_Loaded">

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="*" />

                            <ColumnDefinition Width="*" />

                        </Grid.ColumnDefinitions>

                        <TextBlock Margin="2" Text="{Binding FirstName}" VerticalAlignment="Center" />

                        <TextBlock Margin="2" Grid.Column="1" Text="{Binding LastName}" VerticalAlignment="Center"/>

                        <TextBlock Visibility="Collapsed" x:Name="dummy" Text="{Binding Designation}" />

                    </Grid>

                </DataTemplate>

            </ListBox.ItemTemplate>

 And add the event handler logic in the code behind. 

        private void Grid_Loaded(object sender, RoutedEventArgs e)

        {

            Grid grid = sender as Grid;

            TextBlock txt = grid.FindName("dummy") as TextBlock;

            ToolTipService.SetToolTip(grid, txt.Text);

        }

dynamictip.jpg

As you would have understood by looking at the code, I am handling the creating of each ListBoxItem by the way of handling the Grid's loaded event. In that I am creating a Tooltip from code behind and using the dummy TextBlock (which isn't visible but present only to help get the required value to display), I specify the content for the Tooltip and attach it to the Grid and thus achieve data binding for Tooltip indirectly.

If you know of a different way to do this, do write back. BTW, Interestingly, when using Tooltip direclty in the XAML code, the XAML files fail to load in Expression Blend 2.5 June CTP.

Comments

You actually got about 90% to a slightly more elegant solution.

You don't need the dummy text box - all you need to do is put this in your Grid_Loaded handler:
ToolTip t = ToolTipService.GetToolTip(sender as Grid) as ToolTip;
t.DataContext = (sender as Grid).DataContext;

Now your tooltip will have the same datacontext as your items and voila...you can create a ToolTip template in your xaml like you tried before and it will work.

The work you did gave me the idea though, so thank you!

Anye, thanks for sharing.

This is an excellent hack, especially including the update from Anye. It's very common for where I work to have fields that contain way too long of text fields to display so we only display the first 40 characters and show the rest in a tooltip. Great solution to a common problem for us!

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter