WPF Simplified Part 5: Control Templates

Recall from my previous post, that elements in the WPF object tree which derive from Visual constitute the visual tree. The visual tree is thus all the WPF elements that come together to render a control’s (or a set of controls’) UI to the screen. Control templates are XAML declarations of the visual tree of a control.

Consider the simple case of a Button control,

<Grid>
        <!-- A simple button -->
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

The Button has a default visual template, ie, a visual tree supplied by WPF. We want to override this implementation and provide our own custom UI for the button. To do this we’ll customize the Button control’s UI template (and thus it’s visual tree).

This is simple enough,

<Grid>    
    <Button Height="100" Width="100" Content="Some Text">

        <!-- Override the Button's UI template -->
        <Button.Template>
 
            <!-- Create a new ControlTemplate that targets a Button -->
            <ControlTemplate TargetType="{x:Type Button}">
 
                <!-- Override the default UI of the control with this custom UI -->
                <Grid>
                    <Ellipse Height="60" Width="60" Stroke="Red" Fill="LightGray" />
                    <!-- Take the control's Content (which is “Some Text”) and place here -->
                    <ContentPresenter Content="{TemplateBinding Content}" 
                           VerticalAlignment="Center" HorizontalAlignment="Center" />
                </Grid>
            </ControlTemplate>
        </Button.Template>
 
    </Button>
</Grid>
 

As you can see from the XAML above, we’ve overridden the button’s UI template and provided our own implementation which consists of a Grid, with an Ellipse inside it and the Button’s Content inside the Ellipse. Even the height and width of the button get overridden to the new values of 60.

image image
           Default WPF Button       After providing a custom UI 
      template for the button

 

Another way to do the same thing is similar to the way we define styles in WPF, like below,

<Grid>
    <Grid.Resources>
        <!-- Define a named template, with a target type -->
        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <!-- Override the default UI of the control with this custom UI -->
            <Grid>
                <Ellipse Height="60" Width="60" Stroke="Red" Fill="LightGray" />
                <!-- Take the control's Content and place here -->
                <ContentPresenter Content="{TemplateBinding Content}" 
                        VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>
    </Grid.Resources>

    <!—- Apply the 'RoundButtonTemplate' template to this button -->
    <Button Height="100" Width="100" Content="Some Text"
            Template="{DynamicResource RoundButtonTemplate}" />

    </Grid>

Note that although we have overridden the UI, the button’s functionality remains the same, all the same events are raised and all the same properties are still available.

You should use TemplateBinding to allow the control to specify property values as much as possible. For example,

<Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <Grid>
                <!-- The Stroke and Fill properties are bound to the button's BorderBrush and Background -->
                <Ellipse Height="60" Width="60"
                         Stroke="{TemplateBinding BorderBrush}"
                         Fill="{TemplateBinding Background}" />

                  <ContentPresenter Content="{TemplateBinding Content}" 
                        VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>
    </Grid.Resources>

    <!-- Apply the 'RoundButtonTemplate' template to this button -->
    <Button Height="100" Width="100" Content="Some Text"
            Template="{DynamicResource RoundButtonTemplate}"
            BorderBrush="Red" Background="LightGray" />

</Grid>

 

Control templates also work with styles and triggers. The syntax is straightforward,

<Grid>
    <Grid.Resources>

        <!-- First ControlTemplate definition for normal button state UI -->

        <ControlTemplate x:Key="RoundButtonTemplate" TargetType="{x:Type Button}">
            <Grid>
                <Ellipse Height="60" Width="60" x:Name="MyEllipse"
                         Stroke="{TemplateBinding BorderBrush}"
                         Fill="{TemplateBinding Background}" />
                <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
            <!-- Set a trigger to activate on mouseover -->
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="MyEllipse" Property="Fill" Value="Blue" />
                </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>

        <!-- Second ControlTemplate definition for clicked button state UI -–> 
        <ControlTemplate x:Key="RoundButtonPressedTemplate" TargetType="{x:Type Button}">
            <Grid>
                <Ellipse Height="60" Width="60"
                     Stroke="{TemplateBinding BorderBrush}"
                     Fill="Red" />
                <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Grid>
        </ControlTemplate>

        <!-- Create a style definition for the button -->

        <Style x:Key="RoundButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Template" Value="{DynamicResource RoundButtonTemplate}"/>
            <Style.Triggers>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Template" Value="{DynamicResource RoundButtonPressedTemplate}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>

    <!-- Apply the 'RoundButtonStyle' to this button -->
    <Button Height="100" Width="100" Content="Text"
            Style="{DynamicResource RoundButtonStyle}"
            BorderBrush="Red" Background="LightGray" />

</Grid>

The WPF  content model allows the Button’s content to be a tree of elements. In our examples, we have set the button’s content to “Some Text” like this,

<Button Height="100" Width="100" Content="Some Text" />

But the button’s Content could be set to a tree, like this,

<Button Height="100" Width="100">
    <Button.Content>
        <Grid>
            <StackPanel>
                <TextBlock Text="Some Text"/>
            </StackPanel>
        </Grid>
    </Button.Content>
</Button>
About these ads

About soumya chattopadhyay
I live and work in Seattle, WA. I work with Microsoft technologies, and I'm especially interested in C#.

3 Responses to WPF Simplified Part 5: Control Templates

  1. Pingback: WPF Simplified Series « I.Net

  2. Pingback: WPF Simplified Part 15: Data Validation « I.Net

  3. Paras Saini says:

    Why you used Dynamic resource in button declaration like:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: