Build Your First Windows 8 App with Microsoft Visual C# and Visual Basic
- 2/15/2013
Adding UI elements
In this section, you will analyze the remaining project items that the template created and add some code to build a list of people and bind it to the user interface.
Let’s start by analyzing the code proposed by the Visual Studio 2012 template. You have explored the meaning and functionality of the application manifest and the image folder. Example 3-1 shows the XAML source code for the main page, which has been modified to contain a ListView standard user control that will display the FullName property of a list of bound elements.
Example 3-1. Modified MainPage.xaml page
<Page x:Class="MyFirstApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:MyFirstApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <ListView x:Name="list" DisplayMemberPath="FullName" /> </Grid> </Page>
The page includes the classic XAML definition for a page control represented by the MyFirstApp.MainPage class. The user control references four XML namespaces—just like a Silverlight project, a WPF app, or a Windows Phone 7.x application.
By default, the template uses a Grid for the layout, but you will change this in a later procedure, where you will add some styling to change the look and feel of this simple application.
You will also modify the code behind for the MainPage.xaml page, as shown in Example 3-2, so that it calls a fake “business layer” that returns a list of people represented by the Person class you will also implement shortly.
Example 3-2. Modified MainPage.xaml.cs code
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238 namespace MyFirstApp { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); // Fill the ListView var biz = new Biz(); list.ItemsSource = biz.GetPeople(); } /// <summary> /// Invoked when this page is about to be displayed in a Frame. /// </summary> /// <param name="e">Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page.</param> protected override void OnNavigatedTo(NavigationEventArgs e) { } } }
Modify and test the application
Modify the MainPage.xaml file so that its contents are identical to Example 3-1.
Open the code-behind file (MainPage.xaml.cs) and insert the bold lines in Example 3-2.
Add a new class file to the project to implement the Biz class by right-clicking the term Biz in the code behind. Then choose Generate | Class.
Generate a method stub for the GetPeople method by using the same technique: right-click the GetPeople method, choose Generate | Method Stub. Use the following code to replace the code of the Biz.cs file.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstApp { public class Biz { public List<Person> GetPeople() { return new List<Person>() { new Person() { FullName = "Roberto Brunetti" }, new Person() { FullName = "Paolo Pialorsi" }, new Person() { FullName = "Marco Russo" }, new Person() { FullName = "Luca Regnicoli" }, new Person() { FullName = "Vanni Boncinelli" }, new Person() { FullName = "Guido Zambarda" }, new Person() { FullName = "Jessica Faustinelli" }, new Person() { FullName = "Katia Egiziano" } }; } } public class Person { public string FullName { get; set; } } }
Run the application.
The code in the Biz class simply returns a list of people represented by the Person class. For the sake of simplicity, this class has just one property, FullName.
When you run the app, the result will look similar to Figure 3-4. You should be able to select a person from the list.
Figure 3-4 Main page of the application presenting the listbox of names.
It is time to forget the developer inside you and put on your designer hat to transform the plain vanilla list into something more appealing. Stop the debugging session and return to Visual Studio 2012.
Before refining the appearance of the list, you need to add some more user interface elements to the page—such as a TextBlock control to display the application’s title—and make your first app appear more integrated with the Windows 8 environment.
To add a title, you need to modify the XAML source in the MainPage.xaml file, as shown in Example 3-3:
Example 3-3. MainPage.xaml with a GridView control
<Page x:Class="MyFirstApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:MyFirstApp" xmlns:d="http://schemas.m icrosoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="140"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- page title --> <Grid Grid.Row="0" Grid.Column="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="120"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock x:Name="pageTitle" Grid.Column="1" Text="My First Windows 8 App" Style="{StaticResource PageHeaderTextStyle}"/> </Grid> <ListView x:Name="list" DisplayMemberPath="FullName" Grid.Row="1" Grid.Column="0" Margin="116,0,0,46"/> </Grid> </Page>
Now, if you press F5 in Visual Studio, your page should look similar to the one shown in Figure 3-5.
Figure 3-5 The main page with the title.
Example 3-3 used a Grid element as the root element of the page. In XAML, the Grid panel allows you to place child elements in rows and columns, as well as define in advance the number and the properties of each row and column by leveraging the RowDefinitions and ColumnDefinitions properties of the Grid control.
In the example, the main grid was split into two rows. But now it is time to return to the code for a deeper explanation. The first four lines of the Grid control definition are as follows.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="140"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
To define rows and columns of the main Grid control, we used the Grid.RowDefinitions property. This syntax (in the form classtype.propertyname, also known as extended property syntax) represents a standard way to set complex properties using the XAML markup language. Within the RowDefinitions property you’ll find two instances of RowDefinition: the first sets the height equal to 140 pixels, whereas the second uses the “*” (star) character to define an unknown-at-design-time value that can fill the remaining space on the screen. Keep in mind that it is very important to design a user interface that can adapt to the user’s screen resolution; tablets and devices are available with widely varying screen resolutions and orientations. Using relative rather than absolute sizing helps a great deal in achieving the goal of an adaptive interface.
Assigning each graphic element to a cell of the grid suffices to set the Grid.Row and Grid.Column properties of the element itself. These properties are also called attached properties because they don’t belong to the object model of the target element, but are instead “attached” to the control itself. This scenario includes two child elements in the main grid.
First, a secondary Grid control that will contain the title page elements. This Grid control has two attached properties: Grid.Row, with a value of 0, and Grid.Column, also with a value of 0. This will place it in the first row and first column of the main grid.
Next, there is a ListView control, with the properties Grid.Row = “1” and Grid.Column = “0,” that place it in the second row of the first column.
Here are some other useful tidbits of information about how to use the Grid control.
You can omit the Grid.Row and/or Grid.Column properties if their value is 0.
If a Grid control does not explicitly set the RowDefinitions property, it is treated as having a single RowDefinition definition whose Height property is set to “*”.
If a Grid control does not explicitly set the ColumnDefinitions property, it is treated as having a single ColumnDefinition definition whose Width property is set to “*”.
You can set the RowDefinition’s Height property to “Auto,” in which case its size is defined at runtime by the height of the controls it contains.
You can set the ColumnDefinition’s Width property to “Auto,” in which case its size is defined at runtime by the width of the controls it contains.
Continuing the analysis of the XAML code, you’ll find a secondary Grid control, further divided into two columns, whose only child is a TextBlock control.
<TextBlock x:Name="pageTitle" Grid.Column="1" Text="My First Windows 8 App" Style="{StaticResource PageHeaderTextStyle}"/>
The property setting Grid.Column = “1” means that the TextBlock control will be positioned in the second column of the parent Grid control, whereas the Style property references a style called PageHeaderTextStyle using the special {StaticResource} syntax (you will explore the basic concepts underlying such styles in later chapters). For now, just remember that a style is simply a container for property settings—a shared object that can be reused in different scenarios.
The property Grid.Row = “1” has been added to the ListView control so that it will occupy the entire second row of the main grid, and the property Margin = “116,0,0,46” places the ListView control a few pixels away from the edges of the cell. The Margin property is set using four numbers separated by commas. The first number identifies the distance from the left edge and then continuing clockwise; in our example, the ListView control is placed 116 pixels away from the left edge, 0 from the top and right edges, and 46 pixels from the bottom edge.
Now try to add some photos to the project. To do that, simply drag the folder called Photos (included in the Demo Files for this chapter) into Visual Studio, and drop it when your cursor is on the project root called MyFirstApp. As a result of this operation, Visual Studio will create a directory called Photos in the project’s root (at the same level as the Assets and Common folders) containing some .jpg files.
The next step is to modify the Person class to add a custom property called Photo, and define the business component to set that property.
Example 3-4 shows the code for the modified Biz.cs file. Copy Example 3-4 into the Biz.cs file.
Example 3-4. Modified Biz.cs code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstApp { public class Biz { public List<Person> GetPeople() { return new List<Person>() { new Person() { FullName = "Roberto Brunetti", Photo = "Photos/01.jpg" }, new Person() { FullName = "Paolo Pialorsi", Photo = "Photos/02.jpg" }, new Person() { FullName = "Marco Russo", Photo = "Photos/03.jpg" }, new Person() { FullName = "Luca Regnicoli", Photo = "Photos/04.jpg" }, new Person() { FullName = "Vanni Boncinelli", Photo = "Photos/05.jpg" }, new Person() { FullName = "Guido Zambarda", Photo = "Photos/06.jpg" }, new Person() { FullName = "Jessica Faustinelli", Photo = "Photos/07.jpg" }, new Person() { FullName = "Katia Egiziano", Photo = "Photos/08.jpg" } }; } } public class Person { public string FullName { get; set; } public string Photo { get; set; } } }
To make the view of the people contained in the ListView control more appealing, you must modify the control’s ItemTemplate property. It is important to understand that in XAML, a template object is equivalent to the concept of “structure,” and the ItemTemplate property represents the structure of the individual items in the ListView control.
You start by editing the XAML source code of the MainPage.xaml page to make some tweaks to the ListView control.
Replace the ListView definition in the MainPage.xaml:
<ListView x:Name="list" DisplayMemberPath="FullName" Grid.Row="1" Grid.Column="0" Margin="116,0,0,46"/>
with this markup code:
<ListView Grid.Row="1" Grid.Column="0" x:Name="list" Margin="116,0,0,46"> <ListView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding FullName}" FontSize="10" /> </DataTemplate> </ListView.ItemTemplate> </ListView>
The second example removes the DisplayMemberPath property, which displayed only simple strings connected to the FullName property of the bound objects, and replaces it with the ItemTemplate property that accepts objects of type DataTemplate. In this scenario, the DataTemplate consists of a simple label (a TextBlock) with its Text property connected to the FullName property of the bound object; if you now run the application, you will see the list of people displayed in a smaller font. This is not a huge graphical improvement over the previous version, but these steps function as the basis for subsequent activities you will perform.
In the next step, you will try to change the DataTemplate of each item to display both the name and the photo. Replace the DataTemplate definition of the ListView.
<DataTemplate> <TextBlock Text="{Binding FullName}" FontSize="10" /> </DataTemplate>
with this code:
<DataTemplate> <StackPanel Width="200" Height="200"> <TextBlock Text="{Binding FullName}" /> <Image Source="{Binding Photo}" /> </StackPanel> </DataTemplate>
Compared to the previous step, this uses a new panel called StackPanel, which places child items arranged vertically, one under the other, or—if the Orientation property is set to Horizontal—side by side. In this scenario, each item in the ListView will be displayed using a StackPanel that will render the person’s name and photo by binding, respectively, the FullName property with the Text property of a TextBlock and the Photo property with the Source property of an Image control.
Until now we have used the ListView control, which can display a series of vertical elements; now, let’s try to replace the previous ListView definition:
<ListView Grid.Row="1" Grid.Column="0" x:Name="list" Margin="116,0,0,46"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Width="200" Height="200"> <TextBlock Text="{Binding FullName}" /> <Image Source="{Binding Photo}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
with this new markup code that uses a GridView control:
<GridView Grid.Row="1" Grid.Column="0" x:Name="list" Margin="116,0,0,46"> <GridView.ItemTemplate> <DataTemplate> <StackPanel Width="200" Height="200"> <TextBlock Text="{Binding FullName}" /> <Image Source="{Binding Photo}" /> </StackPanel> </DataTemplate> </GridView.ItemTemplate> </GridView>
The GridView control, as the name suggests, is able to display its items in a tabular form, or grid.
If you press F5 in Visual Studio, you will see the result shown in Figure 3-6.
Figure 3-6 Element selected in the customized GridView control.
This outcome is acceptable, but you can do even better using just a bit of creativity and a few lines of XAML code within the DataTemplate. The next listing shows the entire MainPage.xaml page with the code changed in the previous step highlighted in bold.
Replace the entire code of the MainPage.xaml with the following.
<Page x:Class="MyFirstApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:MyFirstApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="140"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- Back button and page title --> <Grid Grid.Row="0" Grid.Column="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="120"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock x:Name="pageTitle" Grid.Column="1" Text="My First Windows 8 App" Style="{StaticResource PageHeaderTextStyle}"/> </Grid> <GridView Grid.Row="1" Grid.Column="0" x:Name="list" Margin="116,0,0,46"> <GridView.ItemTemplate> <DataTemplate> <Grid> <Image Source="{Binding Photo}" Width="200" Height="130" Stretch="UniformToFill" /> <Border Background="#A5000000" Height="45" VerticalAlignment="Bottom"> <StackPanel Margin="10,-2,-2,-2"> <TextBlock Text="{Binding FullName}" Margin="0,20,0,0" Foreground="#7CFFFFFF" HorizontalAlignment="Left" /> </StackPanel> </Border> </Grid> </DataTemplate> </GridView.ItemTemplate> </GridView> </Grid> </Page>
The new DataTemplate uses a Grid as the root element, with two elements nested within it: an Image and a Border. Because the Grid has neither RowDefinitions nor ColumnDefinitions, it will render as a single cell containing the two child elements, following the order defined in the markup—that is, the first child element rendered by the runtime will be the Image control, then the Border control (with all its children) will be rendered in overlay. Beyond those changes, the XAML markup adds only one new thing: the Background property of the Border control that contains the following string “#A5000000.” It is worth noting the first two characters after the #: they represent the alpha channel, or transparency, of the color defined by the subsequent six characters (black, in this case). In fact, in this example, the Border does not have a full and “opaque” color as background, but rather uses a semi-transparent black for graphical purposes.
The result is quite in line with the Windows 8 ecosystem and visually pleasing, as you can see in Figure 3-7.
Figure 3-7 A different customization of the GridView control.
It is worth noting that the controls provided by the framework support all types of input, such as mouse, keyboard, touch screen, and pen for free—in other words, you don’t have to write code to make the controls respond to normal input.