Showing posts with label UWP. Show all posts
Showing posts with label UWP. Show all posts

Friday, 21 July 2017

Toasts 03 Background

So as we all know UWP apps just like there predecessors only run when they are in the foreground, this is to ensure performance of the device, preserve battery-life, etc... not sure what else that's why i put etc. Anyway we do have a way to run some logic in the background, now a caveat is that there is no guarantee that you're background task will ever fire, if the user has put their device into quite hours or the system is constrained for resources etc...

to start we have to add new winRT component project to our solution


so our solution explorer should look like

now that we have our two project ready let's open up the Class1.cs file in the newly added pc.background-taskExample project.

Now let's change it from Class1 to something a little more descriptive let's rename Class1 to LaunchToastBackgroundTask.

We are also going to implement the IBackgroundTask interface, which only implements one method; and it's Run, this is the method that will asynchronously fire. I mention Asynchronously because we're going to have to grab a deferral to keep our Background task from falling out of scope before we fire our code.

using Windows.ApplicationModel.Background;

namespace pc_backgroundTaskExample
{
    public sealed class LaunchToastBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var deferral = taskInstance.GetDeferral();

            //Our Logic goes here

            deferral.Complete();
        }
    }
}


Now let's create the logic to fire a toast notification from our background task

using Windows.ApplicationModel.Background;
using Windows.UI.Notifications;

namespace pc_backgroundTaskExample
{
    public sealed class LaunchToastBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var deferral = taskInstance.GetDeferral();

            var template = ToastTemplateType.ToastText02;
            var xmlDoc = ToastNotificationManager.GetTemplateContent(template);
            var xml = xmlDoc.GetXml();
            /*  <toast>
                    <visual>
                        <binding template="ToastText02">
                            <text id="1"></text>
                            <text id="2"></text>
                        </binding>
                    </visual>
                </toast>*/

            var textElements = xmlDoc.GetElementsByTagName("text");
            textElements[0].AppendChild(xmlDoc.CreateTextNode("Title text"));
            textElements[1].AppendChild(xmlDoc.CreateTextNode("body text"));

            var toast = new ToastNotification(xmlDoc);

            var notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(toast);

            deferral.Complete();
        }
    }
}


just as before

next let's move to our application, the first thing we have to do is enable background tasks in the app manifest.

once that's done let's go to our main application and write the logic of when to fire our background task.

using pc_backgroundTaskExample;
using System;
using System.Linq;
using Windows.ApplicationModel.Background;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace pc.toastExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage() { this.InitializeComponent(); }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var allowed = await BackgroundExecutionManager.RequestAccessAsync();

            switch (allowed)
            {
                case BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity:
                case BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity:
                    var existing = BackgroundTaskRegistration.AllTasks
                        .FirstOrDefault(t =>
                            t.Value.Name == nameof(LaunchToastBackgroundTask)).Value;

                    if (existing != null)
                        existing.Unregister(false);

                    var builder = new BackgroundTaskBuilder();
                    builder.Name = nameof(LaunchToastBackgroundTask);
                    builder.TaskEntryPoint = typeof(LaunchToastBackgroundTask).ToString();
                    builder.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false));

                    var taskRegistration = builder.Register();

                    break;
                case BackgroundAccessStatus.Denied:
                    //denied
                    break;
                case BackgroundAccessStatus.Unspecified:
                    //cancelled
                    break;
            }
        }
    }
}


now our background task will fire when our device receives an internet connection.

Thursday, 20 July 2017

Toasts 02 Scheduled

Now we are going to create a toast using our ScheduledToastNotification class, firstly we'll use our award winning UI from our previous toast post.

<Page
    x:Class="pc.toastExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:pc.toastExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Click="Button_Click">Send toast</Button>
        </StackPanel>
    </Grid>
</Page>

It really is a minimalist piece...

Let's take a look at our code behind

using System;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace pc.toastExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage() { this.InitializeComponent(); }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var template = ToastTemplateType.ToastText02;
            var xmlDoc = ToastNotificationManager.GetTemplateContent(template);
            var xml = xmlDoc.GetXml();
            /*  <toast>
                    <visual>
                        <binding template="ToastText02">
                            <text id="1"></text>
                            <text id="2"></text>
                        </binding>
                    </visual>
                </toast>*/

            var textElements = xmlDoc.GetElementsByTagName("text");
            textElements[0].AppendChild(xmlDoc.CreateTextNode("Title text"));
            textElements[1].AppendChild(xmlDoc.CreateTextNode("body text"));
           
            var time = DateTimeOffset.Now.AddSeconds(5);
            var toast = new ScheduledToastNotification(xmlDoc, time);
           
            var notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.AddToSchedule(toast);
        }
    }
}


pretty straight forward, there's no events that we can tie into so this really is just a way to notify our user of some piece of information at a specific point in time

now let's say we scheduled some toasts but now we want to remove them. well add another button to our UI and add the following to the click event.

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    var notifier = ToastNotificationManager.CreateToastNotifier();
    var toasts = notifier.GetScheduledToastNotifications();

    foreach (var t in toasts)
        notifier.RemoveFromSchedule(t);

}

simple as that.

Wednesday, 19 July 2017

Toasts 01 Local while running

toasts are the little message boxes that pop-up in the left hand coroner of your windows 10, there is three ways to activate them

  • Scheduled: set using the "ScheduledToastNotification" class, it can be a one off or a recurring toast.
  • Local: can be fired from the application either while running or from a background task.
  • Push: from a push service in the cloud.

now a simple local toast that can be fired while our application is running can be accomplished as follows.

<Page
    x:Class="pc.toastExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:pc.toastExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Click="Button_Click">Send toast</Button>
        </StackPanel>
    </Grid>
</Page>

and the codebehind

using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace pc.toastExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage() { this.InitializeComponent(); }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var template = ToastTemplateType.ToastText02;
            var xmlDoc = ToastNotificationManager.GetTemplateContent(template);
            var xml = xmlDoc.GetXml();
            /*  <toast>
                    <visual>
                        <binding template="ToastText02">
                            <text id="1"></text>
                            <text id="2"></text>
                        </binding>
                    </visual>
                </toast>*/

            var textElements = xmlDoc.GetElementsByTagName("text");
            textElements[0].AppendChild(xmlDoc.CreateTextNode("Title text"));
            textElements[1].AppendChild(xmlDoc.CreateTextNode("body text"));

            var toast = new ToastNotification(xmlDoc);
            var notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(toast);
        }
    }
}


fairly simple, with the only use being able to notify the user of some piece of information. no real interactivity. lucky we can add some event recievers to our toastNotification object.

using Windows.UI.Notifications;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System;

namespace pc.toastExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage() { this.InitializeComponent(); }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var template = ToastTemplateType.ToastText02;
            var xmlDoc = ToastNotificationManager.GetTemplateContent(template);
            var xml = xmlDoc.GetXml();
            /*  <toast>
                    <visual>
                        <binding template="ToastText02">
                            <text id="1"></text>
                            <text id="2"></text>
                        </binding>
                    </visual>
                </toast>*/

            var textElements = xmlDoc.GetElementsByTagName("text");
            textElements[0].AppendChild(xmlDoc.CreateTextNode("Title text"));
            textElements[1].AppendChild(xmlDoc.CreateTextNode("body text"));

            var toast = new ToastNotification(xmlDoc);
            toast.Activated += Toast_Activated;
            toast.Dismissed += Toast_Dismissed;

            //if user disables notifications in general or for your application
            //int the action ceter, then the failed event will fire
            toast.Failed += Toast_Failed;

            var notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(toast);
        }

        private async void Toast_Failed(ToastNotification sender, ToastFailedEventArgs args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("failed toast").ShowAsync());
        }

        private async void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("dismissed toast").ShowAsync());
        }

        private async void Toast_Activated(ToastNotification sender, object args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("Activated toast").ShowAsync());
        }
    }
}


now we've added three events for toast interaction:
  • Activated: will fire if the user clicks on the toast
  • Dismissed: will fire if the user clicks the [x] to hide the toast
  • Failed: will fire if the user blocks all or just our app from firing toast notifications.
one final word of warning since we subscribed to events, if we navigate away from the page we should really unsubscribe from them as well. To do that successfully you probably do not want to create your toastNotification object locally in your button, because each time you click on it it'll create a new instance that'll sit in memory and not be collected by the GC. you'd want to create the ToastNotification object at the page or viewmodel level and then unsubscribe the events when you navigate away from the page or the object is no longer needed.

something along the lines of


using System;

using Windows.UI.Notifications;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace pc.toastExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage() { this.InitializeComponent(); }

        ToastNotification toast;

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var template = ToastTemplateType.ToastText02;
            var xmlDoc = ToastNotificationManager.GetTemplateContent(template);
            var xml = xmlDoc.GetXml();
            /*  <toast>
                    <visual>
                        <binding template="ToastText02">
                            <text id="1"></text>
                            <text id="2"></text>
                        </binding>
                    </visual>
                </toast>*/

            var textElements = xmlDoc.GetElementsByTagName("text");
            textElements[0].AppendChild(xmlDoc.CreateTextNode("Title text"));
            textElements[1].AppendChild(xmlDoc.CreateTextNode("body text"));

            //use only one instance of toast
            if (toast == null)
            {
                toast = new ToastNotification(xmlDoc);

                toast.Activated += Toast_Activated;
                toast.Dismissed += Toast_Dismissed;

                //if user disables notifications in general or for your application
                //int the action ceter, then the failed event will fire
                toast.Failed += Toast_Failed;
            }

            var notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(toast);
        }

        private async void Toast_Failed(ToastNotification sender, ToastFailedEventArgs args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("failed toast").ShowAsync());
        }

        private async void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("dismissed toast").ShowAsync());
        }

        private async void Toast_Activated(ToastNotification sender, object args)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal,
                async () => await new MessageDialog("Activated toast").ShowAsync());
        }

        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            base.OnNavigatingFrom(e);

            //unsubscribe events when navigating away from page.
            if(toast != null)
            {
                toast.Activated -= Toast_Activated;
                toast.Dismissed -= Toast_Dismissed;
                toast.Failed -= Toast_Failed;
                toast = null;
            }
        }
    }
}

now you see how we declared our toast field at the class level and we test to make sure it's null before we instantiate and attach our event handlers, this ensures that we only have one instance of it in memory and when we navigate away from our page we unsubscribe our event handlers letting the Garbage collector take care of our toast variable