Earlier I introduced a series of posts entitled
10 reasons to consider WPF for your next desktop application. If you haven't read the intro yet, better head on back and
read it first.
WPF's ControlTemplates didn't make my
10 reasons you should consider WPF for your next desktop application but it was close. Perhaps they should have.
I feel like I'm keeping a dirty little secret from you and I can't do it anymore. In
Reason 3. we took a look at DataTemplates and inside the DataTemplate we created a little 'bar' to show the CustomerRating for each of the books:
This was created using the following Xaml inside our DataTemplate
<Border BorderBrush="Black" BorderThickness="1" Width="100" Height="20" Background="White"
SnapsToDevicePixels="True" HorizontalAlignment="Left">
<Viewbox Stretch="Fill">
<Border Width="5" BorderThickness="0">
<Rectangle Width="{Binding CustomerRating}" Fill="Orange"
Height="10" HorizontalAlignment="Left"/>
</Border>
</Viewbox>
</Border>
Note that the Rectangle's Width property is bound to our Customer Rating. Because this varies from 0 to 5 we put it inside a Border that is fixed to 5 pixels (or rather, device independent units) and then we put the whole thing in a Viewbox to scale it up.
And this works pretty well but if we look closely at exactly what it is we're trying to do - we'll see that
there is already a control in WPF that is built to do just this. The
ProgressBar. Seriously!

This was created using the following Xaml inside our DataTemplate
<ProgressBar Value="{Binding CustomerRating}" Minimum="0" Maximum="5" />
Notice how we specify a minimum (0), a maximum (5) and bind the Value to our CustomerRating.
The only problem is we don't want to use ProgressBars that actually look like ProgressBars!
Guess what? as with most things in WPF: "where there's a want there's a way"
ControlTemplates
Yup, we can reskin the ProgressBar to look how we want it to using a ControlTemplate:
<ControlTemplate x:Key="ratingBarTemplate" TargetType="{x:Type ProgressBar}">
<Border Name="PART_Track" Width="{TemplateBinding Width}" BorderBrush="Black" BorderThickness="1" SnapsToDevicePixels="True">
<Rectangle Name="PART_Indicator" HorizontalAlignment="Left" Fill="Orange" Height="{TemplateBinding Height}" />
</Border>
</ControlTemplate>
Note that this has an x:Key attribute - that's because we're using it as a resource. I've talked about Resources in Xaml before here:
Xaml. Using Resources. Don't worry, it's easy.
Now we apply this to our ProgressBar (inside our DataTemplate) like so:
<ProgressBar Template="{StaticResource ratingBarTemplate}" ...
And hey presto!

Extending the ProgressBar
I'm not entirely happy yet. I want to be able to change the Fill colour of our orange rectangle on the ProgressBar object itself, something like this:
<ProgressBar Fill="Orange" ...
But since the ProgressBar has no such property this isn't possible. It's easily solved though, we can simply inherit from ProgressBar to create our own RatingBar control.
public class RatingBar : ProgressBar
{
public static DependencyProperty FillProperty =
DependencyProperty.Register("Fill",
typeof(Brush),
typeof(RatingBar),
new FrameworkPropertyMetadata(
Brushes.Orange,
FrameworkPropertyMetadataOptions.AffectsRender));
public Brush Fill
{
get
{
return (Brush) GetValue(FillProperty);
}
set
{
SetValue(FillProperty, value); }
}
}
}
Now we just need to add another TemplateBinding to our rectangle inside the control template
<Rectangle Name="PART_Indicator" HorizontalAlignment="Left" Fill="{TemplateBinding Fill}" Height="{TemplateBinding Height}" />
To finish, we just use the RatingBar instead of the ProgressBar.
Bingo.
As promised, I will be releasing the source for the examples for all 10 reasons at the end of the series. The source and example in
Reason4 includes this stuff as it builds on the pieces in Reason3.

Post By
Josh Twist
04:01
19 Oct 2007
» Next Post:
Reason 4. Triggers
« Previous Post:
Reason 3. DataTemplates