WPF: ControlTemplate - Completely Swapping the UI of an Element.

Posted on 3/5/2007 @ 1:22 AM in #Silverlight and WPF by | Feedback | 22199 views


This post is in continuation to an explanation on WPF Templates.

1. ControlTemplate - You use this, when you want to completely redefine the visual appearance of any control. Say, you don't want a radiobutton to look like a radiobutton - you want it to look like a smiley instead. Smiling means Checked, and Frowning means Unchecked. You could easily acheive this using ControlTemplate.

2. ItemsPanelTemplate - Is a rather simple kind of template, it lets you control the appearance of the "ItemsPanel" property defined by "ItemsControl" on elements such as ListBox or ComboBox.

3. DataTemplate - is probably the most common kind of template you will use. It lets you change how "Content" is rendered on any control. So if you have an object called "Customer" and you want to define a standard look and feel for "Customer" - you'd use DataTemplate.


We've all seen a button. No not your shirt button - I'm talking about a button on the screen - a Windows Button.

Y'know, it's rectangular, with some text on it - boring as hell. What's up with that, heck even a M&M dispensing machine has a more attractive button. Actually, most windows controls have a standard "look & feel". While some folks argue that there is an advantage of this familiarity, I would argue that there is still a learning curve - of learning computers, and understanding what a button looks like, what a checkbox looks like, so on so forth.

Here is how you'd create a boring button in WPF -

<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="100" >

 

Now, what if I wanted to completely swap out the UI, but retain the functionality? Easy, use the Button.Template property as shown below -

<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="100" >

  <Button.Template>

    <ControlTemplate>

     .. specify look and feel here ..

    </ControlTemplate>

  </Button.Template>

</Button>       

 

Inside ControlTemplate, you can start specifying the look & feel of your button, this could be as creative as you want - even 3d stuff. I'm going to keep it simple and use a simple colorful rectangle to render my button -

   <ControlTemplate>

      <Rectangle RadiusX="5" RadiusY="5" Stroke="LightYellow" StrokeThickness="3" Name="myRectangle">

        <Rectangle.Fill>

          <VisualBrush Opacity="0.7">

            <VisualBrush.Visual>

              <TextBlock Name="myTextBlock" Foreground="LightYellow" Background="DarkBlue" Padding="10">Press Me!</TextBlock>

            </VisualBrush.Visual>

          </VisualBrush>

        </Rectangle.Fill>

      </Rectangle>

    </ControlTemplate>

 This causes the button to look a whole lot more attractive as shown below -

(Okay okay, this is about how creative I feel at 1 AM. But the idea being, you could use tools like Expression*.*/Zam3d to create a button that looks like whatever you wish.)

Now the problem is, a Windows Button gives the user a feedback when clicked. So it actually looks like a button is being clicked. How can I acheive the same in this totally customized button? Earlier I had blogged about "Triggers" in WPF. Well, you can use the same concept to make the above button click. Just go ahead and add the following trigger as shown below -

<ControlTemplate.Triggers>

  <Trigger Property="Button.IsPressed" Value="True">

    <Setter TargetName="myRectangle" Property="Stroke" Value="LightGreen"/>

    <Setter TargetName="myTextBlock" Property="Background">

      <Setter.Value>

        <LinearGradientBrush>

          <GradientStop Offset="1" Color="DarkBlue"/>

          <GradientStop Offset="0.5" Color="Blue"/>

          <GradientStop Offset="0" Color="DarkBlue"/>

        </LinearGradientBrush>

      </Setter.Value>

    </Setter>

  </Trigger>

</ControlTemplate.Triggers>

Great, now the full button's XAML looks like this (font size reduced - because I know you're gonna copy paste this into XAMLPad or sump'n) -

<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="100" >

  <Button.Template>

    <ControlTemplate>

      <Rectangle RadiusX="5" RadiusY="5" Stroke="LightYellow" StrokeThickness="3" Name="myRectangle">

        <Rectangle.Fill>

          <VisualBrush Opacity="0.7">

            <VisualBrush.Visual>

              <TextBlock Name="myTextBlock" Foreground="LightYellow" Background="DarkBlue" Padding="10">Press Me!</TextBlock>

            </VisualBrush.Visual>

          </VisualBrush>

        </Rectangle.Fill>

      </Rectangle>

      <ControlTemplate.Triggers>

        <Trigger Property="Button.IsPressed" Value="True">

          <Setter TargetName="myRectangle" Property="Stroke" Value="LightGreen"/>

          <Setter TargetName="myTextBlock" Property="Background">

            <Setter.Value>

              <LinearGradientBrush>

                <GradientStop Offset="1" Color="DarkBlue"/>

                <GradientStop Offset="0.5" Color="Blue"/>

                <GradientStop Offset="0" Color="DarkBlue"/>

              </LinearGradientBrush>

            </Setter.Value>

          </Setter>

        </Trigger>

      </ControlTemplate.Triggers>

    </ControlTemplate>

  </Button.Template>

</Button>       

NEAT, now lets throw in some glassy effect, and run the app. 

Not Clicked -

Clicked -

NEAT! :-)

Sound off but keep it civil:

Older comments..


On 2/27/2009 5:57:41 PM Tracy said ..
Application wide, I want to to let the designer's specify if data entry is mandatory at design time... I intent to use an attached property on the label called "IsMandatory".

When IsMandatory=True, I would like a red asterisk to appear directly beside the label.

I am trying to update my Label.XAML control template to have a ControlTemplate Trigger.

I essentially, need to replace the entire template when this value is set at runtime.

Here is a sample of my XAML. This causes an exception at runtime.

I was hoping to use a combination of AttachedProperties and ControlTemplat.

Is this possible?

<Style x:Key="{x:Type Label}" TargetType="Label">


<Setter Property="HorizontalContentAlignment" Value="Left"/>


<Setter Property="VerticalContentAlignment" Value="Top"/>


<Setter Property="Template">


<Setter.Value>


<ControlTemplate TargetType="Label">


<Border>


<ContentPresenter


HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"


VerticalAlignment="{TemplateBinding VerticalContentAlignment}"


RecognizesAccessKey="True"/>


</Border>


<ControlTemplate.Triggers>


<Trigger Property="IsEnabled" Value="false">


<!--<Setter TargetName="Border" Property="Background" Value="{x:Null}"/>-->


<!--<Setter TargetName="Border" Property="BorderBrush" Value="{x:Null}"/>-->


<Setter Property="Control.Template">


<Setter.Value>


<ControlTemplate>


<DockPanel LastChildFill="True">


<TextBlock DockPanel.Dock="Right"


Foreground="Red"


FontSize="11pt"


FontWeight="Bold">*


</TextBlock>


<Border BorderBrush="Red" BorderThickness="1">


<AdornedElementPlaceholder Name="myControl"/>


</Border>


</DockPanel>


</ControlTemplate>


</Setter.Value>


</Setter>


</Trigger>

</ControlTemplate.Triggers>


</ControlTemplate>


</Setter.Value>


</Setter>


</Style>


On 5/11/2011 4:01:42 PM Prince said ..
A very gud tutorial...how did u add the glass effect to it and can u help me out with a custom control for timepicker in WPF 3.5 visual Studio 2008