This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html
It's possible to derive one Style from another, in the process inheriting all the Setter objects. The new Style can add to those Setter objects or override them. However, it is not possible to derive from a ControlTemplate. There's no way to reference an existing ControlTemplate and specify an additional piece of the visual tree, or a replacement for part of the visual tree. (It's hard enough imaging the mechanics or syntax of such a process.)
Here's a program called FlipToggleDemo that includes a custom class named FlipToggleButton that derives from ToggleButton. But FlipToggleButton doesn't add any code to ToggleButton- just a Style and ControlTemplate.
In the FlipToggleDemo project, I added a new item of type Windows Phone User Control and I gave it a name of FlipToggleButton.xaml. This process creates a FlipToggleButton.xaml file and a FlipToggleButton.xaml.cs file for a class that derives from UserControl. But then I went into both files and changed UserControl to ToggleButton so FlipToggleButton derives from ToggleButton.
To keep things simple, I decided not to implement any state transitions for a disabled button, but to flip the button upside down for the Unchecked state. Here's the complete XAML file for the custom button, with indentation reduced to 2 spaces to avoid lines wider than the pages of this book:
<ToggleButton x:Class="FlipToggleDemo.FlipToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border BorderBrush="{StaticResource PhoneForegroundBrush}"
BorderThickness="{StaticResource PhoneBorderThickness}"
Background="{TemplateBinding Background}"
RenderTransformOrigin="0.5 0.5">
<Border.RenderTransform>
<RotateTransform x:Name="rotate" />
</Border.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotate"
Storyboard.TargetProperty="Angle"
To="180" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotate"
Storyboard.TargetProperty="Angle"
Duration="0:0:0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style
</ToggleButton>
Usually when you're looking at XAML file, the bulk of the file is set to the Content property of the root element. Here the bulk of the file is set to the Style property of the root element. Notice that the Style object and the ControlTemplate object both have TargetType set to ToggleButton rather than FlipToggleButton. This is fine because neither references any properties specifically defined by FlipToggleButton because FlipToggleButton does not define any new properties.
The two animations that flip the button upside down (for the Checked state) and back (for the Unchecked state) have non-zero times. The DoubleAnimation for the Checked state has no From value; it uses the base value of the property, which is zero. The DoubleAnimation for the Unchecked state has neither a To or From value! The animation starts at whatever value the Angle property of the RotateTransform happens to be-probably 180 but perhaps something lower if the animation to flip the button hasn't quite completed when the button is unchecked-and it ends at the base value, which is zero.
Here's the complete code-behind file for the custom control:
using System.Windows.Controls.Primitives;
namespace FlipToggleDemo
{
public partial class FlipToggleButton : ToggleButton
{
public FlipToggleButton()
{
InitializeComponent();
}
}
}
The MainPage.xaml file of the project instantiates the custom button to test it out:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<local:FlipToggleButton
Content="Flip
Toggle"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
Custom Controls in a Library
Generally when you create a custom control, you define some new properties for the control as well as a default Style and ControlTemplate, and you put that new control in a DLL for sharing among multiple applications. You can couple the code and Style as shown in the FlipToggleButton example, but a more standard approach for Silverlight libraries involves defining the Style in a special file named generic.xaml located in a directory named Themes. This generic.xaml file has a root element of ResourceDictionary.
Suppose you conceive of a ToggleButton template something like the one in the CustomButtonTemplate project but much more generalized. Rather than just switch between two hard-coded text strings, you want to switch between two objects of any type. And not just switch-you want an object associated with the Checked state and an object associated with the Unchecked state to fade from one to the other. Your name for this new button is called FadableToggleButton.
I defined this class in the Petzold.Phone.Silverlight library. The complete code for FadableToggleButton is here:
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Petzold.Phone.Silverlight
{
public class FadableToggleButton : ToggleButton
{
public static readonly DependencyProperty CheckedContentProperty =
DependencyProperty.Register("CheckedContent", typeof(object),
typeof(FadableToggleButton), new PropertyMetadata(null));
public FadableToggleButton()
{
this.DefaultStyleKey = typeof(FadableToggleButton);
}
public object CheckedContent
{
set { SetValue(CheckedContentProperty, value); }
get { return (object)GetValue(CheckedContentProperty); }
}
}
}
This is the only C# code required to implement this control! There's not even a property-changed handler for this new CheckedContent property. It's just a DependencyProperty definition and a CLR property definition. Everything else is XAML.
Silverlight looks in a very special XAML file in the library. This XAML file is always named generic.xaml and it is always located in a directory named Themes of the DLL project. This is how a control gets a default theme style and template.
This generic.xaml file has a root element of ResourceDictionary. However the file is special in another way: The contents are regarded as resources but the Style elements don't require x:Key or x:Name attributes because they are referenced via the TargetType.
Here's the portion of the generic.xaml file in the Themes directory of Petzold.Phone.Silverlight that contains the default Style definition of the FadableToggleButton class:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Petzold.Phone.Silverlight">
<Style TargetType="local:FadableToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:FadableToggleButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="disableRect"
Storyboard.TargetProperty="Opacity"
To="0.6" Duration="0:0:0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="uncheckedContent"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="checkedContent"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="uncheckedContent"
Storyboard.TargetProperty="Opacity"
Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="checkedContent"
Storyboard.TargetProperty="Opacity"
Duration="0:0:0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{StaticResource PhoneForegroundBrush}"
BorderThickness="{StaticResource PhoneBorderThickness}"
Background="{TemplateBinding Background}">
<Grid Margin="{TemplateBinding Padding}">
<ContentPresenter Name="uncheckedContent"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}" />
<ContentPresenter Name="checkedContent"
Opacity="0"
Content="{TemplateBinding CheckedContent}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}" />
</Grid>
</Border>
<Rectangle Name="disableRect"
Fill="{StaticResource PhoneBackgroundBrush}"
Opacity="0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>





Join the conversation! Your thoughts help the community grow.