Introduction
Few weeks ago, I wanted to use a stop watch to measure the time it takes to complete a 400 meter lap. I looked at all the installed apps in my Windows phone but couldn't find one available. A quick search on Windows market place and I found plenty of stop watch applications on Windows market place for free which encouraged me to write one for myself.
This application is a very basic stop watch. It starts the stop watch once a user presses start button. The timer continues until user presses stop button. While stop watch is on, user can add laps. The application also has some other basic features like:
- Turning off the idle time screen out. This feature was important as, to save battery, usually phone screen automatically turns off if inactive for few seconds.
- Saving timer values if moved away from the application. This is to save the values if user moves away from stop watch application accidentally without stopping the timer.
Required Software
Without these software, this application will not compile.
Basic Design
Visual Studio provides many different templates for Windows Phone development as shown below:
Before I start my design choices, I will present a basic understanding of few of these templates.
The most basic template is Windows Phone Application. User is presented with a scrollable screen. User can move to next screen by pushing on some button. The screen can be scrollable vertically but not horizontally. A developer can add application bar to give user choices like change settings or selection of different menu.
Pivot application presents data in a tab format. This might be the most common format for Windows phone development as user can swipe horizontally to see/tap other tabs. Check the Marketplace application for a sample of this template. The nice thing about the pivot template is that a developer can add an application bar.
Panaroma applications are very similar to pivot application but they don't have any application bar. Basically this type of application acts like a large canvas and user swipes horizontally to see other available options. The application title stretches to multiple screens and each section is narrower than the entire phone width so that the next available section is visible to user. The entire screens is wrapped around so that swiping from last option, the screen will move to first option.
To develop this application, I have chosen Windows Phone Panaroma Application. The choice is mainly based on the fact that I liked the look of this type of application and in my application, I don't need an application bar as I can give all the user choices in one of the sections itself.
To show elapsed time, I developed a new control, TimeSpanControl
. Initially, I used a simple text block and was updating this text block everytime elapsed time changed. The problem with this approach was that the update of the text was not smooth. It looked like an old Windows desktop paint application where the text used to flicker during update.
To save data to/from phone, I created a generic class, PersistSettings
. This class is based on an article on isolated storage in Silverlight on the www.silverlight.net web-site. This class automatically saves the data everytime a variable (attached to this class) is used. When user comes back to this application (using OnNavigatedTo
event handler), the settings load from the isolated storage.
The application uses a timer to calculate the elapsed time.
The entire application is divided into 3 sections: timer, settings and about. The timer section displays the elapsed time, user controlled lap count and reset buttons. The settings section has few settings like turning off the screen lock feature and a warning message for reset timer values. The about section is basically about the application like the version, etc.
Using the Code
As mentioned above, this application has three pararoma items: timer
, settings
and about
. Each section uses a grid with various rows (and possibly columns).
<controls:Panorama x:Name="StopWatch" Title="stop watch" Foreground="White"
FontFamily="Calibri" FontSize="18">
<controls:PanoramaItem x:Name="ElapedTimer" Foreground="White" FontSize="16" Header="Timer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
...
...
...
To use the Windows phone toolkit, I added the namespace in the XAML file as shown below (on top of the file):
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"<br>
To use an included namespace
, just prefix the name as shown below:
<toolkit:ToggleSwitch x:Name="ResetWarning"
Header="Reset warning"<br> IsChecked="True"
Checked="ResetWarning_Checked"
Unchecked="ResetWarning_Unchecked" Content="Yes"
Grid.Row="1"/><local:timespancontrol fontsize="40"
fontfamily="Segoe WP Bold" horizontalalignment="Center"
digitwidth="28" grid.columnspan="4" grid.row="0"
x:name="TotalTimeDisplay"><local:timespancontrol
x:name="TotalTimeDisplay" grid.row="0" grid.columnspan="4"
digitwidth="28" horizontalalignment="Center"
fontfamily="Segoe WP Bold" fontsize="40" />
The exact same way any user controls can be used.
Usually Microsoft.Phone namespace
provides many useful features to control phones like shown above. To perform phone related tasks like sending an email, start camera or search a particular content in the MarketPlace
, use the Microsoft.Phone.Tasks namespace
.
As an example, to turn on/off the idle time screen lock, just use the IdleDetectionMode enum
as shown below:
private void UserIdleModeOnOff_Checked(object sender, RoutedEventArgs e)
{
this.UserIdleModeOnOff.Content = "Yes";
try
{
Microsoft.Phone.Shell.PhoneApplicationService.Current.ApplicationIdleDetectionMode =
Microsoft.Phone.Shell.IdleDetectionMode.Disabled;
}
catch (InvalidOperationException ex)
{
}
}
Persist Settings Class
This class uses a key/value pair to save or retrieve data from isolated storage. This is a generic class and can be used to save any kind of basic data like integer, string, etc. To use this class, just use it as:
PersistSettings<int> currentLap = new PersistSettings<int> ("LapCount", 0);
where LapCount
is the value we want to save. To use the LalCount
, just use as LapCount.Value
. The Value
property is implemented in a very simple way:
public T Value
{
get
{
if (!IsolatedStorageSettings.ApplicationSettings.TryGetValue(
this.name, out this.value))
{
IsolatedStorageSettings.ApplicationSettings[this.name] = this.initialValue;
}
return this.value;
}
set
{
IsolatedStorageSettings.ApplicationSettings[this.name] = value;
this.value = value;
}
}
Time Span Control
The time span control is a user control which displays the elapsed time string within a defined width. If the elapsed time text won't fit within the defined width, the text will cut off. The interesting part of this control is that it has a fixed width and hence the text doesn't flicker as shown below:
public partial class TimeSpanControl : UserControl
{
int digitWidth;
TimeSpan time;
public TimeSpanControl()
{
InitializeComponent();
if (DesignerProperties.IsInDesignTool)
this.LayoutRoot.Children.Add(new TextBlock { Text = "00:00:00.0" });
}
public int DigitWidth
{
get { return this.digitWidth; }
set
{
this.digitWidth = value;
this.Time = this.time;
}
}
public TimeSpan Time
{
get { return this.time; }
set
{
this.LayoutRoot.Children.Clear();
string hoursString = value.Hours.ToString();
ConcatenateTime((value.Hours / 10).ToString());
ConcatenateTime((value.Hours % 10).ToString());
this.LayoutRoot.Children.Add(new TextBlock { Text = ":" });
string minutesString = value.Minutes.ToString();
ConcatenateTime((value.Minutes / 10).ToString());
ConcatenateTime((value.Minutes % 10).ToString());
this.LayoutRoot.Children.Add(new TextBlock { Text = ":" });
ConcatenateTime((value.Seconds / 10).ToString());
ConcatenateTime((value.Seconds % 10).ToString());
this.LayoutRoot.Children.Add(new TextBlock
{
Text = System.Globalization.CultureInfo.CurrentUICulture.
NumberFormat.NumberDecimalSeparator
});
ConcatenateTime((value.Milliseconds / 100).ToString());
this.time = value;
}
}
void ConcatenateTime(string timeValue)
{
TextBlock textBlock = new TextBlock
{
Text = timeValue,
Width = this.DigitWidth,
HorizontalAlignment = HorizontalAlignment.Center
};
this.LayoutRoot.Children.Add(textBlock);
}
}
History
- Feb 2012: First version
- Feb 13, 2012: Updated to add history of completed lap and time