Skip navigation

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:

Creating a new style resource.

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:

The button is disabled, but not the image.
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:

Now the image is disabled when the button is.
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:

The rather pathetic lit image now appears 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 IsChecked property 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!

D
D