design: XAML image template-swapping part 3: switching images in a style
This is the third of three articles on how to create and use vector images from
Adobe Illustrator as XAML art in a WPF project. We're going to wire up our imported
images in a way that lets a ContentControl switch templates based on
its parent button's state. We learned in the
first article how to create images
for importing using Adobe Illustrator. The
second article had us laying out the simple
bones of a WPF project and importing our images into a Resource Dictionary using
Expression Blend 3.0. If you Googled your way here, you probably want to skim through
those two articles first.
creating a style
Begin by creating a new style for the button's ContentControl in the
Resource Dictionary. Expression Blend can help us out with the first part of this
operation. Highlight the button’s ContentControl, then choose
Object | Edit Style... | Edit a Copy...
from the menu. Call the style “SplatButtonImageStyle” or something like
that and place the style into our Resource Dictionary. Here’s what this dialog looks like:
The style created looks like this:
<Style x:Key="SplatButtonImageStyle"
TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The button's ContentControl XAML code changes to this:
<ContentControl x:Name="contentImageStates" Width="24" Height="24"
Style="{DynamicResource SplatButtonImageStyle}"/>
If you run the program now, you'll see a splat image in the middle of the button.
What our style does is to tell the ContentControl to use the SplatNormal
template that we previously loaded from an Illustrator import. But there’s a bit of a problem:
if you uncheck the “Enable button” checkbox, the picture still looks enabled:
setting the disabled image using a trigger
Let's make wiring up a disabled state image for the button be our next step. To make this work,
we'll need to add a DataTrigger to the style that swaps out the image when the button's
enabled state changes. Take a look at how this is done, then we'll talk through it.
<Style x:Key="SplatButtonImageStyle"
TargetType="{x:Type ContentControl}">
<Setter Property="Template" Value="{StaticResource SplatNormal}"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}, AncestorLevel=1},
Path=IsEnabled}" Value="False">
<Setter Property="Template"
Value="{StaticResource SplatDisabled}"/>
</DataTrigger>
</Style.Triggers>
</Style>
We've added a Style.Triggers section, with a single DataTrigger within.
The Binding on this trigger is to the IsEnabled property of a button. The
button is located as being an “ancestor” (a parent) of the Content Control,
one level up. This points the DataTrigger right at our button. When the trigger's condition
is satisfied (IsEnabled=false), the Content Control's template property is exchanged for
SplatDisabled. This is the disabled state image resource we imported previously.
Now when we run the application and uncheck the "Enable button" checkbox, we see a disabled state image:
setting the pressed-state image using a trigger
Our last step is to create another DataTrigger that shows a lit state image when the
button is pressed. This works exactly the same way as the disabled state trigger did. The value
we want to test for is IsPressed. The finished Style follows:
<Style x:Key="SplatButtonImageStyle"
TargetType="{x:Type ContentControl}">
<Setter Property="Template" Value="{StaticResource SplatNormal}"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}, AncestorLevel=1},
Path=IsPressed}" Value="True">
<Setter Property="Template" Value="{StaticResource SplatLit}"/>
</DataTrigger>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}, AncestorLevel=1},
Path=IsEnabled}" Value="False">
<Setter Property="Template"
Value="{StaticResource SplatDisabled}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Now the image changes to the lit state when the button is pressed:
Hmm, not much of a lit state image. If this were a real application, I’d go back and rework it.
wrapping it up
Now you know the basic techniques of preparing source art, importing it into a Content Control Template, and displaying the appropriate image using Data Triggers in a WPF application's Style. Some additional uses of this technique include:
-
Connecting a lit state image to the
IsCheckedproperty of a Toggle Button - Adding a rollover image to a button
- Extending a Button in code to add application-specific Dependency Properties and creating Data Triggers that bind to them
- Binding the Data Trigger to some other object property held by a Window or UserControl a couple ancestor levels up
Hope this helps, have fun out there!