❗ There is also a version of this document with code in VB.Net ❗ |
---|
The ActivationService is in charge of handling the applications initialization and activation.
With the method ActivateAsync()
it has one common entry point that is called from the app lifecycle events OnLaunched
, OnActivated
and OnBackgroundActivated
.
For more information on application lifecycle and its events see Windows 10 universal Windows platform (UWP) app lifecycle.
For choosing the concrete type of activation the ActivationService relies on ActivationHandlers, that are registered in the method GetActivationHandlers()
.
Each class in the application that can handle application activation should derive from the abstract class ActivationHandler<T>
(T is the type of ActivationEventArguments the class can handle) and implement the method HandleInternalAsync().
The method HandleInternalAsync()
is where the actual activation takes place.
The virtual method CanHandleInternal()
checks if the incoming activation arguments are of the type the ActivationHandler can manage. It can be overwritten to establish further conditions based on the ActivationEventArguments.
We'll have look at the SchemeActivationHandler, added by DeepLink feature, to see how activation works in detail:
protected override bool CanHandleInternal(ProtocolActivatedEventArgs args)
{
// If your app has multiple handlers of ProtocolActivationEventArgs
// use this method to determine which to use. (possibly checking args.Uri.Scheme)
return true;
}
// By default, this handler expects URIs of the format 'wtsapp:sample?paramName1=paramValue1¶mName2=paramValue2'
protected override async Task HandleInternalAsync(ProtocolActivatedEventArgs args)
{
// Create data from activation Uri in ProtocolActivatedEventArgs
var data = new SchemeActivationData(args.Uri);
if (data.IsValid)
{
NavigationService.Navigate(data.PageType, data.Parameters);
}
await Task.CompletedTask;
}
The CanHandleInternal()
method was overwritten here and it returns true by default, devs can use args to add extra validations in scenarios with multiple ProtocolActivationEventArgs.
The HandleInternalAsync()
method gets the ActivationData from argument's Uri and uses the PageType and Parameters to navigate.
The following flowchart shows the Activation process that starts with one of the app lifecycle event and ends with the StartupAsync call.
Activation starts from one of the app lifecycle events: OnLaunched
, OnActivated
or OnBackgroundActivated
. All call a common entry point for activation in ActivationService.ActivateAsync().
The first calls in ActivateAsync are InitializeAsync() and ShellCreation (in case you activation is interactive). If you added an Identity feature to your app, code for Identity configuration and SilentLogin will be included here too.
After this first block, HandleActivation is called (more details below).
IsInteractive
Interacting with the app window and navigating is only available when the activation arguments extend from IActivatedEventArgs. An example for non-interactive activation is activation from a background Task.
InitializeAsync
InitializeAsync contains services initialization for services that are going to be used as ActivationHandler. This method is called before the window is activated. Only code that needs to be executed before app activation should be placed here, as the splash screen is shown while this code is executed.
StartupAsync
StartupAsync contains initializations of other classes that do not need to happen before app activation and starts processes that will be run after the Window is activated.
The HandleActivation method gets the first ActivationHandler that can handle the arguments of the current activation. It also creates a DefaultActivationHandler and execute if it necesary (when no one ActivationHandler was selected or the selected ActivationHandler does not realize a Navigation).
We are going to create a new ActivationHandler to understand how to extend the ActivationService in our project. In this case, we are going to add a markdown files (.md) reader to our app.
The following sample code is thought to be added in a WinTS MVVM Basic app.
For viewing the markdown a MarkdownTextBlock from the Windows Community Toolkit is used.
Add the following files to your project
Views/MarkdownPage.xaml
<Page
x:Class="YourAppName.Views.MarkdownPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">
<Grid
x:Name="ContentArea">
<Grid.RowDefinitions>
<RowDefinition x:Name="TitleRow" Height="48"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
x:Name="TitlePage"
x:Uid="Markdown_Title"
FontSize="28" FontWeight="SemiLight"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" VerticalAlignment="Center"
Margin="24,0,24,7"/>
<Grid Grid.Row="1" >
<ScrollViewer
Margin="12"
BorderBrush="{ThemeResource AppBarBorderThemeBrush}"
BorderThickness="2"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Visible">
<controls:MarkdownTextBlock
x:Name="UiMarkdownText"
Padding="24,12,24,12"
Foreground="Black"
Background="White"
LinkClicked="UiMarkdownText_LinkClicked"
Text="{x:Bind ViewModel.Text, Mode=TwoWay}" />
</ScrollViewer>
</Grid>
</Grid>
</Page>
Views/MarkdownPage.xaml.cs
using System;
using Microsoft.Toolkit.Uwp.UI.Controls;
using Windows.Storage;
using Windows.System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using YourAppName.ViewModels;
namespace YourAppName.Views
{
public sealed partial class MarkdownPage : Page
{
public MarkdownViewModel ViewModel { get; } = new MarkdownViewModel();
public MarkdownPage()
{
InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (!string.IsNullOrEmpty(e.Parameter?.ToString()))
{
var text = await FileIO.ReadTextAsync(e.Parameter as StorageFile);
ViewModel.Text = text;
}
}
private async void UiMarkdownText_LinkClicked(object sender, LinkClickedEventArgs e)
{
await Launcher.LaunchUriAsync(new Uri(e.Link));
}
}
}
ViewModels/MarkdownViewModel.cs
using System;
using YourAppName.Helpers;
namespace YourAppName.ViewModels
{
public class MarkdownViewModel : Observable
{
private string _text;
public string Text
{
get { return _text; }
set { Set(ref _text, value); }
}
public MarkdownViewModel()
{
}
}
}
Strings/en-us/Resources.resw
You also should add a string resource for Markdown Title.
<data name="Markdown_Title.Text" xml:space="preserve">
<value>Markdown</value>
</data>
Add a file type association declaration in the application manifest, to allow the App to be shown as a default handler for markdown files.
Handle the file activation event by implementing the override of OnFileActivated:
App.xaml.cs
protected override async void OnFileActivated(FileActivatedEventArgs args)
{
await ActivationService.ActivateAsync(args);
}
Add a FileAssociationService to your project that handles activation from files. It derives from ApplicationHandler<T>
.
As it manages activation by FileActivatedEventArgs the signature would be:
FileAssociationService
internal class FileAssociationService : ActivationHandler<FileActivatedEventArgs>
{
}
Override HandleInternalAsync()
, to evaluate the event args, and take action:
protected override async Task HandleInternalAsync(FileActivatedEventArgs args)
{
var file = args.Files.FirstOrDefault();
NavigationService.Navigate(typeof(MarkdownPage), file);
await Task.CompletedTask;
}
Last but not least, we'll have to add our new FileAssociationService to the ActivationHandlers registered in the ActivationService:
ActivationService
private IEnumerable<ActivationHandler> GetActivationHandlers()
{
// Add this new FileAssociationService
yield return Singleton<FileAssociationService>.Instance;
}