Monday, August 4, 2014

Extend and style a Button to display two labels in Windows Phone 8 with a Tilt animation effect

While developing a Windows Phone 8 app, I wanted to create a button that can display two lines of  text and has a tilt animation effect when pressed, similar to the standard Windows Phone UTC time zone list screen, as shown below. 
After some research, I found out how to do it by doing the following:
  • Extending the Button class to add in an additional property for the second text label
  • Create and use a new Button style resource for the extended Button class
  • Download and include the Control Tilt Effect example TiltEffect.cs file
Extend the Button class
In the Windows Phone solution, create a new class that extends the Button class, and expose a new property; I named the property as 'Content2'. 

namespace Example
{
public class TwoLabelButton : Button
{
public static readonly DependencyProperty Content2Property =
DependencyProperty.Register("Content2", typeof(string), typeof(TwoLabelButton), new PropertyMetadata(default(string)));
 
public string Content2
{
get { return (string)GetValue(Content2Property); }
set { SetValue(Content2Property, value); }
}
}
}
Define a new Button style resource
In the XAML file that I want to use the new two label extended button, create a new style resource. I name this as the TimeZoneButtonStyle. In the code snippet below, the style resource is placed in a Grid.Resources block, as I wanted to use the style in a grid layout. In the style I wanted the two labels (to be bounded to the Content and Content2 properties) to be in two TextBlocks, oriented vertically in a StackPanel. The upper TextBlock will be styled with the standard PhoneTextTitle2Style while the lower TextBlock will be styled with the standard PhoneTextSubtleStyle.


<Grid.Resources>
<Style x:Key="TimeZoneButtonStyle" TargetType="local:TwoLabelButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TwoLabelButton">
<StackPanel Orientation="Vertical">
<TextBlock
Style="{StaticResource PhoneTextTitle2Style}"
TextWrapping="Wrap"
Text="{TemplateBinding Content}"></TextBlock>
<TextBlock
Style="{StaticResource PhoneTextSubtleStyle}"
Text="{TemplateBinding Content2}"></TextBlock>                                
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
Download and include the example TiltEffect.cs file
Follow the instructions on the MSDN web site http://msdn.microsoft.com/en-us/library/ff941094 to download and include the TiltEffect.cs file in your solution.
In my case, I edited the TiltEffect.cs file and changed the namespace to be consistent with my solution. Then in the XAML file where I wanted to use the effect, I added in the page level property to enable the tilt effect for the whole page as shown in the snippet below.

<phone:PhoneApplicationPage
    x:Class="Example"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Example"
    local:TiltEffect.IsTiltEnabled="True"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    mc:Ignorable="d"
    Loaded="PhoneApplicationPage_Loaded"
    shell:SystemTray.IsVisible="True">

The complete example XAML file is shown below. To display the list of time zones, I bounded a DataTemplate to a collection of my custom TimeZone class.

<phone:PhoneApplicationPage
x:Class="Example"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Example"
local:TiltEffect.IsTiltEnabled="True"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
mc:Ignorable="d"
Loaded="PhoneApplicationPage_Loaded"
shell:SystemTray.IsVisible="True">
 
<phone:PhoneApplicationPage.Resources>
<local:TimeZones x:Key="TimeZones" />
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<Grid.Resources>
<Style x:Key="TimeZoneButtonStyle" TargetType="local:TwoLabelButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TwoLabelButton">
<StackPanel Orientation="Vertical">
<TextBlock
Style="{StaticResource PhoneTextTitle2Style}"
TextWrapping="Wrap"
Text="{TemplateBinding Content}"></TextBlock>
<TextBlock
Style="{StaticResource PhoneTextSubtleStyle}"
Text="{TemplateBinding Content2}"></TextBlock>                                
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="SELECT A TIME ZONE" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector
ItemsSource="{StaticResource TimeZones}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<!--<TextBlock Text="{Binding Path=DisplayName}" />-->
<local:TwoLabelButton
Tap="TimeZoneButton_Tap"
Style="{StaticResource TimeZoneButtonStyle}"
Content="{Binding Path=DisplayName}"
Content2="{Binding Path=ZoneOffset}"
HorizontalContentAlignment="Left" BorderThickness="0" RenderTransformOrigin="0.5,0.5">                                
</local:TwoLabelButton>
<!--<TextBlock Text="{Binding Path=ZoneOffset}" />-->
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
 
</phone:PhoneApplicationPage>

The code listing for the TimeZone class is shown below.

namespace Example
{
public class TimeZone
{
private string _displayName = string.Empty;
private string _zoneOffset = string.Empty;
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
public string ZoneOffset
{
get { return _zoneOffset; }
set { _zoneOffset = value; }
}
public TimeZone(string zoneOffset, string displayName)
{
_displayName = displayName;
_zoneOffset = zoneOffset;
}
}
public class TimeZones : List<TimeZone>
{
public TimeZones()
{
Add(new TimeZone("UTC-12:00", "International Date Line West"));
Add(new TimeZone("UTC-11:00", "Samoa"));
Add(new TimeZone("UTC-10:00", "Hawaii"));
Add(new TimeZone("UTC-09:00", "Alaska"));
Add(new TimeZone("UTC-08:00", "Pacific Time (US & Canada)"));
//...etc...
}
}
}
The following screenshot shows how the example XAML file looks rendered on a Windows Phone.


No comments: