使用从 ScheduledNotification 继承的 Alarm 和 Reminder 类以及 ScheduledActionService 类创建向系统注册计划通知。警报和提醒计划在未来的指定时间启动并且可以配置为按定期计划启动。启动提醒时,会启动一个对话框,该对话框显示标题以及您应用程序指定的其他文本内容。如果用户点按提醒 UI,您的应用程序会启动并导航到您指定的页面。您可以使用查询字符串参数在启动时将信息传递到您的应用程序。启动警报时,始终显示标题“Alarm”以及您提供的其他文本内容。警报还允许您指定启动时播放的自定义声音文件。如果用户点按警报 UI,则用来创建警报的应用程序会启动,而且会显示初始应用程序页面。
本主题将带您完成创建使用警报和提醒的应用程序。这两种类型的计划通知的用法非常相似。对于警报和提醒,此示例中的代码只有一小部分不同。在下面的步骤中,您将实现三个应用程序页面:
创建一个用来列出计划通知的页面。该页面显示由该应用程序创建和注册的所有警报和提醒的列表。该页面还允许您从列表中删除通知。
创建一个用来添加计划通知的页面。该页面使用 Silverlight 控件创建一个输入窗体,该窗体允许用户创建新的警报和提醒。
创建由提醒启动的页面。这是用户点按提醒对话框时启动并且由该应用程序将导航到的页面。该页面将显示通过查询字符串传递到页面的数据。该功能仅适用于提醒。警报始终启动初始应用程序页面并且不传递查询字符串参数。
您在此示例中创建的第一个页面就是列出为您的应用程序注册的所有警报和提醒的页面。本示例使用 ListBox 控件,该控件将数据绑定到包含计划通知列表的 IEnumerable 对象。但是,为了简便起见,本示例将不会实现全部 Model-View-ViewModel 框架。有关此常见 Silverlight 应用程序模式的更多信息,请参阅在 Windows Phone 应用程序中实现模型视图查看模型模式。
在 Visual Studio 中,创建一个新的“Windows Phone 应用程序”项目。此模板在“Silverlight for Windows Phone”类别中。
创建提醒列表页面的第一步是采用 XAML 创建用户界面。由于创建外观美观的 UI 所需的 XAML 代码非常多,因此首先提供 XAML 代码,然后再突出显示重要的元素。有关使用 Silverlight 控件的详细信息,请参阅 Windows Phone 的控件。
将以下代码粘贴到您项目的 MainPage.xaml 文件中。该代码应粘贴在名为“ContentPanel”的 Grid 元素中。
<TextBlock Text="you have no notifications registered" Name="EmptyTextBlock" Visibility="Collapsed"/> <ListBox Name="NotificationListBox"> <ListBox.ItemTemplate> <DataTemplate> <Grid Background="Transparent" Margin="0,0,0,30"> <Grid.ColumnDefinitions> <ColumnDefinition Width="380"/> <ColumnDefinition Width="50"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0"> <StackPanel Orientation="Vertical"> <TextBlock Text="{Binding Title}" TextWrapping="NoWrap" Foreground="{StaticResource PhoneAccentBrush}" FontWeight="Bold"/> <TextBlock Text="{Binding Content}" TextWrapping="Wrap" Foreground="{StaticResource PhoneAccentBrush}"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="begin "/> <TextBlock Text="{Binding BeginTime}" HorizontalAlignment="Right"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="expiration "/> <TextBlock Text="{Binding ExpirationTime}" HorizontalAlignment="Right"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="recurrence "/> <TextBlock Text="{Binding RecurrenceType}" HorizontalAlignment="Right"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="is scheduled? "/> <TextBlock Text="{Binding IsScheduled}" HorizontalAlignment="Right"/> </StackPanel> </StackPanel> </Grid> <Grid Grid.Column="1"> <Button Tag="{Binding Name}" Click="deleteButton_Click" Content="X" BorderBrush="Red" Background="Red" Foreground="{StaticResource PhoneBackgroundBrush}" VerticalAlignment="Top" BorderThickness="0" Width="50" Padding="0,0,0,0"></Button> </Grid> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
查看您粘贴的代码并注意以下元素:
第一个 TextBlock 控件用于在没有计划通知显示时向用户显示消息。C# 代码隐藏页面在没有项和列表时将该控件的 Visibility 属性切换为 Visible,在列表中有要显示的项时切换为 Collapsed。
ListBox 元素定义将列出所有计划通知及其关联数据的控件。其余 XAML 代码实际上并不向列表中添加项,而是包含一个告知 ListBox 如何显示绑定到它的数据的 DataTemplate。ListBox 名称“NotificationListBox”将用于在 C# 代码隐藏页面中引用该控件。
Grid 和 StackPanel 元素是用于组织其他控件布局的容器控件。
Grid 和 StackPanel 控件中的 TextBlock 元素显示 ScheduledNotification 类中的属性值。例如,Content 属性是警报或提醒的文本内容。RecurrenceType 属性告知您启动提醒的间隔。语法“{Binding RecurrenceType}”将这些控件中每个控件的 Text 属性映射到指定的属性名称。包含其他 TextBlock 控件是为了为每个值提供一个标签。请注意,Alarm 类不支持 Title 属性,因此对警报来说,绑定到该属性的控件将为空。
最后,添加了一个 Button 以允许用户删除计划通知。警报或提醒的 Name 属性用于唯一标识每个通知。该值绑定到 Button 的 Tag 属性,以便该按钮的 Click 事件处理程序 deleteButton_Click 可以确定要删除的通知。稍后会将该处理程序添加到 C# 代码隐藏页面。
需要添加到 MainPage.xaml 的最后一项为 ApplicationBar。ApplicationBar 将拥有一个 ApplicationBarIconButton,用户可以通过单击该按钮转到用于添加新计划通知的页面。将以下代码粘贴到模板中包含的已注释掉的示例 ApplicationBar 代码上。确保正确替换注释以便此代码不会被注释掉。
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/add.png" Text="Add" Click="ApplicationBarAddButton_Click"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
现在,可以在 MainPage.xaml.cs 中修改该代码。首先,您需要添加一个 using 指令以包括 Reminder 类所在的命名空间。将此行添加到位于文件顶部的另一个 using 指令中。
using Microsoft.Phone.Scheduler;
接下来,添加一个类型为 IEnumerable<ScheduledNotification> 的类变量。变量类型的“<ScheduledNotification>”部分指示 IEnumerable 对象将包含 ScheduledNotification 对象。紧接着所示的 MainPage 类定义下面添加此代码。
public partial class MainPage : PhoneApplicationPage { IEnumerable<ScheduledNotification> notifications;
创建一个名为 ResetItemsList 的方法。该方法将使用 GetActions<(Of <<'(T>)>>)()()()() 方法检索已向该应用程序的 ScheduledActionService 注册的 ScheduledNotification 对象。
如果计划通知列表包含一个或多个项,则会将通知用户没有注册通知的文本块折叠,否则会使其可见。最后,ListBox 的 ItemsSource 属性设置为包含通知列表的 IEnumerable 类变量。这将强制 ListBox 自行更新。将以下方法定义粘贴到 MainPage 类定义中。
private void ResetItemsList() { // Use GetActions to retrieve all of the scheduled actions // stored for this application. The type <Reminder> is specified // to retrieve only Reminder objects. //reminders = ScheduledActionService.GetActions<Reminder>(); notifications = ScheduledActionService.GetActions<ScheduledNotification>(); // If there are 1 or more reminders, hide the "no reminders" // TextBlock. IF there are zero reminders, show the TextBlock. //if (reminders.Count<Reminder>() > 0) if (notifications.Count<ScheduledNotification>() > 0) { EmptyTextBlock.Visibility = Visibility.Collapsed; } else { EmptyTextBlock.Visibility = Visibility.Visible; } // Update the ReminderListBox with the list of reminders. // A full MVVM implementation can automate this step. NotificationListBox.ItemsSource = notifications; }
重写页面的 PhoneApplicationPage 基类的 OnNavigatedTo(NavigationEventArgs) 方法。只要用户导航到该页面(包括应用程序启动并第一次显示该页面时)就调用该类。只要引发此事件,就调用刚刚定义的 ResetItemsList 方法。
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { //Reset the ReminderListBox items when the page is navigated to. ResetItemsList(); }
现在,实现采用 XAML 创建的删除按钮的 Click 事件处理程序。列表中的每个计划通知都有一个删除按钮,但所有这些按钮都将使用相同的处理程序。为了确定应该删除哪个通知,该方法检查 Tag 属性,然后调用 ScheduledActionService 的 Remove(String) 方法。请记住,ScheduledNotification 的 Name 属性(以及所有其他 ScheduledAction 类型)唯一标识每个应用程序的对象。删除项后,调用 ResetItemsList 以更新 UI。
private void deleteButton_Click(object sender, RoutedEventArgs e) { // The scheduled action name is stored in the Tag property // of the delete button for each reminder. string name = (string)((Button)sender).Tag; // Call Remove to unregister the scheduled action with the service. ScheduledActionService.Remove(name); // Reset the ReminderListBox items ResetItemsList(); }
MainPage.xaml.cs 的最后更新是为用户点按来添加新警报或提醒的 ApplicationBarIconButton 创建 Click 事件处理程序。使用 Navigate 方法将应用程序导航到将在下一节中实现的 AddNotification.xaml。
private void ApplicationBarAddButton_Click(object sender, EventArgs e) { // Navigate to the AddReminder page when the add button is clicked. NavigationService.Navigate(new Uri("/AddNotification.xaml", UriKind.RelativeOrAbsolute)); }
本演练的下一步是创建一个允许用户添加新警报和提醒的页面。该页面必须能够允许用户选择将启动新警报或提醒的日期和时间。Windows Phone SDK 7.1 不提供用于执行此任务的内置控件,并且从头开始创建控件需要进行大量工作。因此,本示例使用 Silverlight for Windows Phone 工具包中包含的 DatePicker 和 TimePicker 控件。编译此处提供的代码之前,需要安装该工具包。下面的步骤介绍如何在您的应用程序中使用该工具包控件。
从计算机的“开始”菜单,转到“所有程序->Silverlight for Windows Phone->二进制文件”。在打开的 Windows 资源管理器窗口中,复制 Microsoft.Phone.Controls.Toolkit.dll 并将其粘贴到您的应用程序项目目录中。在 Visual Studio 中,从“项目”菜单中,选择“添加引用...”。浏览到您的应用程序项目目录,选择 Microsoft.Phone.Controls.Toolkit.dll,然后单击“确定”。
向您的项目中添加一个新页面。从“项目”菜单中,选择“添加新项...”。选择“Windows Phone 纵向页面”。在“名称”文本框中,键入 AddNotification.xaml。
在“解决方案资源管理器”中,双击 AddNotification.xaml 以将其打开。向根 phone:PhoneApplicationPage 元素中添加一个 XML 命名空间声明,放在其他命名空间声明的旁边。
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
接下来,添加允许用户设置新警报或提醒的属性的控件。将以下代码粘贴到 AddNotification.xaml 中。该代码应该粘贴到命名为“ContentPanel”的 Grid 元素中。
<ScrollViewer> <StackPanel Orientation="Vertical"> <StackPanel Orientation="Horizontal"> <RadioButton Content="Reminder" Name="reminderRadioButton" GroupName="ReminderOrAlarm" IsChecked="True"></RadioButton> <RadioButton Content="Alarm" Name="alarmRadioButton" GroupName="ReminderOrAlarm" ></RadioButton> </StackPanel> <TextBlock Height="30" HorizontalAlignment="Left" Name="titleLabel" Text="title" VerticalAlignment="Top" /> <TextBox Height="72" HorizontalAlignment="Left" Name="titleTextBox" Text="" VerticalAlignment="Top" Width="460" MaxLength="63"/> <TextBlock Height="30" HorizontalAlignment="Left" Name="contentLabel" Text="content" VerticalAlignment="Top" /> <TextBox Height="160" HorizontalAlignment="Left" Name="contentTextBox" Text="" VerticalAlignment="Top" Width="460" TextWrapping="Wrap" MaxLength="256" AcceptsReturn="True" /> <TextBlock Height="30" HorizontalAlignment="Left" Name="beginTimeLabel" Text="begin time" VerticalAlignment="Top" /> <StackPanel Orientation="Horizontal"> <toolkit:DatePicker x:Name="beginDatePicker" Width="220" HorizontalAlignment="Left"></toolkit:DatePicker> <toolkit:TimePicker x:Name="beginTimePicker" Width="220" HorizontalAlignment="Right"></toolkit:TimePicker> </StackPanel> <TextBlock Height="30" HorizontalAlignment="Left" Name="expirationTimeLabel" Text="expiration time" VerticalAlignment="Top" /> <StackPanel Orientation="Horizontal"> <toolkit:DatePicker x:Name="expirationDatePicker" Width="220" HorizontalAlignment="Left"></toolkit:DatePicker> <toolkit:TimePicker x:Name="expirationTimePicker" Width="220" HorizontalAlignment="Right"></toolkit:TimePicker> </StackPanel> <Grid> <RadioButton Content="once" Height="72" HorizontalAlignment="Left" Margin="0,0,0,0" Name="onceRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval" IsChecked="True"/> <RadioButton Content="weekly" Height="72" HorizontalAlignment="Left" Margin="0,70,0,0" Name="weeklyRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval"/> <RadioButton Content="daily" Height="72" HorizontalAlignment="Left" Margin="0,140,0,0" Name="dailyRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval"/> <RadioButton Content="monthly" Height="72" HorizontalAlignment="Left" Margin="240,0,0,0" Name="monthlyRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval"/> <RadioButton Content="end of month" Height="72" HorizontalAlignment="Left" Margin="240,70,0,0" Name="endOfMonthRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval"/> <RadioButton Content="yearly" Height="72" HorizontalAlignment="Left" Margin="240,140,0,0" Name="yearlyRadioButton" VerticalAlignment="Top" GroupName="ScheduleInterval"/> </Grid> <TextBlock Height="30" HorizontalAlignment="Left" Name="param1Label" Text="context parameter 1" VerticalAlignment="Top" /> <TextBox Height="72" HorizontalAlignment="Left" Name="param1TextBox" Text="" VerticalAlignment="Top" Width="440" MaxLength="63"/> <TextBlock Height="30" HorizontalAlignment="Left" Name="param2Label" Text="context parameter 2" VerticalAlignment="Top" /> <TextBox Height="72" HorizontalAlignment="Left" Name="param2TextBox" Text="" VerticalAlignment="Top" Width="440" MaxLength="63"/> </StackPanel> </ScrollViewer>
上一节中的 XAML 代码包含以下控件:
一对 RadioButton 控件,允许用户在添加警报和提醒之间进行切换。
用于 Title 和 Content 属性的 TextBox 控件。在这些控件上设置 MaxLength 属性可将输入限制为属性的字符限制。请注意,Alarm 类不支持 Title 属性,因此创建警报时将不使用该值。
用于 BeginTime 属性的 DatePicker 和 TimePicker 控件。这两个控件一起指定第一次启动通知的时间。这两个控件来自 Silverlight for Windows Phone 工具包,这就是它们使用先前定义的 toolkit 命名空间的原因。
用于 ExpirationTime 属性的 DatePicker 和 TimePicker 控件。该属性确定多久之后将不再启动重现通知。
用于设置 RecurrenceType 属性的 RadioButtons。该属性确定启动通知的间隔。
两个文本框,允许用户设置将附加到 NavigationUri 属性的两个任意参数值。启动提醒并且用户点按提醒 UI 时,将启动应用程序并导航到提供的 URI。该功能允许您向提醒中附加上下文信息。警报不支持该功能。
TextBlock 控件始终用作其他控件的标签。
通过将该代码粘贴到被注释掉的 ApplicationBar 代码上向 XAML 添加 ApplicationBar。这会提供“保存”按钮,单击该按钮时,将尝试从 UI 中的值创建新的计划通知并向系统注册该通知。
双击“解决方案资源管理器”中的 AddNotification.xaml.cs 以将其打开并向文件顶部的 using 指令中添加一个 Scheduler 命名空间的 using 指令。
using Microsoft.Phone.Scheduler;
此页面的所有其他代码都将放置在“保存”按钮的 Click 事件处理程序中。将此空定义粘贴到 AddNotification 类定义中。
private void ApplicationBarSaveButton_Click(object sender, EventArgs e) { // The code in the following steps goes here. }
为新通知生成一个唯一名称。应用程序可以使用通知的任何命名惯例,但对于应用程序中的每个通知,该名称必须唯一。本示例为每个提醒名称生成一个 GUID。将以下代码粘贴到“保存”按钮的 Click 处理程序中。
// Generate a unique name for the new notification. You can choose a // name that is meaningful for your app, or just use a GUID. String name = System.Guid.NewGuid().ToString();
组合 BeginTime 属性的日期和时间值。验证该时间位于当前时间之后,如果不是,则警告用户并退出 Click 处理程序。然后对 ExpirationTime 值执行相同的操作。过期时间必须位于开始时间之后。
// Get the begin time for the notification by combining the DatePicker // value and the TimePicker value. DateTime date = (DateTime)beginDatePicker.Value; DateTime time = (DateTime)beginTimePicker.Value; DateTime beginTime = date + time.TimeOfDay; // Make sure that the begin time has not already passed. if (beginTime < DateTime.Now) { MessageBox.Show("the begin date must be in the future."); return; } // Get the expiration time for the notification. date = (DateTime)expirationDatePicker.Value; time = (DateTime)expirationTimePicker.Value; DateTime expirationTime = date + time.TimeOfDay; // Make sure that the expiration time is after the begin time. if (expirationTime < beginTime) { MessageBox.Show("expiration time must be after the begin time."); return; }
查看是否选中了单选按钮来确定重复间隔。此属性的默认值为 None。
// Determine which recurrence radio button is checked. RecurrenceInterval recurrence = RecurrenceInterval.None; if (dailyRadioButton.IsChecked == true) { recurrence = RecurrenceInterval.Daily; } else if (weeklyRadioButton.IsChecked == true) { recurrence = RecurrenceInterval.Weekly; } else if (monthlyRadioButton.IsChecked == true) { recurrence = RecurrenceInterval.Monthly; } else if (endOfMonthRadioButton.IsChecked == true) { recurrence = RecurrenceInterval.EndOfMonth; } else if (yearlyRadioButton.IsChecked == true) { recurrence = RecurrenceInterval.Yearly; }
生成一个 URI 以设置 NavigationUri 属性。此 URI 的基设置为接下来将创建的 ShowParams.xaml。参数文本框的值作为查询字符串参数添加到 URI。仅当创建提醒时才能使用该值。
// Create a URI for the page that will be launched if the user // taps on the reminder. Use query string parameters to pass // content to the page that is launched. string param1Value = param1TextBox.Text; string param2Value = param2TextBox.Text; string queryString = ""; if (param1Value != "" && param2Value != "") { queryString = "?param1=" + param1Value + "¶m2=" + param2Value; } else if(param1Value != "" || param2Value != "") { queryString = (param1Value!=null) ? "?param1="+param1Value : "?param2="+param2Value; } Uri navigationUri = new Uri("/ShowParams.xaml" + queryString, UriKind.Relative);
查看是否选中了提醒的 RadioButton。如果选中,则创建一个新提醒并设置刚刚从 UI 获取的属性。使用 ScheduledActionService 类的 Add(ScheduledAction) 方法向系统注册新提醒。
if ((bool)reminderRadioButton.IsChecked) { Reminder reminder = new Reminder(name); reminder.Title = titleTextBox.Text; reminder.Content = contentTextBox.Text; reminder.BeginTime = beginTime; reminder.ExpirationTime = expirationTime; reminder.RecurrenceType = recurrence; reminder.NavigationUri = navigationUri; // Register the reminder with the system. ScheduledActionService.Add(reminder); }
如果未选中提醒的 RadioButton,则创建一个警报。与添加提醒的方式相同,但不支持 Title 和 NavigationUri 属性。而且,警报允许您指定要在调用警报时播放的 Sound。通过在 Visual Studio 中选择“项目->添加现有项...”向您的项目中添加一个声音文件。添加声音之后,确保“生成操作”属性设置为“内容”。
else { Alarm alarm = new Alarm(name); alarm.Content = contentTextBox.Text; alarm.Sound = new Uri("/Ringtones/Ring01.wma", UriKind.Relative); alarm.BeginTime = beginTime; alarm.ExpirationTime = expirationTime; alarm.RecurrenceType = recurrence; ScheduledActionService.Add(alarm); }
最后,调用 GoBack()()()() 将用户到返回提醒列表页面。
// Navigate back to the main reminder list page. NavigationService.GoBack();
在上一节中,创建了用来向系统添加新警报和提醒的页面。启动提醒并且用户点按提醒时,会启动在 NavigationUri 属性中指定的页面。在此示例中,创建一个只是显示附加到 URI 的查询字符串参数的页面。
向您的项目中添加一个新页面。从“项目”菜单中,选择“添加新项...”。选择“Windows Phone 纵向页面”。在“名称”文本框中,键入 ShowParams.xaml。
向将用于显示查询字符串参数的 ShowParams.xaml 中添加 TextBlock 控件。该代码应粘贴在名为“ContentPanel”的 Grid 元素中。
<TextBlock Height="30" HorizontalAlignment="Left" Margin="12,10,0,0" Name="param1Label" Text="param1 value:" VerticalAlignment="Top" Foreground="{StaticResource PhoneForegroundBrush}" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,40,0,0" Name="param1TextBlock" Text="TextBlock" VerticalAlignment="Top" Foreground="{StaticResource PhoneAccentBrush}"/> <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,80,0,0" Name="param2Label" Text="param2 value:" VerticalAlignment="Top" Foreground="{StaticResource PhoneForegroundBrush}"/> <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,110,0,0" Name="param2TextBlock" Text="TextBlock" VerticalAlignment="Top" Foreground="{StaticResource PhoneAccentBrush}" />
重写 PhoneApplicationPage 基类的 OnNavigatedTo(NavigationEventArgs) 方法。从提醒启动应用程序时将调用该方法,从而导航到该页面。在此方法中,使用 NavigationContext 类获取查询字符串值并设置 TextBlock 对象的 Text 属性以显示这些值。
// Implement the OnNavigatedTo method and use NavigationContext.QueryString // to get the parameter values passed by the reminder. protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); string param1Value = "" ; string param2Value = ""; NavigationContext.QueryString.TryGetValue("param1", out param1Value); NavigationContext.QueryString.TryGetValue("param2", out param2Value); param1TextBlock.Text = param1Value; param2TextBlock.Text = param2Value; }