design: XAML image template-swapping part 3: switching images in a style
November 29, 2009 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.
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 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!
deep gray sea