在本文中,将探讨如何开发一个基本的Windows Phone计时器应用。这个应用能够测量400米跑的时间,并且具备添加圈数、保存计时值、关闭屏幕自动关闭等功能。
在寻找Windows Phone上的计时器应用时,发现市场上有很多免费的应用,这激发了开发自己的计时器应用的想法。
为了开发这个应用,需要以下软件:
没有这些软件,应用将无法编译。
Visual Studio为Windows Phone开发提供了多种不同的模板。在开始设计之前,先了解一下这些模板:
最基本的模板是Windows Phone Application。用户会看到一个可滚动的屏幕,可以通过按下按钮移动到下一个屏幕。屏幕可以垂直滚动,但不能水平滚动。开发者可以添加应用程序栏,为用户提供更改设置或选择不同菜单的选项。
Pivot应用程序以标签格式呈现数据。这可能是Windows Phone开发中最常见的格式,用户可以通过水平滑动查看/点击其他标签。Marketplace应用程序就是一个示例。Pivot模板的好处是开发者可以添加应用程序栏。
Panorama应用程序与Pivot应用程序非常相似,但它们没有应用程序栏。基本上,这种类型的应用程序像一个大画布,用户通过水平滑动查看其他可用选项。应用程序标题延伸到多个屏幕,每个部分都比整个手机宽度窄,以便用户可以看到下一个可用的部分。整个屏幕被包裹起来,所以从最后一个选项滑动,屏幕会移动到第一个选项。
为了开发这个应用,选择了Windows PhonePanorama Application。选择的主要原因是喜欢这种类型的应用程序的外观,而且在应用程序中,不需要应用程序栏,因为可以在一个部分中提供所有用户选择。
为了显示经过的时间,开发了一个新的控件:
最初,使用了一个简单的文本块,并在每次经过的时间变化时更新这个文本块。这种方法的问题是文本的更新不平滑。它看起来像旧的Windows桌面画图应用程序,在更新期间文本会闪烁。
为了将数据保存到手机,创建了一个通用类:
这个类基于Silverlight上的隔离存储文章,网址是www.silverlight.net。这个类在每次使用变量(附加到这个类)时自动保存数据。当用户回到这个应用程序(使用OnNavigatedTo事件处理程序)时,设置从隔离存储中加载。
应用程序使用计时器来计算经过的时间。整个应用程序分为三个部分:计时器、设置和关于。计时器部分显示经过的时间,用户控制的圈数和重置按钮。设置部分有一些设置,如关闭屏幕锁定功能和重置计时器值的警告消息。关于部分基本上是关于应用程序的,如版本等。
如上所述,这个应用程序有三个Panorama项目:计时器、设置和关于。每个部分使用一个网格,包含不同的行(可能还有列)。
<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" />
...
</Grid.RowDefinitions>
</Grid>
</controls:PanoramaItem>
<!-- Panorama item two -->
...
</controls:Panorama>
要使用Windows Phone工具包,在XAML文件中添加了命名空间,如下所示(在文件顶部):
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
要使用包含的命名空间,只需如下所示添加前缀:
<toolkit:ToggleSwitch x:Name="ResetWarning" Header="Reset warning" IsChecked="True" Checked="ResetWarning_Checked" Unchecked="ResetWarning_Unchecked" Content="Yes" Grid.Row="1"/>
通常,Microsoft.Phone命名空间提供了许多有用的功能来控制手机,如上所示。要执行与手机相关的任务,如发送电子邮件、启动相机或在MarketPlace中搜索特定内容,请使用Microsoft.Phone.Tasks命名空间。
例如,要打开/关闭空闲时间屏幕锁定,只需使用IdleDetectionMode枚举,如下所示:
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)
{
}
}
这个类使用键/值对从隔离存储中保存或检索数据。这是一个通用类,可以用来保存任何类型的基本数据,如整数、字符串等。要使用这个类,只需如下所示:
PersistSettings currentLap = new PersistSettings("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;
}
}
时间跨度控件是一个用户控件,它在定义的宽度内显示经过的时间字符串。如果经过的时间文本不适合定义的宽度,文本将被截断。这个控件的有趣之处在于它有固定宽度,因此文本不会闪烁,如下所示:
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);
}
}