Windows 8 Apps: Working with Toast Notifications

With the advent of always-connected smart devices like phones and tablets, users have become increasingly accustomed to receiving notifications for changes as opposed to checking for changes (aka Push instead of Pull). For example, if a professional is using a tablet application to keep track of appointments, they would rather want to be notified when their appointments change instead of repeatedly looking for changes.

To notify users, Windows 8 Store apps provides a Toast Notification framework that can be used for showing notifications. The notifications can come in via two modes, Push Notifications implemented through WNS or through traditional pull mechanisms, in which application takes care of polling a server to get notifications. Today we will use the second type and see how we can leverage the built in Notification framework to let users know that a change has occurred.

A Use Case for Toast Notifications

The class ‘ToastNotification’ is used to define metadata associated with the toast notification e.g. its contents, expiration etc. The default time for the notification is set to 7 seconds.

A toast notification is a message to the user that contains time-sensitive information. This provides quick access to related content in an app. It can appear even if another app is running. The toast notification can contain Text, Images. Toast notification must be used to show very important notification for the user.


In the code here, WCF service is used, which makes call to Database server. This service is consumed by Windows Store App, which makes periodic call to WCF service. The application shows the toast notification when the record is added in the table on database server and when it is available to Windows Store app using WCF Service Call.

Building the Sample

Let’s start with the database. The database script creates the Company DB and sets up the required schema.

Create database Company
GO
USE [Company]
GO

/****** Object:  Table [dbo].[EmployeeInfo]    Script Date: 8/21/2012 3:57:12 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[EmployeeInfo](
    [EmpNo] [int] IDENTITY(1,1) NOT NULL,
    [EmpName] [varchar](50) NOT NULL,
    [Salary] [decimal](18, 0) NOT NULL,
    [DeptName] [varchar](50) NOT NULL,
    [Designation] [varchar](50) NOT NULL,
CONSTRAINT [PK_EmployeeInfo] PRIMARY KEY CLUSTERED
(
    [EmpNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO

Step 1: Open VS 2012 and create a new solution, name it as ‘Store_CS_ToastNotification’. In this solution, add a new WCF Service application, name it as ‘WCF_SampleService’.

Step 2: In the WCF service, add a new ADO.NET EF. Name it as CompanyEDMX. In the Wizard, select EmployeeInfo table. After completing the wizard, the result will be as below:

employee-table

Step 3: Define the below operations in IServic1.cs

[ServiceContract]
public interface IService1
{
[OperationContract]
EmployeeInfo[] GetEmployees();
}


Step 4: Implement the above interface in the Service1 class as below:
public class Service1 : IService1
{
CompanyEntities objContext;
public Service1()
{
  objContext = new CompanyEntities();
}

public EmployeeInfo[] GetEmployees()
{
  return objContext.EmployeeInfoes.ToArray();
}
}


Build the service.

Note: Instead of WCF service you could build an Http WebAPI service too.

Step 5: In the solution, add a new Windows Store App and name it as ‘Store_CS_ToastNotification’. In this project, add a WCF service reference, name it as ‘MyRef’.

Step 6: In the MainPage.Xaml add the below Xaml in the Page Resources:

<Page.Resources>
<!--Style for displaying Data in the TextBlock in GridView-->
<Style TargetType="TextBlock" x:Key="txtstyle">
  <Setter Property="Width" Value="75"></Setter>
  <Setter Property="FontFamily" Value="Times New Roman"></Setter>
  <Setter Property="FontSize" Value="18"></Setter>
  <Setter Property="Width" Value="100"></Setter>
  <Setter Property="Foreground" Value="Red"></Setter>
</Style>
<!--Ends Here-->
<!--DataTemplate for Displaying Data in the GridView-->
<DataTemplate x:Key="EmpTemplate">
  <Grid Width="200" Height="100"   Background="Aqua">
   <Grid.ColumnDefinitions>
    <ColumnDefinition Width="100"></ColumnDefinition>
    <ColumnDefinition Width="100"></ColumnDefinition>
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
    <RowDefinition Height="20"></RowDefinition>
    <RowDefinition Height="20"></RowDefinition>
    <RowDefinition Height="20"></RowDefinition>
    <RowDefinition Height="20"></RowDefinition>
    <RowDefinition Height="20"></RowDefinition>
   </Grid.RowDefinitions>
   <TextBlock Grid.Column="0" Grid.Row="0" Text="EmpNo"  Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding EmpNo}" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="0" Grid.Row="1" Text="EmpName" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding EmpName}" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="0" Grid.Row="2" Text="Salary" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Salary}" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="0" Grid.Row="3" Text="DeptName" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="1" Grid.Row="3" Text="{Binding DeptName}" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="0" Grid.Row="4" Text="Designation" Style="{StaticResource txtstyle}"></TextBlock>
   <TextBlock Grid.Column="1" Grid.Row="4" Text="{Binding Designation}" Style="{StaticResource txtstyle}"></TextBlock>
  </Grid>
</DataTemplate>
<!--Ends Here-->

<!--Items Panel for displaying Rows and Columns in the GridView -->
<ItemsPanelTemplate x:Key="GridViewItemsPanel">
  <WrapGrid MaximumRowsOrColumns="5"/>
</ItemsPanelTemplate>
<!--Ends Here-->
</Page.Resources>


Step 7: Add the following Xaml Code in the MainPage.Xaml. This has Button and GridView as shown below:

<Button x:Name="btncreatetoast" Content="Get Employees"
        Margin="10,10,0,0" VerticalAlignment="Top" Width="723"
        Click="btncreatetoast_click"
        Grid.Row="0"/>
       
<GridView x:Name="gvEmployees"
          ItemTemplate="{StaticResource EmpTemplate}" Margin="0,10,0,0"
          ItemsPanel="{StaticResource GridViewItemsPanel}" Grid.Row="1"
          BorderBrush="Red" BorderThickness="2"
          ScrollViewer.VerticalScrollBarVisibility="Visible"
          ScrollViewer.HorizontalScrollBarVisibility="Visible" 
          Height="690">
</GridView>


Step 7: Define the following objects at MainPage class in MainPage.xaml.cs:

MyRef.Service1Client Proxy;
ObservableCollection<MyRef.EmployeeInfo> Employees;

public MainPage()
{
this.InitializeComponent();
Proxy = new MyRef.Service1Client();
}

The proxy is instantiated in the constructor.

Step 8: From the MainPage.xaml, register for the loaded event from <page> tag. In the code behind, you will have the loaded event as below:

/// <summary>
/// On the loaded event which makes class to WCF service
/// and display data in the GridView
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Page_Loaded_1(object sender, RoutedEventArgs e)
{
Employees = await Proxy.GetEmployeesAsync();
gvEmployees.ItemsSource = Employees;
}


Please read comments applied on the method. The method makes call to WCF service and displays received data in the GridView of name gvEmployees.

Step 9: Write the following code in the click event of the ‘Get Employees’:

/// <summary>
/// This will Start the Dispatcher Timer and will make
/// call to WCF service after each 20 Seconds.
/// The Data will be synchronized after each 20 Seconds
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btncreatetoast_click (object sender, RoutedEventArgs e)
{
DispatcherTimer tim = new DispatcherTimer();
tim.Interval = new TimeSpan(0,0,20);
tim.Tick += tim_Tick;
tim.Start();
}

/// <summary>
/// The Tick Event whihc makes call to WCF service after each 20 seconds
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
async void tim_Tick(object sender, object e)
{
ObservableCollection<MyRef.EmployeeInfo> NewRecord = await Proxy.GetEmployeesAsync();
//If the newly received data is lesser than the Newly received data from the WCF service
//the show the toast notification
if (Employees.Count < NewRecord.Count || Employees!=NewRecord)
{
  var Emp = NewRecord.LastOrDefault();
  //The ToastNotificationManager object used to raise toast notification
  XmlDocument xtempl = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
  XmlNodeList text = xtempl.GetElementsByTagName("text");
                text[0].AppendChild(xtempl.CreateTextNode("The new Employee with EmpName as " + Emp.EmpName + " is added") );

  //ToastNotification defines the metadata for the ToastNotification
  ToastNotification toast = new ToastNotification(xtempl);
  ToastNotificationManager.CreateToastNotifier().Show(toast);
}
Employees = NewRecord;
gvEmployees.ItemsSource = Employees;
}

The above code in the button click event creates an object of ‘DispatcherTimer’. This object sets an interval for 20 seconds and raises the Tick event. In the events, the call to WCF service is made. if the newly received data is greater than the data received in loaded event, then the Toast notification is displayed. The class ToastNotificationManager creates the ToastNotifier object which is used to raise Toast Notification. The GetTemplateContent accepts ToastTemplateType. The ToastTemplateType is an enumeration, this defines predefined templates for toast notification. The details about ToastTemplate can be found over here. Along with the toast notification, the GridView will show the refreshed data.

Step 10: To enable the application for Toast Notification capabilities, double click on the manifest file and select ‘Application UI’ and click on Badge Logo and select Toast Capable as ‘Yes’. The image is as shown below:

toast-notification

Step 11: Run the application and the result will be similar to the following:

get-employees

Click on the ‘Get Employees’ button and add the Record in the EmployeeInfo table in SQL Server. After about 20 seconds, you will get a toast notification as shown below:

updated-employee

Conclusion

We saw a simple example of how toast notifications can be used to provide non-intrusive yet useful updates for an application. Another recommended use case for Toast Notifications is from background processes that watch for changes and create notification Toasts accordingly. When a background process is implemented, notifications are delivered even when the frontend application is not the running e.g. the Mail app in Windows 8.

The sample code can be downloaded from GitHub here https://github.com/devcurry/winrt-toast-notifications




About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

1 comment:

Anonymous said...

How would you edit the above to run as a background task when the app is running/suspended in the background?

I've been looking for a solution like this that doesn't require WNS so thanks for the information so far