Introduction
In this blog I want to demonstrate several things. The first is how to reveal a text message in a ProgressBar. This is easy to do and you can get reasonably close to the moving marquee effect that you can see on some digital displays.
A more important topic that I'll cover is TemplateBinding in ControlTemplates. This makes it easy to use a core template, but allow individual instances of the control type that use the template to tweak selected properties. In this case I'll use the FontSize property.
Finally, you sometimes come across a situation where the control (in this case the ProgressBar) doesn't have a property that maps directly to a property that exists in the ControlTemplate. Although this might seem improbable, it actually happens quite often. We'll look at how we can work round this limitation and in the example I will show you can set a Content property on a ProgressBar control, even though the ProgressBar doesn't actually possess such a property.
If you've read my last three or four blogs you may be thinking that I'm a bit obsessed with ProgressBars! That's not the case and it's simply that they represent a good case study for ControlTemplates, they require named parts and I think they make a nice change from the demos that mostly seem to use Buttons.
The ControlTemplate
In the previous blogs on ProgressBars, you will have seen that I have stored the ControlTemplates in the Window.Resources collection. This time, the ControlTemplate is going to be stored in the Application.Resources collection. The thinking behind this is that this template is designed to be reused in several Windows across the application, with different values being placed on some properties in some instances. So having it in a central location clearly makes it available application-wide and makes sense.
Here is the markup for the initial version of the ControlTemplate:
<ControlTemplate x:Key="PBWordReveal" TargetType="{x:Type ProgressBar}">
<Grid>
<Border Name="PART_Track"
BorderThickness="2" CornerRadius="5"
Background="LightSkyBlue" BorderBrush="Navy" />
<ContentControl Name="PART_Indicator"
Content=" Loading . . . . Loading . . . . . Loading"
Margin="4,0" FontSize="14"
Foreground="Black" Background="LightSkyBlue"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
</Grid>
</ControlTemplate>
If you are not familiar with the concept of named parts – specifically the PART_Track and PART_Indicator shown above – you get an explanation in my previous ProgressBar blogs.
The outer track is a Border element, while the Indicator uses a ContentControl. The key property is probably the Content property, which as you can see contains the text that will be displayed. The HorizontalAlignment property is also quite important to the look of the final effect.
ProgressBar Instance
To use this template, I will create a ProgressBar instance in a Window.
<ProgressBar Name="PBReveal"
Template="{StaticResource PBWordReveal}"
Width="200" Height="40" />
To animate the ProgressBar in the code-behind of the Window (in this example, a Window named 'WordReveal':
Imports System.Windows.Media.Animation
Partial Public Class WordReveal
Private Sub WordReveal_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim a As New DoubleAnimation
a.From = 0
a.To = 100
a.Duration = New TimeSpan(0, 0, 6)
PBReveal.BeginAnimation(ProgressBar.ValueProperty, a)
End Sub
End Class
When you run this project, the text content in the ProgressBar will be revealed from left to right. The screenshot shows the bar with the animation approx 60% complete:

And this is the display when the animation has ended:

Effectively all that is happening is that the ContentControl, the PART_Indicator element of the template, is revealed over the period of the animation – the revelation commencing from left to right.
If you want more of a marquee type effect, you can change the HorizontalAlignment :
HorizontalAlignment="Right"
This will make the text scroll from right to left:

Setting the HorizontalAlignment to either Center or Stretch will produce a different result again.
TemplateBinding
TemplateBinding allows you to hand back control of selected properties to the instance of the ProgressBar that is using this ControlTemplate. It is simple to implement and in this example only requires the change shown below to the PART_Indicator named part of the template:
<ContentControl Name="PART_Indicator"
Content=" Loading . . . . Loading . . . . . Loading"
Margin="4,0"
FontSize="{TemplateBinding FontSize}"
Foreground="Black" Background="LightSkyBlue"
VerticalAlignment="Center"
HorizontalAlignment="Right"
/>
The TemplateBinding points to a specific property – FontSize. This can now be set in an instance of the control that uses the template:
<ProgressBar Name="PBReveal"
Template="{StaticResource PBWordReveal}"
Width="200" Height="40"
FontSize="22"/>
Note that if you choose not to set a value on the FontSize property in the ProgressBar instance, the default value for that property will be used (12).
Mapping Between Properties
As you can see from the markup, the PART_Indicator uses a ContentPresenter. The ContentPresenter has a Content property and this is what is used to set the value of the text to be displayed. The basic ProgressBar however doesn't have such a property. So if you want to include TemplateBinding to allow different text to be used in different instances that use this template, you need to map the ContentPresenter property to some other property of the ProgressBar.
In most cases, if you have only one property that needs this kind of mapping, the solution is simple. You can use the Tag property:
<ContentControl Name="PART_Indicator"
Content="{TemplateBinding Tag}"
Margin="4,0"
FontSize="{TemplateBinding FontSize}"
Foreground="Black" Background="LightSkyBlue"
VerticalAlignment="Center"
HorizontalAlignment="Right"
/>
And you insert the required text in the markup for the ProgressBar instance :
<ProgressBar Name="PBReveal"
Template="{StaticResource PBWordReveal}"
Width="200" Height="40"
FontSize="22"
Tag=" Please Wait . . . . . . . . . ."/>
These two templated changes result in the following display:

As you can see, both the FontSize and the Content have changed.
If you need to include more than one of these kind of cross property mappings, there is nothing (apart from common sense and experience) stopping you from hijacking other ProgressBar properties. You can identify a property that you absolutely, categorically know won't be used (and there's the start of the slippery slope right there) you can use this to map to.
The more professional alternative, of course, is to create a custom control of your own which contains all the properties you desire. I'll be looking at the steps involved in creating a custom control in a future blog.







