Universal app – Windows 10 Action Center Toast Notification

Action Center is a new feature of Windows 10. It’s a collapsible panel on the right of the monitor where the software can notify his user about his status. It works exactly same as the notification of apps in smartphone. User can click on the notification to go directly to the apps. In this blog spot I will make a short demo showing how we can display a notification in Action Center. The demo contains a very simple email client working with IMAP server and notifies the user how many unread emails there are in his inbox. This email client works as an universal app.

1. Universal app

1.1 Simple toast

The image below shows how our demo looks like. The layout is designed with XAML which is very familiar if you already have experience with WPF development.

Universal Email Client

The Interval (in seconds) is the time range we set to tell the app how often he should connect to server and check for unread emails. The next option Toast template defines how we would like to compose a toast. A toast can be composed in 2 ways: either with C# code or with XML Template. In code listing below, we made a ToastContent from a ToastVisual with a header line and a body line. The Launch action will be triggered when user clicks on the body of the toast.

private async void NotifyActionCenter()
{
	XmlDocument toastXml = null;
	if (SelectedToastOption.Type == ToastOptionType.Code)
	{
		ToastVisual toastVisual = new ToastVisual()
		{
			TitleText = new ToastText() {Text = "Unread email"},
			BodyTextLine1 = new ToastText() {Text = string.Format("There are {0} unread mails", Unread)}
		};

		ToastContent toastContent = new ToastContent()
		{
			Visual = toastVisual,
			Launch = new QueryString()
			{
				{ConstantValues.ToastAction, ConstantValues.ToastActionCheckMail}
			}.ToString()
		};
		toastXml = toastContent.GetXml();
	}
	else
	{
		string template = await ResourceUtil.GetText("Windows_10_Action_Center.XmlToastTemplate.xml");
		XmlUtil xmlUtil = new XmlUtil(template);
		xmlUtil.SetValue("visual.binding.text","Unread email");
		xmlUtil.SetValue("visual.binding.text[1]", string.Format("There are {0} unread mails", Unread));
		xmlUtil.Document.Root.SetAttributeValue("launch", "ToastAction=CheckMail");
		toastXml = xmlUtil.XmlDocument;

	}

	ToastNotification toastNotification = new ToastNotification(toastXml)
	{
		ExpirationTime = DateTime.Now.AddMinutes(5)
	};

	ToastNotificationManager.CreateToastNotifier().Show(toastNotification);
}

The Launch action will be caught in App.xaml.cs class. When this action was fired, I’ll broadcast an event through EventAggregator so that the app can handle this action as he wants

private void OnLaunchedOrActivated(IActivatedEventArgs e)
{
	if (e.PreviousExecutionState == ApplicationExecutionState.Running)
	{
		var eventArgs = e as ToastNotificationActivatedEventArgs;
		if (eventArgs != null)
		{
			QueryString args = QueryString.Parse(eventArgs.Argument);

			switch (args[ConstantValues.ToastAction])
			{
				case ConstantValues.ToastActionCheckMail:
					IEventAggregator eventAggregator = container.GetInstance<IEventAggregator>();
					eventAggregator.PublishOnBackgroundThread(ConstantValues.ToastActionCheckMail);
					break;
			}
		}

		Window.Current.Activate();
		return;

	}
	DisplayRootViewFor<MainViewModel>();
}

The MainViewModel inherits Screen and implements IHandle so that ViewModel can receive notification from EventAggregator and enhance data binding.

internal class MainViewModel : Screen, IHandle<string>
{  
	public MainViewModel(IEventAggregator eventAggregator)
	{
		Email = "4nh7i3m@gmail.com";
		Password = "eFutXMF8";
		ImapServer = "imap.googlemail.com";
		Port = 993;
		Interval = 30;
		WatchText = "Start watching!";
		Timer = new DispatcherTimer();
	   
		eventAggregator.Subscribe(this);
	}
	
	...
}

1.2 Caliburn.Micro

I like MVVM pattern in WPF. Therefore for universal app I also use Caliburn.Micro for enhancing the data binding. Not like in WPF app, in universal app we can’t create a bootstrapper for entire application. We have to rewrite the App.xaml.cs class and inject Caliburn.Micro there.

The code listing below show a very simple example of injecting Caliburn.Micro into universal app.

public sealed partial class App
{
	private WinRTContainer container;

	public App()
	{
		InitializeComponent();
	}

	protected override void Configure()
	{
		container = new WinRTContainer();
		container.RegisterWinRTServices();

		//TODO: Register your view models at the container
		container.PerRequest<MainViewModel>();
	}

	protected override object GetInstance(Type service, string key)
	{
		var instance = container.GetInstance(service, key);
		if (instance != null)
			return instance;
		throw new Exception("Could not locate any instances.");
	}

	protected override IEnumerable<object> GetAllInstances(Type service)
	{
		return container.GetAllInstances(service);
	}

	protected override void BuildUp(object instance)
	{
		container.BuildUp(instance);
	}

	protected override void PrepareViewFirst(Frame rootFrame)
	{
		container.RegisterNavigationService(rootFrame);
	}

	protected override void OnActivated(IActivatedEventArgs args)
	{
		OnLaunchedOrActivated(args);
	}

	protected override void OnLaunched(LaunchActivatedEventArgs args)
	{
		OnLaunchedOrActivated(args);
	}

	private void OnLaunchedOrActivated(IActivatedEventArgs e)
	{
		if (e.PreviousExecutionState == ApplicationExecutionState.Running)
		{
			var eventArgs = e as ToastNotificationActivatedEventArgs;
			if (eventArgs != null)
			{
				QueryString args = QueryString.Parse(eventArgs.Argument);

				switch (args[ConstantValues.ToastAction])
				{
					case ConstantValues.ToastActionCheckMail:
						IEventAggregator eventAggregator = container.GetInstance<IEventAggregator>();
						eventAggregator.PublishOnBackgroundThread(ConstantValues.ToastActionCheckMail);
						break;
				}
			}

			Window.Current.Activate();
			return;

		}
		DisplayRootViewFor<MainViewModel>();
	}


  
}

In App.xaml we have to use CaliburnApplication instead of the default one.

<caliburn:CaliburnApplication x:Class="Windows_10_Action_Center.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows_10_Action_Center" RequestedTheme="Light" xmlns:caliburn="using:Caliburn.Micro" >
</caliburn:CaliburnApplication>

The DisplayRootViewFor from code listing above is took from base class CaliburnApplication.

2. References

In our demo I just make a very simple toast layout. There are many other template layouts in Microsoft library and the layout is very easy to customize. The reference links below will help us to discover more about toast notification in Windows 10.

The toast template catalog
Toast Notification and Action Center Overview for Windows 10
Adaptive and interactive toast notifications for Windows 10

3. Source code

https://bitbucket.org/hintdesk/dotnet-windows-10-action-center-toast-notification

Leave a Reply

Your email address will not be published. Required fields are marked *