Now that we understand what is in a newly created Visual Studio project, we can modify it to create Tally. First, we can remove all capabilities in the application manifest, but temporarily leave ID_CAP_NETWORKING so we can debug the app on a phone:
[code]
<Capabilities>
<!– None needed –>
<!– TODO: This is only for debugging on a device: –>
<Capability Name=”ID_CAP_NETWORKING” />
</Capabilities>
[/code]
We can also change the two icon images and remove the splash screen image from the project. Now we’re ready to change MainPage.xaml and MainPage.xaml.cs.
Updating the User Interface
Listing 1.4 contains the XAML needed to create Tally
LISTING 1.4 MainPage.xaml—The User Interface for Tally
[code]
<phone:PhoneApplicationPage
x:Class=”Tally.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”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeHuge}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”Portrait”
shell:SystemTray.IsVisible=”True”>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
<RowDefinition Height=”Auto”/>
</Grid.RowDefinitions>
<!– Row 0: The Header –>
<StackPanel Grid.Row=”0” Margin=”24,24,0,12”>
<TextBlock Text=”TALLY” Style=”{StaticResource PhoneTextNormalStyle}”/>
<TextBlock Text=”tap to count” Margin=”-3,-8,0,0”
Style=”{StaticResource PhoneTextTitle1Style}”/>
</StackPanel>
<!– Row 1: The text block containing the count –>
<TextBlock x:Name=”CountTextBlock” Grid.Row=”1” TextAlignment=”Center”
Text=”0”/>
<!– Row 2: The reset button –>
<Button x:Name=”ResetButton” Grid.Row=”2” Click=”ResetButton_Click”
Content=”reset”/>
</Grid>
</phone:PhoneApplicationPage>
[/code]
Notes:
- Unnecessary attributes have been removed, the two text blocks in the header have been updated, and the old ContentGrid has been replaced with a text block and a button. A third row has been added to the grid to hold the button.
- The text block and reset button have been assigned names (CountTextBlock and ResetButton, respectively) so they can be referenced easily in the code-behind file.
- The page’s font size has been changed to PhoneFontSizeHuge (186.667 px, which is 140 pt). This value is inherited by CountTextBlock only, because the other text blocks have their own styles explicitly set and the text inside ResetButton doesn’t inherit page-level text properties.
- The reset button has a Click event, and it is assigned to a handler called ResetButton_Click that must be defined in the code-behind file.
How x:Name Works
The XAML compiler generates an internal field in the root class (MainPage in this case) for each named element, using the element name as the field name.Therefore,Tally’s MainPage has a field called CountTextBlock and a field called ResetButton that can be used in the code-behind file.You can look inside the hidden C# file that gets generated by the XAML compiler in the objDebug or objRelease folder (MainPage.g.cs, where the g stands for generated) to see how this is done inside the implementation of InitializeComponent:
[code]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
Application.LoadComponent(this, new
Uri(“/Tally;component/MainPage.xaml”, UriKind.Relative));
this.CountTextBlock = (TextBlock)this.FindName(“CountTextBlock”);
this.ResetButton = (Button)this.FindName(“ResetButton”);
}
[/code]
The FindName method defined on many Silverlight elements recursively searches the element’s children for an element marked with a matching x:Name.Once you obtain an instance to the named element, you can do anything you want with it: set properties, attach event handlers, call methods, and so on.
Many elements have a Name property that you can set in XAML without using the x: prefix, but the x:Name keyword works in more scenarios, so that is what this book uses consistently.This book also generally only names an element if it needs to be accessed in the code-behind file.
Updating the Code-Behind
Listing 1.5 contains all the logic needed to make Tally work.
LISTING 1.5 MainPage.xaml.cs—The Code-Behind for Tally
[code]
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using WindowsPhoneApp; // For the Setting class
namespace Tally
{
public partial class MainPage : PhoneApplicationPage
{
int count = 0;
// Remember what the user typed, for future app activations or launches
Setting<int> savedCount = new Setting<int>(“SavedCount”, 0);
public MainPage()
{
InitializeComponent();
}
// Handle a tap anywhere on the page (other than the Button)
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
this.count++;
this.CountTextBlock.Text = this.count.ToString(“N0”);
}
// Handle a tap on the button
void ResetButton_Click(object sender, RoutedEventArgs e)
{
this.count = 0;
this.CountTextBlock.Text = this.count.ToString(“N0”);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Persist state when leaving for any reason (Deactivated or Closing)
this.savedCount.Value = this.count;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Restore persisted state
this.count = this.savedCount.Value;
this.CountTextBlock.Text = this.count.ToString(“N0”);
}
}
}
[/code]
Notes:
- Often, a class that exposes an event XXX also exposes an OnXXX method that subclasses can override rather than attaching a handler to the event. By overriding OnMouseLeftButtonDown, this code effectively handles the page’s
MouseLeftButtonDown event, which gets raised for any tap on the page except for taps on the button—exactly the condition we want for incrementing the count and updating the TextBlock’s Text property accordingly. This method conveniently doesn’t get called on button taps because buttons internally handle this event (as well as the MouseLeftButtonUp event) in order to provide special behavior, preventing it from bubbling up to the page. - “N0” formatting is used whenever the count is turned into a string. This basically means “show it as a natural number (with the thousands separator).” The thousands separator is a comma for United States English but it automatically varies based on the phone’s “region & language” settings.
- Button has its own event for a tap, called Click, which should be handled instead of its MouseLeftButtonDown event. Another reason is that a button is not supposed to invoke its action when it is tapped; it should invoke it when it is released. A button highlights itself when a finger presses it (MouseLeftButtonDown, internally) and unhighlights itself while raising the separate Click event when the finger is removed (MouseLeftButtonUp). This button’s Click event handler—ResetButton_Click— simply resets the count and updates the TextBlock accordingly.
- The Setting member called savedCount, along with the OnNavigatedFrom/OnNavigatedTo methods, provide a simple way for the count to be remembered whenever the user leaves the app and restored whenever the user comes back to it (even if the user reboots the phone).
- Remembering to update TextBlock.Text every time count changes can be errorprone, especially for larger apps. A more robust approach would be to turn count into a dependency property that can be data-bound to the text block’s Text property, enabling the display to update automatically whenever the value is changed.
The Finished Product