Rants and Raves

Thanks for visiting my blog!

Using Content in ControlTemplates
Using Content in ControlTemplates
December 1, 2008

Silverlight Logo
I often get questions about how the ContentPresenter works in ControlTemplates. There are certainly oddities to how it works in practice, but this is some additional information that should help you make sense of it.

By default, the purpose of the ContentPresenter is to allow you to have a placeholder for the content (whatever the content). For example, a simple Button ControlTemplate (note the ContentPresenter):

<ControlTemplate TargetType="Button">
  <Grid>
    <Rectangle Fill="#FFFF0000"
               Stroke="#FF000000"
               RadiusX="6"
               RadiusY="6" />
    <ContentPresenter HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      Content="{TemplateBinding Content}" />
  </Grid>
</ControlTemplate>

Note that the Content property is bound using the TemplateBinding to the Content of the original Button. This allows you to specify whatever you want as the content of this button (whether its simple text, or a complex set of XAML). The ContentPresenter becomes a placeholder for that content. Works great.

But there is a problem that many students run into quickly: it is difficult to animate the color of the content based on the Visual State Manager (e.g. during a MouseOver or MouseDown event). The ContentPresenter control does not have a Foreground property that would be useful to animate.  So how do we do this?

By default, Blend 2 adds a ContentPresenter when you override the ControlTemplate because it knows you need it.  The fact is that you can replace the ContentPresenter with a ContentControl and have access to more properties including the necessary Foreground property:

<ControlTemplate TargetType="Button">
  <Grid>
    <Rectangle Fill="#FFFF0000"
               Stroke="#FF000000"
               RadiusX="6"
               RadiusY="6" />
    <ContentControl HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Content="{TemplateBinding Content}"
                    Foreground="{TemplateBinding Foreground}"                     
                    x:Name="contentControl" />
  </Grid>
</ControlTemplate>

By changing this to a ContentControl, you can bind the Foreground of the content (assuming the designers do not specifically specify a Foreground). You still need to use a TemplateBinding to bind the Content property. Most importantly you can use the Visual State Manager to animate the Foreground so you can change the colors or brushes during the different states:

<vsm:VisualStateManager.VisualStateGroups>
  <vsm:VisualStateGroup x:Name="FocusStates">
    <vsm:VisualState x:Name="Unfocused" />
    <vsm:VisualState x:Name="Focused" />
  </vsm:VisualStateGroup>
  <vsm:VisualStateGroup x:Name="CommonStates">
    <vsm:VisualState x:Name="MouseOver">
      <Storyboard>
        <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
    Duration="00:00:00.0010000"
    Storyboard.TargetName="contentControl"
    Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)">
          <SplineColorKeyFrame KeyTime="00:00:00"
                               Value="#FFFFFFFF" />
        </ColorAnimationUsingKeyFrames>
      </Storyboard>
    </vsm:VisualState>
    <vsm:VisualState x:Name="Normal" />
    <vsm:VisualState x:Name="Pressed" />
    <vsm:VisualState x:Name="Disabled" />
  </vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>

Make sense?