What Is XAML?
XAML is the acronym for Extensible Application Markup Language. It’s a markup language based on XML, and its purpose and philosophy are very similar to HTML. Every control that can be placed on a page, whether a button, text box, or custom controls, is identified by a specific XML tag. Like XML, the structure is hierarchical; you can place tags inside other tags. For example, this hierarchical structure is how you can define the layout of a page, thanks to some controls that act as a container for other controls, like Grid
or StackPanel
.
The following is a sample of the XAML that defines a Windows Phone page:
<phone:PhoneApplicationPage x:Class="FirstApp.MainPage" 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" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <TextBlock Text="This is a page" /> </StackPanel> </Grid> </Grid> </phone:PhoneApplicationPage>
PhoneApplicationPage
is the base class of a Windows Phone page. As you can see, every other control is placed inside it. Notice also the x:Class
attribute; it identifies which is the code-behind class that is connected to this page. In this sample, the code that is able to interact with the page will be stored in a class called MainPage
that is part of the FirstApp
namespace. We can see this class simply by clicking on the black arrow near the XAML file in Solution Explorer. You’ll see another file with the same name of the XAML one plus the .cs
extension.
Let’s begin analyzing this simple XAML to introduce some key concepts, like namespaces and resources.
Namespaces
You should already be familiar with namespaces; they’re a way to structure your code by assigning a logical path to your class.
By default, Visual Studio assigns namespaces using the same folder structure of the project. This means that if, for example, you have a class called MyClass
stored inside a file in the Classes
folder, the default full namespace of your class will be Classes.MyClass
.
Namespaces in XAML work exactly the same way. The XAML controls are, in the end, classes that are part of your project, so you have to tell the page where it can find them. In the standard page you can see many examples of namespace declarations:
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
Every namespace starts with the xmlns
prefix, which is the standard XML namespace, followed by a custom prefix (in this sample it’s phone
). This prefix is very important, because it’s the one that we’re going to use in the rest of the page to add the controls. Then, we define the full namespace that contains the controls. If the class is part of our project, it’s enough to specify just the namespace; otherwise, we also need to define which assembly (that is the DLL’s name) contains the class.
In the previous example, we want to include controls and resources in our page that are defined inside the Microsoft.Phone.Controls
namespace, which is included in the Microsoft.Phone.dll
library.
The PhoneApplicationPage
class gives you an example of how to use a namespace. Since the PhoneApplicationPage
class is part of the Microsoft.Phone.Controls
namespace, we have to add the prefix phone
to the tag to use it:
<phone:PhoneApplicationPage />
It’s very important to understand how namespaces in XAML work, because we’ll need to declare them every time we use third-party controls (that we created on our own or are part of an external library) or resources, like converters.
Properties and Events
Every control can be customized in two ways: by setting properties and actions. Both are identified by attributes of the XAML tag, but they have two different purposes.
Properties are used to change the look or the behavior of the control. Usually, a property is simply set by assigning a value to the specific attribute. For example, if we want to assign a value to the Text
property of a TextBlock
control, we can do it in the following way:
<TextBlock Text="This is a text block" />
There’s also an extended syntax that can be used in the case of a complex property that can’t be defined with a plain string. For example, if we need to set an image as a control’s background, we need to use the following code:
<Grid> <Grid.Background> <ImageBrush ImageSource="/Assets/Background.png" /> </Grid.Background> </Grid>
Complex properties are set by using a nested tag with the name of the control plus the name of the property, separated by a dot (to set the Background
property of the Grid
control, we use the Grid.Background
syntax).
One important property that is shared by every control is x:Name
, which is a string that univocally identifies the control in the page. You can’t have two controls with the same name in a single page. Setting this property is very important if you need to interact with the control in the code behind—you’ll be able to refer to it by using its name.
Events are a way to manage user interactions with the control. One of the most used is Tap
, which is triggered when users tap the control.
<Button Tap="OnButtonClicked" />
When you define an action, Visual Studio will automatically prompt you to create an event handler, which is the method (declared in the code behind) that is executed when the event is triggered.
private void OnButtonClicked(object sender, GestureEventArgs e) { MessageBox.Show("Hello world"); }
In the previous example, we display the classic “Hello world” message to users when the button is pressed.
Resources
As in HTML, we are able to define CSS styles that can be reused in different parts of the website or the page. XAML has introduced the concept of resources that can be applied to different controls in an application.
Basically every XAML control supports the Resources
tag: thanks to the hierarchy structure, every other nested control will be able to use it. In the real world, there are two common places to define a resource: at page and application level.
Page resources are defined in a single page and are available to all the controls that are part of that page. They are placed in a specific property called Resources
of the PhoneApplicationPage
class.
<phone:PhoneApplicationPage.Resources> <!-- you can place resources here --> </phone:PhoneApplicationPage.Resources>
Application resources, instead, are globally available and they can be used inside any page of the application. They are defined in the App.xaml
file, and the standard template already includes the needed definition.
<Application.Resources> <!-- here you can place global resources --> </Application.Resources>
Every resource is univocally identified by a name that is assigned using the x:Key
property. To apply a resource to a control, we need to introduce the concept of markup extensions. These are special extensions that allow us to apply different behaviors that would otherwise need some code to properly work. There are many markup extensions in the XAML world, and the one needed to apply a resource is called StaticResource
. Here is an example of how to use it:
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" />
In this sample, the resource is applied to the Style
property by including the StaticResource
keyword inside braces, followed by the resource name which is the value of the x:Key
property.
Resources can be also defined in an external file called ResourceDictionary
if you want to better organize your project. To do this, right-click on your project in Visual Studio, click Add > New Item, and choose XML file. Give the file a name that ends with the .xaml extension and include the following definition:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- put resources here --> </ResourceDictionary>
Now you can add it to your project by declaring it in the App.xaml
:
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Assets/Resources/Styles.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>
Notice the MergedDictionaries
property: all the external resource files should be declared here. This way, they will be automatically merged and every resource declared in each external file will be available to every page, as if they were declared inline.
Let’s see now, in detail, which are the most important kind of resources available.
Styles
XAML styles work the same way as CSS styles: you can define the values of different properties together in a single style that can be applied to multiple controls so that they all use the same layout. Here is how a style definition looks:
<Style x:Key="CustomText" TargetType="TextBlock"> <Setter Property="FontSize" Value="24" /> <Setter Property="FontWeight" Value="Bold" /> </Style>
A style is defined by a Style
tag, which has two important attributes: x:Key
, the name of the style, and TargetType
, the type of controls that will be suitable for this style.
Inside the Style
tag you can place as many Setter
tags as you want. Each one identifies a control’s property you want to change. Every Setter
tag needs two attributes: Property
is the control’s property you want to change, and Value
is the value you want to assign to the property.
The style defined in the previous example can be applied to any TextBlock
control. Its purpose is to change the size of the font to 24 and to apply a bold style to the text.
There are also special types of styles called implicit styles. They are defined in the same way as the styles in the earlier example, except that the x:Key
attribute is missing. In this case, the style is automatically applied to every control that is compliant with the value of the TargetType
property, according to the scope where the style has been defined. If the style is set as a page resource, it will be applied only to the controls of the page; if the style is set as an application resource, it will be applied to every control in the application.
Data Templates
Data templates are a special type of resource that can be applied to a control to define its appearance. Data templates are often used with controls that are able to display collections of elements, like ListBox
or LongListSelector
.
The following sample shows a data template:
<DataTemplate x:Key="PeopleTemplate"> <StackPanel> <TextBlock Text="Name" /> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="Surname" /> <TextBlock Text="{Binding Path=Surname}" /> </StackPanel> </DataTemplate>
A data template simply contains the XAML that will be used to render the specific item. If, for example, we apply this data template to the ItemTemplate
property of a ListBox
control, the result will be that the defined XAML will be repeated for every item in the collection (for the moment, just ignore the Binding
markup extension; we’ll deal with it later when we talk about data binding).
As for every other resource, data templates can be assigned to a property using the StaticResource
markup extension.
<ListBox ItemTemplate="{StaticResource PeopleTemplate}" />
Animations
XAML is a powerful language because it allows us to do more than just create the layout of applications. One of the most interesting features is the animation feature, which can be created using the Storyboard
control.
The Storyboard
control can be used to define different types of animations:
-
DoubleAnimation
, which can be used to change the numeric value of a property (for example,Width
orFontSize
). -
ColorAnimation
, which can be used to interact with properties that define a color (like inside aSolidColorBrush
). -
PointAnimation
, which can be applied to properties that define a point coordinate.
The following sample code defines an animation:
<Storyboard x:Name="Animation"> <DoubleAnimation Storyboard.TargetName="RectangleElement" Storyboard.TargetProperty="Width" From="200" To="400" Duration="00:00:04" /> </Storyboard>
The first two properties are inherited from the Storyboard
control: Storyboard.TargetName
is used to set the name of the control we’re going to animate, while Storyboard.TargetProperty
is the property whose value we’re going to change during the animation.
Next, we define the animation’s behavior: the initial value (the From
property), the ending value (the To
property) and the duration (the Duration
property). The behavior defined in the previous sample animates a Rectangle
control by increasing its width from 200 to 400 over 4 seconds.
We can also control the animation more deeply by using the UsingKeyFrames
variant available for every animation type:
<Storyboard x:Name="Animation"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="RectangleElement" Storyboard.TargetProperty="Width"> <LinearDoubleKeyFrame KeyTime="00:00:00" Value="200" /> <LinearDoubleKeyFrame KeyTime="00:00:02" Value="250" /> <LinearDoubleKeyFrame KeyTime="00:00:04" Value="500" /> </DoubleAnimationUsingKeyFrames> </Storyboard>
This way you’re able to control the animation’s timing. In the previous sample, the animation’s type is the same (it’s a DoubleAnimation
), but we’re able to set, for a specific time, which is the value to apply using the LinearDoubleKeyFrame
tag.
In the previous sample, the Width
of the Rectangle
control is set to 200 at the beginning. Then, after two seconds, it increases to 250 and after four seconds, it is set to 500.
Easing Animations
Another way to create animations is to use mathematical formulas that are able to apply a realistic behavior to an object, like bouncing, or acceleration and deceleration. You could achieve the same result by using key frames, but it would require a lot of work. For this reason, the animation framework offers a set of predefined easing functions that can be easily applied to an animation.
To add an easing function, you just need to set the EasingFunction
property of an animation, as shown in the following sample:
<Storyboard x:Name="EasingAnimation"> <PointAnimation From="0,0" To="0, 200" Duration="00:00:3" Storyboard.TargetName="Circle" Storyboard.TargetProperty="Center"> <PointAnimation.EasingFunction> <BounceEase Bounces="2" EasingMode="EaseOut" /> </PointAnimation.EasingFunction> </PointAnimation> </Storyboard>
After you’ve defined a regular animation (in the example, it’s a PointAnimation
that moves an Ellipse
object from the coordinates (0, 0) to (0, 200)), you can set the EasingFunction
property with one of the available easing functions. This example shows how to use the BounceEase
function, which can be used to apply a bouncing effect to the object (the number of bounces performed is specified with the Bounces
property).
Other available easing functions are:
-
BackEase
, which retracts the motion of the animation slightly before it starts. -
CircleEase
, which applies a circular function to the acceleration animation. -
ElasticEase
, which creates an animation that resembles an oscillating spring.
The official MSDN documentation features a complete list of the available easing functions.
How To Control Animations
Animations are treated like resources. They can be defined as local resources, page resources, or application resources. Unlike traditional resources, Storyboard
controls are identified by the x:Name
property, like a regular control.
The following sample shows an animation that is set as a page resource:
<phone:PhoneApplicationPage.Resources> <Storyboard x:Name="Animation"> <DoubleAnimation Storyboard.TargetName="RectangleElement" Storyboard.TargetProperty="Width" From="200" To="400" Duration="00:00:04" /> </Storyboard> </phone:PhoneApplicationPage.Resources>
Thanks to the unique identifier, you’ll be able to control the animation in the code behind. Every Storyboard
object offers many methods to control it, like Begin()
, Stop()
, or Resume()
. In the following code you can see the event handlers assigned to two buttons that are used to start and stop the animation:
private void OnStartClicked(object sender, GestureEventArgs e) { Animation.Begin(); } private void OnStopClicked(object sender, GestureEventArgs e) { Animation.Stop(); }
Data Binding
Data binding is one of the most powerful features provided by XAML. With data binding, you’ll be able to create a communication channel between a UI element and various data sources, which can be another control or a property in one of your classes. Moreover, data binding is heavily connected to the XAML notification system (which we’ll detail later) so that every time you change something in your object, the control displaying it will be automatically updated to reflect the changes and display the new value.
When you create a communication channel using data binding, you define a source
(which contains the data to display) and a target
(which takes care of displaying the value). By default, the binding channel is set to OneWay
mode. This means that when the source
changes, the target
is updated to display the new value, but not vice versa. If we need to create a two-way communication channel (for example, because the target is a TextBox
control and we need to intercept a new value inserted by the user), we can set the Mode
property of the binding to TwoWay
.
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
Almost every control in the XAML can participate in data binding. Most of the properties available for a control, in fact, are dependency properties
. Beyond offering basic read and write capabilities, these are special properties that support notifications, so that they can notify the other side of the channel that something has changed.
The following example shows how data binding can be used to create a channel between two XAML controls:
<StackPanel> <Slider x:Name="Volume" /> <TextBlock x:Name="SliderValue" Text="{Binding ElementName=Volume, Path=Value}" /> </StackPanel>
The first thing to notice is that to apply binding, we need to use another markup extension, called Binding
. With this expression, we connect the Text
property of a TextBlock
control (the target) to the Value
property of a Slider
control named Volume
(the source).
Since both Text
and Value
are dependent properties, every time you move the slider, the selected value will be automatically displayed on the screen in the TextBlock
control.
Data Binding With Objects
One of the most powerful data binding features is the ability to connect controls with objects that are part of your code. However, first, we need to introduce the DataContext
concept. DataContext
is a property that is available for almost every control and can be used to define its binding context, which is also automatically inherited by every nested control. When you define an object as DataContext
, the control and all its children will have access to all its properties.
Let’s see an example that will help you better understand how it works. Let’s say that you have a class that represents a person:
public class Person { public string Name { get; set; } public string Surname { get; set; } }
Our goal is to display information about a person using this class. Here is how we can do it using data binding. First, let’s take a look at the code behind:
public MainPage() { InitializeComponent(); Person person = new Person(); person.Name = "Matteo"; person.Surname = "Pagani"; Author.DataContext = person; }
When the page is initialized, we create a new Person
object and set a value for the Name
and Surname
properties. Then, we set this new object as DataContext
of the Author
control. Let’s see in the XAML page the Author
control and how the Name
and Surname
properties are displayed:
<StackPanel x:Name="Author"> <TextBlock Text="Name" /> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="Surname" /> <TextBlock Text="{Binding Path=Surname}" /> </StackPanel>
Author
is the name assigned to a StackPanel
control, which is the container we’ve placed inside different TextBlock
controls. In the previous sample we can see the Binding
markup extension in action again, this time with a different attribute: Path
. We’re using it to tell the XAML which property of the current DataContext
to display. Since the DataContext
is inherited from the StackPanel
control, every TextBlock
has access to the properties of the Person
object we’ve created in the code behind. Notice that the Path
attribute is optional. The two following statements are exactly the same:
<TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="{Binding Name}" />
The INotifyPropertyChanged Interface
The previous code has a flaw. Everything works fine, but if you change the value of one of the Name
or Surname
properties during the execution, the user interface won’t be updated to display the new value. The reason is that Name
and Surname
are simple properties, so they aren’t able to notify the user interface that something has changed, unlike dependency properties. For this scenario, the XAML framework has introduced the INotifyPropertyChanged
interface that can be implemented by objects that need to satisfy this notification requirement. Here is how the Person
class can be changed to implement this interface:
public class Person: INotifyPropertyChanged { private string _name; private string _surname; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } } public string Surname { get { return _surname; } set { _surname = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
The class now implements the INotifyPropertyChanged
interface, allowing us to support an event handler (called PropertyChangedEventHandler
) that is triggered every time a property’s value changes. The class also implements a method called OnPropertyChanged()
that acts as a wrapper of the event handler and needs to be invoked when a property changes.
We also need a change in our properties. Every time the set of the property is called (meaning that a new value has been assigned) we raise the OnPropertyChanged()
method. The result will be that every control bound with the property will be notified of the change and will update its visual status accordingly.
Data Binding and Collections
Data binding is especially helpful when you have to deal with collections of objects like arrays or lists (basically, every framework’s collection types that implement the IEnumerable
interface). Almost every control that supports collections, which inherit from the ItemsControl
class (like ListBox
or LongListSelector
), has a property called ItemsSource
, which can be directly assigned to a list.
You can control how every object of the collection will be rendered by using the ItemTemplate
property. As we’ve seen when we talked about data templates, this property allows us to set which XAML to use to display the object.
Now that we’ve talked about data binding, there’s another important piece to add. In the sample code we used to show data templates, we included some binding expressions to display the name and surname of a person.
<DataTemplate x:Key="PeopleTemplate"> <StackPanel> <TextBlock Text="Name" /> <TextBlock Text="{Binding Path=Name}" /> <TextBlock Text="Surname" /> <TextBlock Text="{Binding Path=Surname}" /> </StackPanel> </DataTemplate>
When you set a collection as ItemSource
, every object that is part of it becomes the DataContext
of the ItemTemplate
. If, for example, the ItemsSource
property of a ListBox
is connected to a collection whose type is List<Person>
, the controls included in the ItemTemplate
will be able to access all the properties of the Person
class.
This is the real meaning of the previous sample code: for every Person
object that is part of the collection, we’re going to display the values of the Name
and Surname
properties.
Another important piece of the puzzle when you deal with collections is the ObservableCollection<T>
class. It acts like a regular collection, so you can easily add, remove, and move objects. Under the hood, it implements the INotifyPropertyChanged
interface so that every time the collection is changed, the UI receives a notification. This way, every time we manipulate the collection (for example, we add a new item), the control that is connected to it will automatically be updated to reflect the changes.
Converters
Converters play an important role in data binding. Sometimes, in fact, you need to modify the source data before it is sent to the target. A common example is when you have to deal with DateTime
properties. The DateTime
class contains a full representation of a date, including hours, minutes, seconds, and milliseconds. Most of the time, however, you don’t need to display the full representation—often the date is just enough.
This is where converters come in handy. You are able to change the data (or, as shown in the following example, apply different formatting) before sending it to the control that is going to display it using data binding.
To create a converter, you need to add a new class to your project (right-click in Visual Studio, choose Add > Class), and it has to inherit from the IValueConverter
interface. The following is a converter sample:
public class DateTimeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null) { DateTime date = (DateTime)value; return date.ToShortDateString(); } return string.Empty; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null) { DateTime date = DateTime.Parse(value.ToString()); return date; } return DateTime.Now; } }
When you support the IValueConverter
interface, you are forced to implement two methods:
-
Convert()
is the method invoked when data from the source is sent to the target. -
ConvertBack()
does the opposite—it is invoked when data from the target is sent back to the source.
Most of the time it’s enough to implement the Convert()
method that is supported by every binding. The ConvertBack()
method, instead, is supported only when you have TwoWay
binding.
Both methods receive some important information as input parameters:
- The value returned from the binding source (which is the one you need to manipulate).
- The property that the binding has been applied to.
- An optional parameter that can be set in XAML using the
ConverterParameter
property. This parameter can be used to apply a different behavior in the converter logic. - The current culture.
The previous code sample shows the DateTime
example mentioned before. In the Convert()
method we get the original value and, after we’ve converted it into a DateTime
object, we return a string with the short formatting.
In the ConvertBack()
method, we get the string returned from the control and convert it into a DateTime
object before sending it back to the code.
Converters are treated like resources—you need to declare them and include them in your binding expression using the StaticResource
keyword.
<phone:PhoneApplicationPage.Resources> <converters:DateTimeConverter x:Key="DateConverter" /> </phone:PhoneApplicationPage.Resources> <TextBlock Text="{Binding Path=BirthDate, Converter={StaticResource DateConverter}}" />
It’s important to highlight that converters can have a negative impact on performance if you use them too heavily, since the binding operation needs to be reapplied every time the data changes. In this case, it’s better to find a way to directly modify the source data or to add a new property in your class with the modified value.
Controls
The Windows Phone 8 SDK includes many built-in controls that can be used to define the user interface of the application. There are so many controls that it’s almost impossible to analyze all of them in this series, so we’ll take a closer look at just the most important ones.
Layout Controls
Some controls simply act as containers for other controls and define the layout of the page. Let’s discuss the most important ones.
StackPanel
The StackPanel
control can be used to simply align the nested controls one below the other. It is able to automatically adapt to the size of the child controls.
<StackPanel> <TextBlock Text="First text" /> <TextBlock Text="Second text" /> </StackPanel>
You can also use the StackPanel
control to align controls horizontally, one next to the other, by setting the Orientation
property to Horizontal
.
<StackPanel Orientation="Horizontal"> <TextBlock Text="First text" /> <TextBlock Text="Second text" /> </StackPanel>
Grid
The Grid
control can be used to create table layouts, which fill the entire parent container’s size. It supports rows and columns in which you can place the different controls. The following code sample demonstrates its use:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition MaxHeight="100" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Text="1° row - 2° column" Grid.Row="0" Grid.Column="1" /> </Grid>
You define the grid layout by using the RowDefinitions
and ColumnDefinitions
properties. You can add a RowDefinition
tag for every row that the table will have, while the ColumnDefinition
tag can be used to set the number of columns. For every row and column you can set the width and height, or you can simply omit them so that they automatically adapt to the nested controls.
To define a control’s position inside the grid, we’re going to use two attached properties, which are special dependency properties that are inherited from the Grid
control and can be used with every control. With the Grid.Row
property, we can set the row’s number, and with Grid.Column
, we can set the column’s number.
The previous sample code is used to display a TextBlock
in the cell that is placed in the first row, second column of the grid, as you can see in the following figure:
ScrollViewer
The ScrollViewer
control is a container, but it doesn’t define a layout. If you want to arrange nested controls, you still have to use another container like a StackPanel
or a Grid
. The purpose of this control is to create a layout that is bigger than the size of the screen. This way, users will be able to scroll down to see the rest of the user interface.
This control is useful, for example, when you have to display text that doesn’t fit the size of the page.
<ScrollViewer> <StackPanel> <TextBlock TextWrapping="Wrap" Text="This can be long text" /> </StackPanel> </ScrollViewer>
Border
The Border
control’s purpose is to display a border. This is useful because it’s able to contain a child control that will be wrapped into the border.
The following sample shows how to wrap an image inside a red border:
<Border BorderThickness="5" BorderBrush="Red"> <Image Source="/Assets/windows-phone-8-logo.png"/> </Border>
There are some key properties of the Border
control. The first one is BorderThickness
, which specifies the border’s thickness. You can specify a single value, as we did in the previous sample. In this case, the same thickness is applied to every side. You can also specify multiple values to give every border a different size, as in the following sample:
<Border BorderThickness="5, 10, 15, 20" BorderBrush="Red"> <Image Source="/Assets/windows-phone-8-logo.png"/> </Border>
The second important property is BorderBrush
, which is used to set the brush that is applied to the border. It can use any of the available XAML brushes. By default, it accepts a SolidColorBrush
, so you can simply specify the color you want to apply.
Another useful property is Padding
, which can be used to specify the distance between the border and the child control, as shown in the following sample:
<Border BorderThickness="5" BorderBrush="Red" Padding="10"> <Image Source="/Assets/windows-phone-8-logo.png"/> </Border>
Output Controls
The purpose of these controls is to display something to users, such as text, an image, etc.
TextBlock
TextBlock
is one of the basic XAML controls and is used to display text on the screen. Its most important property is Text
, which, of course, contains the text to display. You have many properties to choose from to modify the text’s appearance, such as FontSize
, FontWeight
, and FontStyle
, and you can automatically wrap the text in multiple lines in case the text is too long by setting the TextWrapping
property to true
.
<TextBlock Text="This is long and bold text" TextWrapping="Wrap" FontWeight="Bold" />
You can also apply different formatting to the text without using multiple TextBlock
controls by using the Run
tag, which can be used to split the text as shown in the following sample:
<TextBlock> <Run Text="Standard text" /> <LineBreak /> <Run Text="Bold test" FontWeight="Bold" /> </TextBlock>
RichTextBlock
The RichTextBlock
control is similar to TextBlock
, but it offers more control over the formatting styles that can be applied to the text. Like the one offered by HTML, you can define paragraphs, apply different text styles, and more.
<RichTextBox> <Paragraph> <Bold>This is a paragraph in bold</Bold> </Paragraph> <Paragraph> <Italic>This is a paragraph in italics</Italic> <LineBreak /> </Paragraph> </RichTextBox>
Image
The Image
control can be used to display images. You can set the Source
property with a remote path (an image’s URL published on the Internet) or a local path (a file that is part of your Visual Studio project). You can’t assign a path that refers to an image stored in the local storage of the application. We’ll see later in this series how to manage this limitation.
<Image Source="http://www.syncfusion.com/Content/en-US/Home/Images/syncfusion-logo.png" />
You can also control how the image will be adapted to fill the control’s size by using the Stretch
property, which can have the following values:
-
Uniform
: The default value. The image is resized to fit the container while keeping the original aspect ratio so that the image won’t look distorted. If the container’s aspect ratio is different from the image’s, the image will look smaller than the available space. -
Fill
: The image is resized to fit the container, ignoring the aspect ratio. It will fill all the available space, but if the control’s size doesn’t have the same aspect ratio as the image, it will look distorted. -
UniformToFill
is a mix of the previous values. If the image has a different aspect ratio than the container, the image is clipped so that it can keep the correct aspect ratio and, at the same time, fill all the available space. -
None
: The image is displayed in its original size.
Input Controls
These controls are used to get input from users.
TextBox
TextBox
is another basic XAML control, and it’s simply a way to collect text from users. The entered text will be stored in the Text
property of the control. When users tap a TextBox
control, the virtual keyboard is automatically opened. As a developer, you can control the keyboard’s type that is displayed according to the data types you’re collecting.
For example, you can display a numeric keyboard if users only need to input a number; or you can use an email keyboard (which provides easy access to symbols like @) if you’re collecting an email address.
You can control the keyboard’s type with the InputScope
property. The list of supported values is very long and can be found in the MSDN documentation. Some of the most used are:
-
Text
for generic text input with dictionary support. -
Number
for generic number input. -
TelephoneNumber
for specific phone number input (it’s the same keyboard that is displayed when you compose a number in the native Phone application). -
EmailNameOrAddress
which adds quick access to symbols like @. -
Url
which adds quick access to common domains like .com or .it (depending on the keyboard’s language). -
Search
which provides automatic suggestions.
<TextBox InputScope="TelephoneNumber" />
Tip: If the TextBox control is used to collect generic text, always remember to set the InputScope property to Text. This way, users will get support from the autocomplete and autocorrection tools.
PasswordBox
PasswordBox
works exactly like the TextBox
control, except that the inserted characters are automatically converted into dots so that people near the user won’t be able to read the text. As the name of the control implies, it’s typically used to collect passwords.
Theme Resources
One of a developer’s goals should be to keep the user interface of her or his application as consistent as possible with the guidelines provided by the operating system. To help achieve this goal, the SDK offers many out-of-the-box resources that can be applied to controls to get the same look and feel of the native applications. These styles are typically used with controls like TextBox
, TextBlock
, or RadioButton
, and they provide a standard set of visual features (like font size, color, opacity, etc.) that are consistent with the other applications.
Another good reason to use theme resources is that they are aware of the theme set by users. For example, you can use the PhoneAccentBrush
style if you want to give a control the same color as the phone’s accent color.
The many theme resources are split into different categories like brush resources, color resources, font names and styles, and text resources. You can find a complete list of the available styles in the MSDN documentation. They are simply applied using the Style
property offered by every control, as shown in the following sample:
<TextBlock Text="App name" Style="{StaticResource PhoneTextNormalStyle}" />
Interacting With Users
In this category, we can collect all the controls that we can use to interact with users, like Button
, CheckBox
, or RadioButton
.
The text to display is set with the Content
property, like in the following sample:
<Button Content="Tap me" Tap="OnClickMeClicked" />
The Content
property can also be complex so that you can add other XAML controls. In the following sample we can see how to insert an image inside a button:
<Button Tap="OnClickMeClicked"> <Button.Content> <StackPanel> <Image Source="/Assets/logo.png" Height="200" /> </StackPanel> </Button.Content> </Button>
These controls offer many ways to interact with users. The most common events are Click
, Tap
, and DoubleTap
.
Note: Click and Tap are the same event—they are both triggered when users press the control. Tap has been introduced in Windows Phone 7.5 to be more consistent with the touch interface, but to avoid breaking old apps, the Click event is still supported.
The Windows Phone Signature Controls
Most of the controls we’ve seen so far are part of the XAML framework and are available on every other XAML-based technology, like Silverlight, WPF, and Windows Store apps.
However, there are some controls that are available only on the Windows Phone platform, since they are specific for the mobile experience. Let’s take a look at them.
Panorama
The Panorama
control is often used in Windows Phone applications since it’s usually treated as a starting point. The control gets its name because an oversized image is used as the background of the page. Users are able to swipe to the left or to the right to see the other available pages. Since the image is bigger than the size of the page, the phone applies a parallax effect that is visually pleasing for users.
The other main feature of the Panorama
control is that users can get a sneak peek of the next page. The current page doesn’t occupy all of the available space because a glimpse of the next page is displayed on the right edge.
A Panorama
control is typically used to provide an overview of the content that is available in an application. It’s a starting point, not a data container. For example, it’s not appropriate to use a panorama page to display all the news published on a blog. It’s better, instead, to display only the latest news items and provide a button to redirect users to another page, where they will be able to see all of them.
From a developer’s point of view, a Panorama
control is composed of different pages: each one is a PanoramaItem
control that contains the layout of the page.
The Panorama
can have a generic title, like the application’s title (which is assigned to the Title
property), while every page can have its own specific title (which is assigned to the Header
property).
<phone:Panorama Title="Panorama"> <phone:PanoramaItem Header="First page"> <StackPanel> <TextBlock Text="Page 1" /> </StackPanel> </phone:PanoramaItem> <phone:PanoramaItem Header="Second page"> <StackPanel> <TextBlock Text="Page 2" /> </StackPanel> </phone:PanoramaItem> </phone:Panorama>
Note: The Panorama control (like the Pivot control) is not available by default in a page. You’ll have to declare the following namespace:
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
Pivot
The Pivot
control, from a technical and user interaction perspective, works in a similar way to the Panorama
control—users can swipe the screen left or right to view the other pages. The difference, from the users’ point of view, is that the view will fit the screen’s size. Users can see which page is next because its header is displayed next to the current page’s header in gray.
However, the Pivot
control is used for purposes not shared by the Panorama
control:
- To display one kind of information applied to different contexts. The previous figure is a good example. The information on each page is the same (the weather forecast), but referred to in different contexts (the cities).
- To display different kinds of information that refer to the same context. A contact’s details page in the People Hub on a Windows Phone is a good example of this—you have much information (the contact’s details, social network updates, conversations, etc.), but it all belongs to the same context (the contact).
As previously anticipated, the XAML for the Pivot
control works like the XAML for the Panorama
control. The main control is called Pivot
, while the nested controls that represent the pages are called PivotItem
.
<phone:Pivot Title="Pivot"> <phone:PivotItem Header="First page"> <StackPanel> <TextBlock Text="Page 1" /> </StackPanel> </phone:PivotItem> <phone:PivotItem Header="Second page"> <StackPanel> <TextBlock Text="Page 2"/> </StackPanel> </phone:PivotItem> </phone:Pivot>
The ApplicationBar
The ApplicationBar
is a control placed at the bottom of the page and is used as a quick access for functions that are connected to the current view.
You can add two element types to an application bar:
-
Icons are always displayed (unless the
ApplicationBar
is minimized), and up to four can be displayed at once. - Menu items are simply text items that are displayed only when the application bar is opened. There’s no limit to the number of items that can be included.
The ApplicationBar
does not behave like the other XAML controls. It is not part of the page—in fact, it’s declared outside the main Grid
, the one called LayoutRoot
—and it doesn’t inherit from the FrameworkElement
class, like every other control. The biggest downside of this is that the control doesn’t support binding; you’ll have to rely on third-party libraries, like the implementation available in the Cimbalino Toolkit for Windows Phone.
Here is an ApplicationBar
sample:
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Assets/Add.png" Text="Add" Click="ApplicationBarIconButton_Click" /> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="update" Click="ApplicationBarMenuItem_Click"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
ApplicationBar
is a property of the PhoneApplicationPage
class, which contains the real ApplicationBar
definition. It’s not part of the standard XAML namespaces, but it’s part of the following namespace, which should already be declared in every standard Windows Phone page:
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
Icon buttons are declared directly inside the ApplicationBar
tag. The base class is ApplicationBarIconButton
, and the most important properties are Text
(the icon description) and IconUri
(the path of the image used as the icon), while Click
is the event handler that is invoked when a user taps the button.
Menu items, instead, are grouped inside a property of the ApplicationBar
control called MenuItems
. You can add as many ApplicationBarMenuItem
controls as you want. They behave like the button icons except that, since they’re just text, the IconUri
property is missing.
Another important limitation of the ApplicationBar
control is that we’re not able to assign the x:Name
property to an ApplicationBarIconButton
or ApplicationBarMenuItem
control. This means that if we need to change a property’s value in the code behind, we can’t simply use the notation ControlName.PropertyName
.
The work-around is to use the code behind to directly access the collections that contain the buttons and menu items that are available because of the ApplicationBar
object. They are called Buttons
and MenuItems
, as you can see in the following code sample:
ApplicationBarIconButton iconButton = this.ApplicationBar.Buttons[0] as ApplicationBarIconButton; iconButton.Text = "New text"; ApplicationBarMenuItem menuItem = this.ApplicationBar.MenuItems[0] as ApplicationBarMenuItem; menuItem.Text = "New text";
The purpose of this sample is to get access to the first icon button and first menu item inside the ApplicationBar
, and to change the value of the Text
property.
In the end, there are two other ways to customize the ApplicationBar
. The first is to minimize it. This way, only the three dots at the right margin will be displayed; icons won’t be visible. To achieve this, you have to set the Mode
property to Minimized.
The other way is to change the opacity. You can set the Opacity
property with a value between 0 (transparent) and 1 (opaque). The biggest difference is that when ApplicationBar
is translucent, the page content will go below the bar and fit the entire size of the screen; when the bar is opaque, the content won’t fit all of the available screen space since the bottom of the page will be reserved for the ApplicationBar
.
Displaying Collections of Data With the LongListSelector
One of the most common requirements in an application is to display a collection of items which can be retrieved from a remote service or a local database. The Windows Phone SDK has included, since the beginning, some controls for this purpose, like ItemsControl
and ListBox
. In Windows Phone 8, Microsoft has introduced a new and more powerful control, which previously was available as part of the Windows Phone Toolkit (you’ll find more details about this toolkit later in this article). This control is called LongListSelector
and has many advantages over other similar controls:
- better performance
- virtualization support to avoid loading all the data at the same time, instead loading data only when it is needed
- group support to turn the list into a
jump list
, so that data is grouped in categories and users can easily jump from one to another (for example, in the People Hub, contacts are grouped by the first letter)
Creating a Flat List
The LongListSelector
control can be used like a regular ListBox
to display a flat list of items, without grouping. In this case, you just need to set the IsGroupingEnabled
property to false
. Except for this modification, you’ll be able to use a LongListSelector
like a standard ItemsControl
. You’ll define the ItemTemplate
property with a DataTemplate
to define the item layout, and you’ll assign the collection you want to display to the ItemsSource
property.
<phone:LongListSelector x:Name="List" IsGroupingEnabled="False" ItemTemplate="{StaticResource PeopleItemTemplate}" />
Creating a List Grouped by Letter
Creating a list grouped by the first letter of the items is a bit more complicated since we have to change our data source. It won’t be a flat collection of data anymore. In addition, if we want to keep the user experience consistent with other applications, we’ll need to create a jump list with all the letters of the alphabet. The groups that don’t have any members will be disabled so that users can’t tap them.
To achieve this result, Microsoft offers a class called AlphaKeyGroup<T>
, which represents a letter of the alphabet and all the items that start with it. However, this class is not part of the Windows Phone SDK, and should be manually added to your project by right-clicking on your project in the Solution Explorer in Visual Studio, and choosing Add new class. The following code example is the full implementation.
public class AlphaKeyGroup<T> : List<T> { /// <summary> /// The delegate that is used to get the key information. /// </summary> /// <param name="item">An object of type T.</param> /// <returns>The key value to use for this object.</returns> public delegate string GetKeyDelegate(T item); /// <summary> /// The key of this group. /// </summary> public string Key { get; private set; } /// <summary> /// Public constructor. /// </summary> /// <param name="key">The key for this group.</param> public AlphaKeyGroup(string key) { Key = key; } /// <summary> /// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping. /// </summary> /// <param name="slg">The </param> /// <returns>The items source for a LongListSelector.</returns> private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg) { List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>(); foreach (string key in slg.GroupDisplayNames) { list.Add(new AlphaKeyGroup<T>(key)); } return list; } /// <summary> /// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping. /// </summary> /// <param name="items">The items to place in the groups.</param> /// <param name="ci">The CultureInfo to group and sort by.</param> /// <param name="getKey">A delegate to get the key from an item.</param> /// <param name="sort">Will sort the data if true.</param> /// <returns>An items source for a LongListSelector.</returns> public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort) { SortedLocaleGrouping slg = new SortedLocaleGrouping(ci); List<AlphaKeyGroup<T>> list = CreateGroups(slg); foreach (T item in items) { int index = 0; if (slg.SupportsPhonetics) { //Checks whether your database has the string yomi as an item. //If it does not, then generate Yomi or ask users for this item. //index = slg.GetGroupIndex(getKey(Yomiof(item))); } else { index = slg.GetGroupIndex(getKey(item)); } if (index >= 0 && index < list.Count) { list[index].Add(item); } } if (sort) { foreach (AlphaKeyGroup<T> group in list) { group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); }); } } return list; } }
The main features of this class are:
- It inherits from
List<T>
, so it represents a list of elements. - It has a property called
Key
, which is the key that identifies the group (the letter of the alphabet). - It uses a special collection type called
SortedLocaleGroup
, which is able to manage the cultural differences between one language and another. - It offers a method called
CreateGroups()
, which is the one we’re going to use to group our data.
To better explain how to use the AlphaKeyGroup<T>
class, let’s use a real example. Let’s define a collection of people that we want to group by the first letters of their names, in a similar way the People Hub does. The first step is to create a class that represents a single person:
public class Person { public string Name { get; set; } public string Surname { get; set; } public string City { get; set; } }
Then, when the application starts, we populate the list with a set of fake data, as shown in the following sample:
void LongListSelectorAlphabetic_Loaded(object sender, RoutedEventArgs e) { List<Person> people = new List<Person> { new Person { Name = "John", Surname = "Doe", City = "Como" }, new Person { Name = "Mark", Surname = "Whales", City = "Milan" }, new Person { Name = "Ricky", Surname = "Pierce", City = "New York" } }; }
Now it’s time to use the AlphaGroupKey<T>
class to convert this flat list into a grouped list by calling the CreateGroups()
method.
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people, Thread.CurrentThread.CurrentUICulture, p => p.Name, true);
The method requires four parameters:
- The collection that we want to group: In the sample, it’s the collection of
Person
objects we created. - The culture to use to generate the alphabet letters: The standard practice is to use the value of the
Thread.CurrentThread.CurrentUICulture
property, which is the primary language set by the user for the phone. - The object’s property that will be used for grouping: This is specified using a lambda expression. In the sample, the list will be grouped by the first letter of the name.
- The last parameter, a
Boolean
type, is used to determine whether to apply ordering: If set totrue
, the collection will be alphabetically ordered.
What we get in return is a collection of AlphaKeyGroup<T>
objects, one for each letter of the alphabet. This is the collection that we need to assign to the ItemsSource
property of the LongListSelectorControl
.
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people, Thread.CurrentThread.CurrentUICulture, p => p.Name, true); People.ItemsSource = list;
However, this code isn’t enough—we also need to supply additional templates in XAML that are used to define the jump list layout.
The first property to set is called GroupHeaderTemplate
, and it represents the header that is displayed in the list before every group. In the case of an alphabetic list, it’s the letter itself. The following XAML code shows a sample template, which recreates the same look and feel of native apps:
<DataTemplate x:Key="PeopleGroupHeaderTemplate"> <Border Background="Transparent" Padding="5"> <Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="62" Height="62" Margin="0,0,18,0" HorizontalAlignment="Left"> <TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/> </Border> </Border> </DataTemplate>
With this layout, the letter is placed inside a square with a background color the same as the accent color of the phone. Take note of two important things:
- Some properties of the
Border
control use thePhoneAccentBrush
resource. It’s one of the theme resources previously described, which identifies the accent color of the phone. - The
Text
property of theTextBlock
control is bound with theKey
property of theAlphaGroupKey<T>
class. This way, we are able to display the group’s letter inside the square.
The second property to define is called JumpListStyle
and, unlike the previous property, it’s not a template but a style. Its purpose is to define the look and feel of the jump list, which is the view that is displayed when users tap a letter. It displays all the letters of the alphabet so that users can tap one of them and quickly jump to that group.
Here is a sample definition which, again, recreates the look and feel of native applications—all the letters of the alphabet are displayed side by side in multiple rows.
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/> <phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/> <Style x:Key="PeopleJumpListStyle" TargetType="phone:LongListSelector"> <Setter Property="GridCellSize" Value="113,113"/> <Setter Property="LayoutMode" Value="Grid" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Width="113" Height="113" Margin="6" > <TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6" Foreground="{Binding Converter={StaticResource ForegroundConverter}}" VerticalAlignment="Center"/> </Border> </DataTemplate> </Setter.Value> </Setter> </Style>
This style uses two converters which are part of the Windows Phone Toolkit called JumpListItemBackgroundConverter
and JumpListItemForegroundConverter
. As you can see in the ItemTemplate
that is applied to the control, these converters are used to define the text color and the Border
background color. Their purpose is to manage empty groups. If the collection contains one or more items, it will be displayed in white with the phone’s accent color as a background. If instead, the letter is associated to a group with no items, both the text and the background will be gray. This way, users can immediately see that the group is disabled, and tapping it won’t produce any effect.
In the end, don’t forget that as for every other control based on the ItemsControl
class, you’ll need to set the ItemTemplate
property with a DataTemplate
to define how a single item will look. The following sample shows a basic implementation, where name and surname are displayed one below the other:
<DataTemplate x:Key="PeopleItemTemplate"> <StackPanel> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Surname}" /> </StackPanel> </DataTemplate>
Once you’ve gathered all the required styles and templates, you can apply them to the LongListSelector
control, as shown in the following example:
<phone:LongListSelector x:Name="People" GroupHeaderTemplate="{StaticResource PeopleGroupHeaderTemplate}" ItemTemplate="{StaticResource PeopleItemTemplate}" JumpListStyle="{StaticResource PeopleJumpListStyle}" IsGroupingEnabled="True" HideEmptyGroups="True" />
Notice the HideEmptyGroups
property, which can be used to hide all the groups in the list that don’t contain any items.
Creating a List Grouped by Category
In some cases, we might need to group items by a custom field instead of the first letter. Let’s look at another example. We want to group Person
objects by the City
field; we need, then, a class to replace the AlphaKeyGroup<T>
, since it’s not suitable for our scenario.
Let’s introduce the Group<T>
class, which is much simpler:
public class Group<T> : List<T> { public Group(string name, IEnumerable<T> items) : base(items) { this.Key = name; } public string Key { get; set; } }
Under the hood, this class behaves like AlphaKeyGroup<T>
. It inherits from List<T>
, so it represents a collection of items, and defines a group which is identified by a key (which will be the category’s name).
private List<Group<T>> GetItemGroups<T>(IEnumerable<T> itemList, Func<T, string> getKeyFunc) { IEnumerable<Group<T>> groupList = from item in itemList group item by getKeyFunc(item) into g orderby g.Key select new Group<T>(g.Key, g); return groupList.ToList(); }
The previous method takes as input parameters:
- the flat collection to group.
- a function (expressed with a lambda expression) to set the object’s property we want to use for grouping
The following example is the code we can use to group the collection of Person
objects by the City
property:
List<Group<Person>> groups = GetItemGroups(people, x => x.City); PeopleByCity.ItemsSource = groups;
The result returned by the GetItemGroups()
method can be directly assigned to the ItemsSource
property of the LongListSelector
control.
As we did in the alphabetical grouping scenario, we still need to define the layout of the group headers and the jump list. We can reuse the resources we’ve previously defined, but, if we want to achieve a better result, we can adapt them so that the background rectangle fills the size of the category’s name, as shown in the following code example:
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/> <phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/> <DataTemplate x:Key="PeopleCityGroupHeaderTemplate"> <Border Background="Transparent" Padding="5"> <Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Height="62" Margin="0,0,18,0" HorizontalAlignment="Left"> <TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/> </Border> </Border> </DataTemplate> <Style x:Key="PeopleCityJumpListStyle" TargetType="phone:LongListSelector"> <Setter Property="GridCellSize" Value="113,113"/> <Setter Property="LayoutMode" Value="List" /> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Height="113" Margin="6" > <TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6" Foreground="{Binding Converter={StaticResource ForegroundConverter}}" VerticalAlignment="Center"/> </Border> </DataTemplate> </Setter.Value> </Setter> </Style>
The following figure shows the result of the code sample.
Remember to assemble the LongListSelector
control by adding all the styles and templates you’ve just created, as shown in the following sample:
<phone:LongListSelector x:Name="PeopleByCity" ItemTemplate="{StaticResource PeopleItemTemplate}" GroupHeaderTemplate="{StaticResource PeopleCityGroupHeaderTemplate}" JumpListStyle="{StaticResource PeopleCityJumpListStyle}" IsGroupingEnabled="True" />
Interacting With the List
The two key concepts to keep in mind when you interact with a LongListSelector
control (or any other control that inherits from the ItemsControl
class) are:
- the
SelectionChanged
event, which is triggered every time the user taps one of the elements on the list - the
SelectedItem
property, which stores the items that have been selected by the user
With the combination of these two items, you’ll be able to detect when and which item has been selected by the user, and properly respond. For example, you can redirect the user to a page where he or she can see the details of the selected item.
<phone:LongListSelector x:Name="People" GroupHeaderTemplate="{StaticResource PeopleGroupHeaderTemplate}" ItemTemplate="{StaticResource PeopleItemTemplate}" SelectionChanged="LongListSelectorAlphabetic_Loaded" />
The previous sample shows a LongListSelector
control that has been subscribed to the SelectionChanged
event. The following sample, instead, shows the event handler code:
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { Person selectedPerson = People.SelectedItem as Person; string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id); NavigationService.Navigate(new Uri(uri, UriKind.Relative)); }
The previous sample shows a LongListSelector
control that has been subscribed to the SelectionChanged
event. The following sample, instead, shows the event handler code:
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { Person selectedPerson = People.SelectedItem as Person; string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id); NavigationService.Navigate(new Uri(uri, UriKind.Relative)); }
Thanks to the SelectedItem
property, we retrieve the selected Person
object and redirect the user to another page called DetailPage.xaml
to display the details of the selected person. We’ll cover navigation in-depth in the next article.
The Windows Phone Toolkit
In this article we’ve seen just the basic controls, but you’ll notice that the SDK is missing many controls that are used in other applications. Most of them are available in a library called Windows Phone Toolkit, which is available on CodePlex and NuGet. It’s maintained directly by Microsoft and is a way to keep a separate, faster development process than the one needed to release a new SDK version.
Here is a brief list of the most important available controls:
-
ToggleSwitch
: Especially helpful for settings pages, since it’s a switch that can be used to define a Boolean value (on/off). -
ContextMenu
: A menu that can be displayed when users tap and hold an item. -
DatePicker
andTimePicker
: Used to select a date or time respectively. -
WrapPanel
: A special container that can align nested controls next to each other and automatically wrap to a new line if no space remains. -
AutoCompleteBox
: A specialTextBox
that can prompt suggestions to the user based on the text the user is typing. -
ListPicker
: Used to display a list of items. It is useful especially when asking users to choose between different values. -
ExpanderView
: Used to create elements that can be expanded using a tree structure to display other elements. A good example of this control is the Mail app; it’s used to display conversations. -
MultiSelectList
: Similar to aListBox
, but automatically places check boxes next to each item, allowing users to select multiple items. -
PhoneTextBox
: A specialTextBox
control with many built-in features, like support for action icons, placeholders, a character counter, etc. -
HubTile
: Can be used to recreate the Start screen experience offered by Live Tiles inside an application. -
CustomMessageBox
: A specialMessageBox
that offers many more options than the standard one, like button customization, custom template support, etc. -
Rating
: Gives users the ability to rate something inside an application. The user experience is similar to the one offered by the Store, where users can vote on an application. -
SpeechTextBox
: Another specialTextBox
that supports vocal recognition so that users can dictate text instead of type it.
The Windows Phone Toolkit also includes a Windows Phone frame replacement (the class that manages views and navigation) with built-in support for animations so that you can easily add transition effects when users move from one page of the application to another. Let’s dig deeper into this topic.
Page Transitions
In this article, we’ve learned how to animate objects that are placed inside a page. Often, one of the easiest ways to improve the look and feel of our application is to add an animation during the transition from one page to another. The Windows Phone Toolkit plays an important role in achieving this result since the standard application frame provided by the SDK, which manages all our application’s pages, doesn’t support transitions. Instead, the toolkit offers a specific frame class, called TransitionFrame
, which can be used to replace the original frame class, which is called PhoneApplicationFrame
.
The first step is to replace the original frame. You can do this in the App.xaml.cs
file, which contains a hidden region titled Phone application initialization. If you expand it, you’ll find a method called InitializePhoneApplication()
which, among other things, initializes the application’s frame with the following code:
RootFrame = new PhoneApplicationFrame();
Replacing the original frame is easy. Once you’ve installed the Windows Phone Toolkit, you can change the initialization of the RootFrame
object by using the TransitionFrame
class, which is part of the Microsoft.Phone.Controls
namespace, as shown in the following sample:
RootFrame = new TransitionFrame();
Now you’re ready to set which animations you want to use in your pages according to the navigation type. Let’s start by looking at the following sample code, which should be added in every page you want to animate with a transition. The code has to be placed under the main PhoneApplicationPage
node before the page’s layout is defined:
<toolkit:TransitionService.NavigationInTransition> <toolkit:NavigationInTransition> <toolkit:NavigationInTransition.Backward> <toolkit:TurnstileTransition Mode="BackwardIn"/> </toolkit:NavigationInTransition.Backward> <toolkit:NavigationInTransition.Forward> <toolkit:TurnstileTransition Mode="ForwardIn"/> </toolkit:NavigationInTransition.Forward> </toolkit:NavigationInTransition> </toolkit:TransitionService.NavigationInTransition> <toolkit:TransitionService.NavigationOutTransition> <toolkit:NavigationOutTransition> <toolkit:NavigationOutTransition.Backward> <toolkit:TurnstileTransition Mode="BackwardOut"/> </toolkit:NavigationOutTransition.Backward> <toolkit:NavigationOutTransition.Forward> <toolkit:TurnstileTransition Mode="ForwardOut"/> </toolkit:NavigationOutTransition.Forward> </toolkit:NavigationOutTransition> </toolkit:TransitionService.NavigationOutTransition>
Transitions are added using the TransitionService
, which is part of the Windows Phone Toolkit (make sure that the xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
namespace is added to your page).
It supports two type of animations, specified using the NavigationInTransition
property:
-
In
animations, which are applied when users move to the current page -
Out
animations, which are applied when users move away from the current page
For every transition type, you also have the chance to specify two additional conditions:
- The
Backward
property is used to specify the transition to use when users reach the page after pressing the Back button. - The
Forward
property is used to specify the transition to use when users reach the page through the regular navigation flow.
Finally, you’re able to specify which transition you want to use for every scenario. The toolkit offers a list of predefined animations, like RotateTransition
to apply a rotation effect, TurnstileTransition
to simulate browsing a book, or SlideTransition
to apply a slide effect. Every transition offers a Mode
property, which can be used to customize the effect that is applied. The previous sample shows a TurnstileTransition
effect applied during every navigation, with a different effect according to the navigation’s type (backward or forward).
Customize the Turnstile Transition
The animation framework can be used not only to apply a transition to the entire page, but also to every control in the page. This scenario is supported by the TurnstileFeatherTransition
control, which applies a turnstile effect to the controls in the page. You can decide which order will be used to animate and enter the controls in the page with the FeatheringIndex
property.
The first step is to add a TransitionService
to your page and define a set of TurnstileFeatherTransition
animations, as in the following sample:
<toolkit:TransitionService.NavigationInTransition> <toolkit:NavigationInTransition> <toolkit:NavigationInTransition.Backward> <toolkit:TurnstileFeatherTransition Mode="BackwardIn"/> </toolkit:NavigationInTransition.Backward> <toolkit:NavigationInTransition.Forward> <toolkit:TurnstileFeatherTransition Mode="ForwardIn"/> </toolkit:NavigationInTransition.Forward> </toolkit:NavigationInTransition> </toolkit:TransitionService.NavigationInTransition> <toolkit:TransitionService.NavigationOutTransition> <toolkit:NavigationOutTransition> <toolkit:NavigationOutTransition.Backward> <toolkit:TurnstileFeatherTransition Mode="BackwardOut"/> </toolkit:NavigationOutTransition.Backward> <toolkit:NavigationOutTransition.Forward> <toolkit:TurnstileFeatherTransition Mode="ForwardOut"/> </toolkit:NavigationOutTransition.Forward> </toolkit:NavigationOutTransition> </toolkit:TransitionService.NavigationOutTransition>
Then, you’re able to apply the TurnstileFeatherTransition.FeatheringIndex
property to any control in the page and specify the order in which they will appear, starting with 0 to set the first control that will enter in the page.
<StackPanel> <TextBlock Text="First Element" toolkit:TurnstileFeatherEffect.FeatheringIndex="0"/> <TextBlock Text="Second Element" toolkit:TurnstileFeatherEffect.FeatheringIndex="1"/> <TextBlock Text="Third Element" toolkit:TurnstileFeatherEffect.FeatheringIndex="2"/> </StackPanel>
In the previous sample, the three TextBlock
controls will appear in the page starting with the first one (whose FeatheringIndex
is equal to 0) and ending with the last one (whose FeatheringIndex
is equal to 2).
Conclusion
It’s been a long journey so far and we’ve just scratched the surface; covering all the XAML features would require an entire book. In this article, we’ve considered some key concepts used in Windows Phone development:
- We introduced the basic XAML concepts like properties, events, namespaces, and resources.
- We learned how data binding works. It’s one of the most powerful XAML features and it’s very important to understand it to be productive.
- We’ve seen some of the basic controls that are included in the SDK and how we can use them to define the user interface of our application.
- We discussed some controls that are specific to the Windows Phone experience, like the
Panorama
,Pivot
, andApplicationBar
controls.
This tutorial represents a chapter from Windows Phone 8 Succinctly, a free eBook from the team at Syncfusion.
Comments