Windows Phone 7 的秒表应用程序






4.96/5 (14投票s)
Windows Phone 7 的秒表应用程序。
引言
几周前,我想使用秒表来测量完成 400 米跑所需的时间。我查看了 Windows Phone 上所有已安装的应用程序,但找不到可用的。在 Windows 市场快速搜索一下,我发现 Windows 市场上有许多免费的秒表应用程序,这促使我决定自己写一个。
这个应用程序是一个非常基础的秒表。一旦用户按下开始按钮,它就会启动秒表。计时器会一直运行,直到用户按下停止按钮。在秒表运行时,用户可以添加圈数。该应用程序还有一些其他基本功能,例如:
- 关闭屏幕自动锁定。此功能很重要,因为为了节省电池,通常手机屏幕在不活动几秒钟后会自动关闭。
- 在离开应用程序时保存计时器值。这是为了在用户不小心离开秒表应用程序而未停止计时器时保存值。
所需软件
- Silverlight for Windows Phone 工具包
- Windows Phone 开发人员工具
- Visual Studio 2010
没有这些软件,此应用程序将无法编译。
基本设计
Visual Studio 为 Windows Phone 开发提供了许多不同的模板,如下所示:
在开始我的设计选择之前,我将简要介绍其中一些模板。
最基本的模板是 Windows Phone Application。用户会看到一个可滚动屏幕。用户可以通过按下某个按钮跳转到下一个屏幕。屏幕可以垂直滚动,但不能水平滚动。开发者可以添加应用程序栏,为用户提供更改设置或选择不同菜单等选项。
Pivot 应用程序以标签格式呈现数据。这可能是 Windows Phone 开发中最常见的格式,因为用户可以通过水平滑动查看/点击其他标签。请查看 Marketplace 应用程序以获取此模板的示例。Pivot 模板的优点是开发者可以添加应用程序栏。
Panorama 应用程序与 Pivot 应用程序非常相似,但它们没有应用程序栏。基本上,这种应用程序就像一个大画布,用户通过水平滑动来查看其他可用选项。应用程序标题会跨越多个屏幕,每个部分比整个手机屏幕窄,以便用户可以看到下一个可用部分。整个屏幕是环绕的,因此从最后一个选项滑动,屏幕将移至第一个选项。
为了开发此应用程序,我选择了 Windows Phone Panorama Application。选择此模板主要是基于我喜欢这种应用程序的外观,而且我的应用程序不需要应用程序栏,因为我可以在其中一个部分中提供所有用户选项。
为了显示经过的时间,我开发了一个新的控件,TimeSpanControl
。最初,我使用了一个简单的文本块,并且每次经过的时间改变时都会更新这个文本块。这种方法的问题是文本更新不流畅。它看起来像旧的 Windows 桌面画图应用程序,文本在更新时会闪烁。
为了保存/从手机读取数据,我创建了一个通用类,PersistSettings
。该类基于 www.silverlight.net 网站上关于 Silverlight 隔离存储的文章。该类在每次使用变量(附加到此类)时自动保存数据。当用户返回此应用程序时(使用 OnNavigatedTo
事件处理程序),设置会从隔离存储中加载。
该应用程序使用一个计时器来计算经过的时间。
整个应用程序分为 3 个部分:计时器、设置和关于。计时器部分显示经过的时间、用户控制的圈数和重置按钮。设置部分包含一些设置,例如关闭屏幕锁定功能以及重置计时器值的警告消息。关于部分主要是关于应用程序的信息,例如版本等。
Using the Code
如上所述,此应用程序有三个 panorama 项目:timer
、settings
和 about
。每个部分都使用一个带有各种行(可能还有列)的网格。
<controls:Panorama x:Name="StopWatch" Title="stop watch" Foreground="White"
FontFamily="Calibri" FontSize="18">
<!--Panorama item one-->
<controls:PanoramaItem x:Name="ElapedTimer" Foreground="White" FontSize="16" Header="Timer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
...
...
<!--Panorama item two-->
...
为了使用 Windows Phone 工具包,我在 XAML 文件中添加了命名空间,如下所示(在文件顶部):
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"<br>
要使用包含的 namespace
,只需像下面一样添加前缀即可:
<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" />
与使用任何用户控件完全相同。
通常,Microsoft.Phone namespace
提供了许多有用的功能来控制手机,如上所示。要执行与手机相关的任务,如发送电子邮件、启动相机或在 MarketPlace
中搜索特定内容,请使用 Microsoft.Phone.Tasks namespace
。
例如,要打开/关闭空闲时间屏幕锁定,只需使用 IdleDetectionMode enum
,如下所示:
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 (持久化设置类)
此类使用键/值对来保存或从隔离存储中检索数据。这是一个通用类,可用于保存任何类型的基本数据,如整数、字符串等。要使用此类,只需像这样使用:
PersistSettings<int> currentLap = new PersistSettings<int> ("LapCount", 0);
其中 LapCount
是我们要保存的值。要使用 LapCount
,只需像 LapCount.Value
一样使用。Value
属性以非常简单的方式实现:
public T Value
{
get
{
// Try to get the value from Isolated Storage
if (!IsolatedStorageSettings.ApplicationSettings.TryGetValue(
this.name, out this.value))
{
// Value is not set so set it for future use
IsolatedStorageSettings.ApplicationSettings[this.name] = this.initialValue;
}
return this.value;
}
set
{
// Save the value to Isolated Storage
IsolatedStorageSettings.ApplicationSettings[this.name] = value;
this.value = value;
}
}
Time Span Control (时间跨度控件)
时间跨度控件是一个用户控件,可在定义的宽度内显示经过的时间字符串。如果经过的时间文本不适合定义的宽度,文本将被截断。此控件有趣之处在于它具有固定宽度,因此文本不会闪烁,如下所示:
public partial class TimeSpanControl : UserControl
{
int digitWidth;
TimeSpan time;
public TimeSpanControl()
{
// Required to initialize variables
InitializeComponent();
// In design mode, show something other than an empty text box
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();
// Hours
string hoursString = value.Hours.ToString();
ConcatenateTime((value.Hours / 10).ToString());
ConcatenateTime((value.Hours % 10).ToString());
this.LayoutRoot.Children.Add(new TextBlock { Text = ":" });
// Support two digits of minutes digits
string minutesString = value.Minutes.ToString();
ConcatenateTime((value.Minutes / 10).ToString());
ConcatenateTime((value.Minutes % 10).ToString());
this.LayoutRoot.Children.Add(new TextBlock { Text = ":" });
// Two digits for Seconds
ConcatenateTime((value.Seconds / 10).ToString());
ConcatenateTime((value.Seconds % 10).ToString());
// Add the decimal separator
this.LayoutRoot.Children.Add(new TextBlock
{
Text = System.Globalization.CultureInfo.CurrentUICulture.
NumberFormat.NumberDecimalSeparator
});
// milliseconds display
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);
}
}
历史
- 2012 年 2 月:初始版本
- 2012 年 2 月 13 日:更新以添加已完成圈数和时间的历史记录