Advanced UWP: Communicating with Apps

The following excerpt is from Advanced UWP Part 2: Communicating with Windows 10 and Other Apps, the second in a five-part series written by Succinctly series author Matteo Pagani. To download the complete white paper, and other papers in the series, visit the White Paper section of Syncfusion’s Technology Resource Portal.


The Universal Windows Platform offers many ways to allow an application to communicate with the operating system or with other apps. One of these mechanisms is contracts, and they’re used to connect two applications so that they can exchange data. There are two kinds of contracts:

  • A source contract is implemented by applications that want to share some data.
  • A target contract is implemented by applications that want to receive shared data.

The most important feature of contracts is that they are completely transparent both to the user and the developer. The source application doesn’t have to know anything about the target one that will receive the data; in the same way, the target application can receive the shared data without knowing anything about who sent it. What’s important is only the kind of content that the application can share or receive. It will be up to Windows to connect the dots and put the two applications in communication.

The sharing contract

The sharing contract is used to share different kinds of data (text, images, files, etc.) between two applications. The following image shows how the sharing contract works:


Figure 1: The sharing contract’s architecture.

The source app registers itself to be notified every time the user chooses to share some data. Inside this event handler, we’re going to define the content to share. Then, it’s time for the operating system to take care of the operation. It will display a list of all the applications that have registered for the share target contract and that can support the data type we want to share. The last step is to perform the real sharing operation, which is performed by the target app. It will receive the shared data and it will take care of processing it. In the rest of this section, we’ll see in detail how to implement both the source and target sharing contracts.

Activating the share

Starting the sharing operation is easy. You just have to call the ShowShareUI() method of the DataTransferManager class (part of the Windows.ApplicationModel.DataTransfer namespace), which is the main class we’re going to use to interact with the sharing contract.

private void OnShareItemClicked(object sender, RoutedEventArgs e)

{

    DataTransferManager.ShowShareUI();

}





Sharing content

The sharing operation is performed at page level, since every page can share a different kind of content. For this reason, each page has its own instance of the DataTransferManager class, which is retrieved using the GetForCurrentView() method. When your application calls the ShowShareUI() method, Windows will invoke an event exposed by the DataTransferManager class called DataRequest. It’s inside this event handler that we’re going to define the content we want to share, like in the following sample:

public sealed partial class MainPage : Page

{

    public MainPage()

    {

        this.InitializeComponent();

        DataTransferManager transferManager = DataTransferManager.GetForCurrentView();

        transferManager.DataRequested += transferManager_DataRequested;

    }

 

    private void transferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)

    {

        //handle the sharing operation

    }

}













The event handler contains a parameter of type DataRequestedEventArgs. It’s the core of our sharing operation, since it allows all the properties of the request to be defined with the Request.Data object. Regardless of the type of content we want to share, there are two properties inside the Properties object that are always required: Title and Description.

Based on the target app, these two properties are used to define the context of the sharing operation.

private void transferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs

    args)

{

    args.Request.Data.Properties.Title = "Sharing demo";

    args.Request.Data.Properties.Description = "This is a sharing demo";

}








For example, the previous code executed on a device with the traditional Windows 10 version (like a desktop or a tablet) will display the title and the description right at the top of the share panel before the list of the available target apps, as you can see from the following image:

Figure 2: The sharing panel in the desktop version of Windows 10 Creators Update.

After you’ve defined the title and description, it’s time to define the real content. The Request.Data object offers many methods that begin with the Set prefix; each of them represents one of the data types you can share. Let’s see some samples of the most common ones.

Sharing a text

Text can be shared with the SetText() method, which simply accepts the string to share, like in the following sample:

private void transferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)args)

{

    args.Request.Data.Properties.Title = "Sharing demo";

    args.Request.Data.Properties.Description = "This is a sharing demo";

    args.Request.Data.SetText("This is a demo text");

}








Sharing a link

A link is simply a string. However, some applications can manage them in a special way. For example, the built-in Mail application in Windows 10 can automatically generate a preview image of a website to embed inside an email. The method used to share a link is called SetWebLink() and it simply requires as parameter an Uri object with the address, like in the following sample:

void transferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)

{

    args.Request.Data.Properties.Title = "Sharing demo";

    args.Request.Data.Properties.Description = "This is a link to share";

    Uri uri = new Uri("http://blog.qmatteoq.com", UriKind.Absolute);

    args.Request.Data.SetWebLink(url);

}









Sharing a file

Sharing a file is one of the most frequently used features, since all the complex data (like images) are treated as files by the operating system.

  • Note: The Request.Data object offers a specific method for sharing images, called SetBitmap(). However, it isn’t best practice to use it, since most apps aren’t able to handle it, and they treat images as regular files. Thus, we won’t discuss this method further here.

However, before showing you how to share a file in an application, there’s an important concept to introduce: asynchronous operations. If you read the UWP Succinctly volumes, there was a chapter dedicated to handling an application’s storage, and you’ll know that all the storage APIs are asynchronous and are based on the async and await pattern. Consequently, in a sharing operation, the operating system won’t be able to determine when the files are ready and the sharing contract can be activated.

To avoid this issue, we’re going to use the technique offered by the Universal Windows Platform of using a deferral object. It’s of type DataRequestDeferral and it’s provided by the GetDeferral() method exposed by the Request object. After getting a reference to this object, you’ll be able to perform any asynchronous operation. You’ll just have to remember to call the Complete() method once the operation is done. Let’s see how to use this approach in combination with the method provided to share one or more files, called SetStorageItems():

async void transferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)

{    

    args.Request.Data.Properties.Title = "Sharing demo";

    args.Request.Data.Properties.Description = "This is a file sharing demo";

    DataRequestDeferral deferral = args.Request.GetDeferral();

    List<StorageFile> files = new List<StorageFile>();

    StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("text.txt");

    files.Add(file);

    args.Request.Data.SetStorageItems(files);

    deferral.Complete();

}











The first thing we do is get a reference to the DataRequestDeferral object by using the GetDeferral() method offered by the Request object (which is one of the properties of the method’s parameters). Then we can retrieve a reference to the file (or the files) we want to share. In the previous file, we want to share a text file called text.txt stored in the local storage. To perform the sharing operation, we call the SetStorageItems() method of the Request.Data object, passing as parameter the list of files to share. This method always requires a collection, so we always need to create a list, even if, like in the previous sample, we are going to share just one file. In the end, we complete the asynchronous operation by calling the Complete() method exposed by the DataRequestDeferral object.

Creating the target application

Receiving some content from another application requires you to subscribe to one of the contracts offered by the operating system. So, before writing some code, we’ll need to register the contract in the manifest file, inside the section called Declarations. In the Available Declarations drop-down menu, you’ll find a list of all the available contracts and extensions. The one required for our scenario is called Share Target and it requires us to set two pieces of information:

  • The data type (or types) we want to support, which can be:
  • Text
  • URI
  • HTML
  • StorageItems (for files)
  • The file types we’re going to support, in case we want to be able to receive files. We do this by setting up the section titled Supported file types. We’ll need to press the Add new button for each type we want to support and to specify the file extension. Otherwise, we can simply enable the Supports any file type option to be able to receive any file type.

The next step is to manage the sharing operation. Typically, an application that acts as a share target offers a specific page for this scenario, providing a preview of the content that will be shared and allowing the user to confirm or cancel the operation. Consequently, you won’t find any sample on how to define the sharing page’s layout. It’s up to you and to the content you want to share to define the layout that best fits your needs. For example, if your application allows the user to share an image, the sharing page should display a thumbnail; or if it can receive a link, it should display an image with a preview of the website; etc.

When a target application is activated by a share contract, it doesn’t follow the traditional application lifecycle. As a result, the OnLaunched() method of the App class isn’t triggered like it is when the application is opened using the main tile or icon in the Start menu. In a sharing scenario, the application is activated by the OnShareTargetActivated() method that can be overridden in the App class, like in the following sample:

protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)

{

    Frame rootFrame = Window.Current.Content as Frame;

    if (rootFrame == null)

    {

        rootFrame = new Frame();

    }

    rootFrame.Navigate(typeof(SharePage), args.ShareOperation);

    Window.Current.Content = rootFrame;

    Window.Current.Activate();

}










This method simply takes care of redirecting the user to the page we’ve prepared to manage the sharing operation (in the previous sample, it’s called SharePage). By using the Navigate() method of the Frame class, we also add as navigation parameter the ShareOperation property that is included in the OnShareTargetActivated() method’s parameters. This property contains all the information about the sharing operation, like the data received from the source application, so we’ll need to use it in the sharing page to perform the operations we need. The only part to highlight of the previous code is that, before performing the navigation, we check whether the Frame of the application is already initialized, otherwise we create it. We need to remember that the sharing operation can also be started when the target application is not running. In this case, we need to initialize the whole navigation infrastructure (the frame, the main window, etc.) as if it had been manually launched by the user. The difference is that, this time, instead of redirecting the user to the main page of the app, we will directly redirect them to the page we’ve created to handle the sharing scenario.

Let’s see in detail how to receive the most common data types from another application.

Receiving a text

Regardless of the data type we want to receive, we need to manage the OnNavigatedTo() method in the page that we have created to act as target. As we learned in the navigation section of one of the UWP Succinctly e-books, thanks to this method, we are able to retrieve the parameter that has been passed by the previous page (even if, in this case, it isn’t really a page but the App class itself). This parameter contains a ShareOperation object, which offers a property called Data. It contains all the information about the content that has been shared. Let’s analyze the following sample:

protected override async void OnNavigatedTo(NavigationEventArgs e)

{

    ShareOperation shareOperation = e.Parameter as ShareOperation;

    if (shareOperation.Data.Contains(StandardDataFormats.Text))

    {

        string text = await shareOperation.Data.GetTextAsync();

        MessageDialog dialog = new MessageDialog(text);

        await dialog.ShowAsync();

    }

}









The purpose of the first operation is to understand what kind of content has been shared. A target application could implement many sharing contracts, so we need to detect which data type we’ve received in order to properly manage it. This operation is performed by checking the content of the Data property. The Universal Windows Platform offers an enumerator called StandardDataFormats with a value for each supported data type. By using the Contains() method, we are able to check if the Data property contains a specific data type. In the previous sample, we evaluate the Text data type. After we identify the proper one, we can use one of the many methods exposed by the Data that start with the Get prefix, which can parse the content and convert it into the expected type. In the previous sample, since we’re working with text, we call the GetTextAsync() method, which simply returns the string that has been shared by the source application. In the sample, we display it to the user using a pop-up message.

However, the sharing operation isn’t complete. We just showed a preview of the content received by the other application to the user. The last and most important step is to interact with the shared content. The way you’re going to design this scenario doesn’t need to follow a fixed set of guidelines, as it depends on the application’s purpose. For example, a Twitter application may handle the sharing page by including a Post button so that the user, after seeing the preview of the tweet, can post the content on their timeline. Consequently, you won’t find any specific sample on how to perform the sharing operation in this chapter. However, there’s one step that’s always required, regardless of the way you perform the sharing operation: notifying the operating system that the sharing operation is complete and that the target application can be closed, so that the control can be returned to the source app. The notification is performed by calling the ReportCompleted() method on the ShareOperation object we’ve received as parameter in the OnNavigatedTo() event. Here is a complete sample:

public sealed partial class SharePage : Page

{

    private ShareOperation shareOperation;

 

    public SharePage()

    {

        this.InitializeComponent();

    }

 

    protected override async void OnNavigatedTo(NavigationEventArgs e)

    {

        shareOperation = e.Parameter as ShareOperation;

        if (shareOperation.Data.Contains(StandardDataFormats.Text))

        {

            string text = await shareOperation.Data.GetTextAsync();

            MessageDialog dialog = new MessageDialog(text);

            await dialog.ShowAsync();

        }

    }

 

 

    private void OnShareClicked(object sender, RoutedEventArgs e)

    {

        //perform the sharing operation

        shareOperation.ReportCompleted();

    }

}




















You’ll notice that we’ve defined the ShareOperation object with class-scope. This way, we can interact with it both in the OnNavigatedTo() method (to retrieve the shared content) and in the method that performs the sharing (in this case, it’s an event handler that’s triggered when the user presses a button on the page).

Receiving a link

Now that we’ve seen how to receive a text, it will be easy to understand how to receive all the other data types. In fact, the base approach is always the same. We subscribe to the OnNavigatedTo() method to receive the ShareOperation object, which contains the shared content. Then, after the sharing operation is completed, we call the ReportCompleted() method. The only difference is that we’ll need to retrieve the content in a different way for each data type.

When it comes to working with links, we have to check whether the Data property of the ShareOperation object contains WebLink content. For this, we can retrieve it using the GetWebLinkAsync() method, like in the following sample:

protected override async void OnNavigatedTo(NavigationEventArgs e)

{

    ShareOperation shareOperation = e.Parameter as ShareOperation;

    if (shareOperation.Data.Contains(StandardDataFormats.WebLink))

    {

        Uri uri = await shareOperation.Data.GetWebLinkAsync();

        MessageDialog dialog = new MessageDialog(uri.AbsoluteUri);

        await dialog.ShowAsync();

    }

}









Receiving a file

When you receive a file from a source application, the Data object contains a value of type StorageItems. In this scenario, we can retrieve the list of shared files using the GetStorageItemsAsync(), which returns a read-only collection. If you want to perform additional operations, it’s better to copy the files in the application’s local storage, like in the following sample:

protected override async void OnNavigatedTo(NavigationEventArgs e)

{

    ShareOperation shareOperation = e.Parameter as ShareOperation;

    if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))

    {

        var list = await shareOperation.Data.GetStorageItemsAsync();

        foreach (IStorageItem item in list)

        {

            StorageFile file = item as StorageFile;

            await file.CopyAsync(ApplicationData.Current.LocalFolder);

        }

    }

}

 











About Advanced UWP Part 2: Communicating with Windows 10 and Other Apps

By reading Matteo Pagani’s white paper on UWP, you will understand the mechanisms that the Universal Windows Platform offers to allow your application to interact with the operating system and with other applications, either developed by you or by other companies. You will learn how to leverage the built-in share capabilities to exchange data between one app and the other, how to register your application so that it can handle specific file types or URI schemes so that it can be invoked by other apps, and how to achieve the same goal but in the background with a new feature called App Services. In the end, you will also learn how to interact with the speech services provided by the Universal Windows Platform and with Cortana, the digital assistant integrated with every Windows 10 device.

Pingbacks and trackbacks (1)+

Loading