Automated Tool Tip for TextBlocks in DataTemplates

We had a long standing problem for WPF Screens in our application where tool tips on TextBlocks appeared on MouseOver, regardless of text truncation, when the tool tip was set to the Text property.  Those familiar with the Label in WinForms, will be aware that when truncated, it displays ellipses and a tooltip will appear on MouseOver.  When not truncated, no tooltip appears on MouseOver.

In order to replicate this behaviour in WPF screens I have added the TextBlockService class (attached) to our solution, cobbled together from code posted on the fantastic Tranxition Developer Blog, that registers a couple of attached properties and a ClassHandler (event handler)  with the WPF Framework.  See Customizing “lookful” WPF controls – Take 2 on the Tranxition Developer Blog for a detailed breakdown of the code in the TextBlockService.

These attached properties are applied to all TextBlocks via a MultiTrigger in an implicit style I have created in the attached TextBlockStyles.xaml.  In order to get the behaviour described above, the tool tip needs to be removed from the TextBlock.  You can also remove the text trimming property and vertical alignment property (if set to center) since I have also added those to the style, e.g.

           <TextBlock Text="{Binding Path= Person.Surname}
                     VerticalAlignment="Center
                     TextTrimming="CharacterEllipsis
                     ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>

Becomes:

           <TextBlock Text="{Binding Path= Person.Surname}"/>

(Caveat: This approach only applies if the tooltip needs to be set to the text property for truncated TextBlocks).

Unfortunately Microsoft, in their ultimate wisdom, have not enabled implicit styles to be passed into DataTemplates.  Microsoft has this to say about this apparent ‘feature’! 

"This behavior is 'By Design' and this is why. Templates are viewed as an encapsulation boundary. Elements produced by these templates fall within this boundary. And lookup for a style with a matching TargetType stops at this boundary. Hence the TextBlock in the repro which is produced through a template does not pick up the Style in question. Whereas the TextBlock defined outside the template does.
One way to work around this problem is to give an explicit name to the Style and reference the style by this name on the TextBlock within the template.

They also say:

Templates are viewed as an encapsulation boundary when looking up an implicit style for an element which is not a subtype of Control.

...which explains why a Button (which is a subtype of Control) in DataTemplates does implement an implicit Button style, but TextBlock (which is not a subtype of Control, but inherits directly from FrameworkElement) does not implement an implicit TextBlock style.

Anyway, referring to the first statement, I have implemented a keyed style in TextBlockStyles.xaml with a key of “TextBlockWithAutoToolTip” that is based on the implicit TextBlock style.  So to apply this to a DataTemplate you must add the reference to the merged dictionaries of the Form/UserControl/View:

    <ResourceDictionary Source="/RRA.Beacon.Recruiter.UI.Controls;component/Styles/TextBlockStyles.xaml" />

And then the TextBlock becomes:

          <TextBlock Text="{Binding Path=PersonSummary.Surname}"

                     Style="{StaticResource TextBlockWithAutoToolTip}"/>

From the above statement regarding “Templates are viewed as an encapsulation boundary”, you would think you could just include a typed style in the DataTemplate itself and then it would apply within that template.  Unfortunately this doesn’t work and you have to apply a keyed style on each TextBlock!

You will find TextBlockService.cs and TextBlockStyles.xaml files in the attached zip.

November 26 2009
blog comments powered by Disqus