Windows phone Stopwatch

这里使用ToggleButton实现一个跑表,其主要功能:

1、能够显示跑表基本功能,即 开始/停止/重置

2、能够选择跑表的显示格式 (弹出自定义对话框)

3、当程序进入墓碑化状态时并返回时  跑表能显示正确的时间。

实现过程:

首先  绘制界面,只需要显示一个TextBlock和一个ToggleButton按钮 .

ToggleButton的不同于一般的Button 他又一个IsChecked属性,有三种可能值 {x:Null},ture,false 和三个事件Checked, Indeterminate,Unchecked 。

这里需要为ToggleButton的Checked和Unchecked添加处理事件。

当然这里也要使用添加ApplicationBar来进行设置和重置,xaml如下:

View Code
  <!--显示秒表-->
<Grid VerticalAlignment="Center" Margin="25 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0" Grid.Column="0" BorderThickness="2" BorderBrush="{StaticResource PhoneForegroundBrush}">
<TextBlock Grid.Row="0" Name="elapsedText" Text="0" FontFamily="Arial" FontSize="{StaticResource PhoneFontSizeExtraLarge}" TextAlignment="Center" Margin="0 5 0 5"></TextBlock>
</Border>
<!--切换秒表的按钮-->
<ToggleButton Grid.Row="1" Content="开始" Name="startStopToggle" Checked="OnToggleButtonChecked" Unchecked="OnToggleButtonChecked" HorizontalAlignment="Center"></ToggleButton>
</Grid>


<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton x:Name="formatAppbarIconButton" IconUri="/Images/appbar.feature.settings.rest.png" Click="formatAppbarIconButton_Click" Text="格式设置"/>
<shell:ApplicationBarIconButton x:Name="resetAppbarIcoButton" Click="resetAppbarIcoButton_Click" IconUri="/Images/appbar.refresh.rest.png" Text="重置"/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

其次   是实现跑表的功能

这里用到了Stopwatch类 ,该类位于System.Diagnostics名字空间下,是系统秒表对象,可以准确的测量运行时间,程序员经常使用该类来 判断程序执行时间,一般通过其Elapsed属性获取运行时间,这里只需要将运行时间实时更新到界面上就OK了

这里通过在点击开始的时候 使用 CompositionTarget.Rendering 事件,只要该事件关联了事件处理程序,应用程序就会开始不断地调用这个事件处理程序在其处理函数中刷新界面,这样就能使时间不断变化了。注意要在单击停止的时候将事件处理 取消关联。 

ToggleButton的Checked和Unchecked的事件处理 OnToggleButtonChecked 如下:

View Code
 private void OnToggleButtonChecked(object sender, RoutedEventArgs e)
{
if ((bool)startStopToggle.IsChecked)
{
startStopToggle.Content
= "停止";
stopwatch.Start();
//CompositionTarget 是一个类,表示正在其上绘制您的应用程序的显示图面。
//WPF 动画引擎为创建基于帧的动画提供了许多功能。但是,在有些应用程序方案中,您需要根据每个帧控制呈现。
//使用 CompositionTarget 对象,可以基于每个帧回调来创建自定义动画。
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
//跑表运行的时候不允许 重置和改变格式
formatAppbarIconButton.IsEnabled = false;
resetAppbarIcoButton.IsEnabled
= false ;
}
else
{
startStopToggle.Content
= "开始";
stopwatch.Stop();
CompositionTarget.Rendering
-= new EventHandler(CompositionTarget_Rendering);
formatAppbarIconButton.IsEnabled
= true;
resetAppbarIcoButton.IsEnabled
= true;
}
}

void CompositionTarget_Rendering(object sender, EventArgs e)
{
DisplayTime();
}

 因为.net提供的Stopwatch秒表  经过的时间是以TimeSpan对象来表示的,但是这并不是我们想要的,所以需要定义自己的时间显示格式,这里定义了DisplayTime方法,用来显示自定义格式的时间,如下:

View Code
 private void DisplayTime()
{
//秒表经过时间
TimeSpan elapsedTime = stopwatch.Elapsed + adjustment;
string str = null;

switch ((Application.Current as App).stopwatchSettings.TimeFormat)
{
case ElapsedTimeFormat.HourMinuteSecond:
str
= string.Format("{0:D2}:{1:D2}:{2:D2}{3}{4:D2}", elapsedTime.Hours, elapsedTime.Minutes,
elapsedTime.Seconds, decimalSeparator, elapsedTime.Milliseconds
/ 10);
break;
case ElapsedTimeFormat.Seconds:
str
= string.Format("{0:F2} 秒", elapsedTime.TotalSeconds);
break;
case ElapsedTimeFormat.Milliseconds:
str
= string.Format("{0:F2} 毫秒", elapsedTime.TotalMilliseconds);
break;
}
elapsedText.Text
= str;
}

这里的adjustment是为了记录时间而定义的,因为程序进入墓碑化只可能记录一些信息,stopwatch对象是不会继续运行的,所以可以定义一个变量来记录当前秒表的时间,当墓碑化的时候将和墓碑化开始时间记录下来,然后在重新回到该程序的时候,将回到该程序的时间,减去墓碑化开始时间再加上原来的时间就OK了,看起来貌似程序一直在运行。

实现这个需要重写OnNavigatedFrom和OnNavigatedTo方法,如下:

View Code
 //以下为解决墓碑化状态问题
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
//进入墓碑化状态时 将当前一些信息保存
PhoneApplicationService service = PhoneApplicationService.Current;
service.State[
"stopWatchRunning"] = (bool)startStopToggle.IsChecked;
service.State[
"adjustment"] = adjustment + stopwatch.Elapsed;
service.State[
"tombstoneBeginTime"] = DateTime.Now;
base.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
PhoneApplicationService service
= PhoneApplicationService.Current;
if (service.State.ContainsKey("stopWatchRunning"))
{
adjustment
= (TimeSpan)service.State["adjustment"];
if ((bool)service.State["stopWatchRunning"])
{
adjustment
+= DateTime.Now - (DateTime)service.State["tombstoneBeginTime"];
startStopToggle.IsChecked
= true;
//上面就会调用OnToggleButtonChecked处理,所以这里下面不用再调用DisplayTime()方法了
}
}
else
{
DisplayTime();
}
base.OnNavigatedTo(e);
}

接下来就是实现 弹出自定义对话框 ,选择时间格式了,其实并不是真正的对话框,只是,

在单击格式设置ApplicationBarIconButton的时候,在原来的元素上覆盖一层灰色,然后再在灰色上面显示 一个带Border的Grid,里面有可选项,和确定、取消按钮,当确定或取消的时候将这些隐藏并将选择保存下来而已。首先在Xaml中添加:

View Code
  <!--定义一个矩形将界面元素使用灰色覆盖-->
<Rectangle x:Name="grayRectangle" Fill="#80000000" Visibility="Collapsed"></Rectangle>
<!--自定义选择时间格式对话框-->
<Border x:Name="selectFormatDialog"
Background
="{StaticResource PhoneChromeBrush}"
BorderBrush
="{StaticResource PhoneForegroundBrush}"
BorderThickness
="3" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Background
="SkyBlue">
<TextBlock Text="选择秒表时间格式" Opacity="50">
</TextBlock></Border>
<StackPanel x:Name="radioButtonPanel"
Grid.Column
="0"
Grid.Row
="1"
Grid.ColumnSpan
="2"
HorizontalAlignment
="Center">
<RadioButton Content="时:分:秒(00:00:00.00)" Tag="HourMinuteSecond" />
<RadioButton Content="秒 (0.00 秒)" Tag="Seconds" />
<RadioButton Content="毫秒 (0.00 毫秒)" Tag="Milliseconds"/>
</StackPanel>
<Button x:Name="okButton" Content="确定" Click="okButton_Click" Grid.Column="0" Grid.Row="2"></Button>
<Button x:Name="cancelButton" Content="取消" Click="cancelButton_Click" Grid.Column="1" Grid.Row="2"></Button>
</Grid>
</Border>

默认是隐藏的,当单击格式设置的时候,将其显示出来,当然还要为其确定取消做处理,如下:

View Code
        //选择秒表格式按钮事件处理
private void formatAppbarIconButton_Click(object sender, EventArgs e)
{
grayRectangle.Visibility
= Visibility.Visible;
selectFormatDialog.Visibility
= Visibility.Visible;

ElapsedTimeFormat currentFormat
= (Application.Current as App).stopwatchSettings.TimeFormat;
foreach (UIElement child in radioButtonPanel.Children)
{
RadioButton radioButton
= child as RadioButton;
ElapsedTimeFormat radioFormat
=(ElapsedTimeFormat)Enum.Parse(typeof(ElapsedTimeFormat), radioButton.Tag as string, true);
radioButton.IsChecked
= (radioFormat == currentFormat);
}
}

private void okButton_Click(object sender, RoutedEventArgs e)
{
foreach (UIElement child in radioButtonPanel.Children)
{
RadioButton radioButton
= child as RadioButton;
if ((bool)radioButton.IsChecked)
{
(Application.Current
as App).stopwatchSettings.TimeFormat = (ElapsedTimeFormat)Enum.Parse(typeof(ElapsedTimeFormat), radioButton.Tag as string, true);
}
cancelButton_Click(sender, e);
}
}

private void cancelButton_Click(object sender, RoutedEventArgs e)
{
grayRectangle.Visibility
= Visibility.Collapsed;
selectFormatDialog.Visibility
= Visibility.Collapsed;
DisplayTime();
}

最后一个是重置,只需将adjustment清空,stopwatch重置即可,其处理函数如下:

View Code
  //重置
private void resetAppbarIcoButton_Click(object sender, EventArgs e)
{
stopwatch.Reset();
adjustment
= new TimeSpan();
DisplayTime();
}

在这个过程中遇到问题是:不能通过x:Name来引用ApplicationBarIconButton,可以通过在构造函数中 获取他然后在下面比较方便的使用,如下:

View Code
  public myStopwatch()
{
InitializeComponent();
DisplayTime();
//这里需要注意 不能通过x:Name来引用ApplicationBarIconButton,可通过下面的方式方便引用
formatAppbarIconButton = ApplicationBar.Buttons[0] as ApplicationBarIconButton;
resetAppbarIcoButton
= ApplicationBar.Buttons[1] as ApplicationBarIconButton;
}

这样就完成了,如下图:

            运行状态

      选择秒表格式

                                 Landscape状态

demo 文件:MyStopwatch.rar

原文地址:https://www.cnblogs.com/yinghuochong/p/2179295.html