How to support pull operations (XAML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Most of the time, the app supplies the share data immediately from its DataRequested event handler. Sometimes, however, your app may need time to get the data ready for sharing. At those times, you can provide a list of supported formats, but delay preparing and providing the content until the target app requests it. Deferring to provide content until the target app requests it is called a pull operation (or delayed sharing).

To support pull operations, you first create a function that packages the data that the user wants to share. Then, instead of supplying the actual data to the target app, you supply a delegate function. When the target app tries to get the data, the system calls your delegate function. The advantage here is that your app can share the data behind the scenes, allowing the user to continue using your app for other activities.

What you need to know

Technologies

Prerequisites

  • You should be familiar with Visual Studio and its templates.
  • You should be familiar with developing in C#.
  • You should understand how to get files and other data, such as by using FileOpenPicker. You can learn more in Accessing files with file pickers.

Instructions

Step 1: Adding the DataTransfer namespace

First you need to add the right namespaces to your app so you can create and process the objects related to sharing. At a minimum, you should add the Windows.ApplicationModel.DataTransfer namespace:

using Windows.ApplicationModel.DataTransfer;

This namespace has all you need for basic sharing. Remember, though, if you want to share content such as images or files, you'll need to add those namespaces as well. Here's a list of the namespaces you might need:

Step 2: Create a delegate function to collect the content that the user wants to share.

The exact nature of the delegate function depends on your app. Here's one example.

async void OnDeferredImageRequestedHandler(DataProviderRequest request)
{
    // Provide updated bitmap data using delayed rendering.
    if (this.imageStream != null)
    {
        DataProviderDeferral deferral = request.GetDeferral();
        InMemoryRandomAccessStream inMemoryStream = new InMemoryRandomAccessStream();

        // Decode the image.
        BitmapDecoder imageDecoder = await BitmapDecoder.CreateAsync(this.imageStream);

        // Re-encode the image at 50% width and height.
        BitmapEncoder imageEncoder = await BitmapEncoder.CreateForTranscodingAsync(inMemoryStream, imageDecoder);
        imageEncoder.BitmapTransform.ScaledWidth = (uint)(imageDecoder.OrientedPixelHeight * 0.5);
        imageEncoder.BitmapTransform.ScaledHeight = (uint)(imageDecoder.OrientedPixelHeight * 0.5);
        await imageEncoder.FlushAsync();

        request.SetData(RandomAccessStreamReference.CreateFromStream(inMemoryStream));
        deferral.Complete();
    }
}

Notice that the delegate function uses SetData to add the content, instead of a format-specific function, like SetBitmap or SetStorageItems. Any time you use a delegate function, you must use SetData to supply the content.

Step 3: Get the DataTransferManager object

The DataTransferManager object is the main starting point for any share operation.

DataTransferManager dataTransferManager = DataTransferManager.GetForCurrentView();

Step 4: Add an event handler for the DataRequested event

Add a DataRequested event handler to fire when the user wants to invoke Share. In a Windows Store app, this occurs automatically when the user invokes the Share charm. If you're developing for Windows Phone, there is no built-in Share charm, so you'll need to add a control for the user to tap and trigger the handler.

dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);

Step 5: Get a DataRequest object

When a DataRequested event occurs, your app receives a DataRequest object. This object contains a DataPackage that you'd typically use to provide the content that the user wants to share. In pull operations, however, you specify a delegate function instead of the actual data.

DataRequest request = e.Request;

Step 6: Set the file types, if needed.

If you're using a delegate function to share files, you need to use DataPackage.Properties.FileTypes to specify the file types that your app supports.

Step 7: Add the delegate function to the DataPackage object

The SetDataProvider method calls the delegate function you created that supplies the actual content.

e.Request.Data.SetDataProvider(StandardDataFormats.Bitmap, 
        new DataProviderHandler(this.OnDeferredImageRequestedHandler));

At this point, when the user initiates sharing, by tapping the Share charm in Windows, for example, the app can respond instantly with a DataPackage object containing the delegate function. The share operation can now continue behind the scenes, freeing the user to continue with whatever other activities they want to do.

Keep in mind that if you're using the SetStorageItems method to share files, this method creates read-only StorageFile objects for sharing and holds onto the original objects. This means that if you add expanded properties to the original file after you call SetStorageItems, those new expanded properties won't be included in the StorageFile objects being shared. That's why we recommend you don't add the files until you're completely ready.

Remarks

To download code that demonstrates how to support pull operations, see our code gallery sample.

Example

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Register this page as a share source.
    this.dataTransferManager = DataTransferManager.GetForCurrentView();
    this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, 
            DataRequestedEventArgs>(this.OnDataRequested);
}

private void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs e)
{
    if (imageFile != null)
    {
        DataPackage requestData = e.Request.Data;
                                // The title is mandatory
        requestData.Properties.Title = "Delay rendered image";
        requestData.Properties.Description = "Resized image from the Share Source sample";
        requestData.Properties.Thumbnail = RandomAccessStreamReference.CreateFromFile(imageFile);
        requestData.SetDataProvider(StandardDataFormats.Bitmap, 
                new DataProviderHandler(this.OnDeferredImageRequestedHandler));
    }
    else
    {
    e.Request.FailWithDisplayText("Select an image you would like to share and try again.");
    }
}

private async void OnDeferredImageRequestedHandler(DataProviderRequest request)
{
    // In this delegate we provide updated Bitmap data using delayed rendering.

    if (this.imageFile != null)
    {
        // If the delegate is calling any asynchronous operations it needs to acquire
        // the deferral first. This lets the system know that you are performing some
        // operations that might take a little longer and that the call to SetData 
        // could happen after the delegate returns. Once you acquired the deferral object 
        // you must call Complete on it after your final call to SetData.
        DataProviderDeferral deferral = request.GetDeferral();
        InMemoryRandomAccessStream inMemoryStream = new InMemoryRandomAccessStream();

        // Make sure to always call Complete when finished with the deferral.
        try
        {
            // Decode the image and re-encode it at 50% width and height.
            IRandomAccessStream imageStream = await this.imageFile.OpenAsync(FileAccessMode.Read);
            BitmapDecoder imageDecoder = await BitmapDecoder.CreateAsync(imageStream);
            BitmapEncoder imageEncoder = await BitmapEncoder.CreateForTranscodingAsync(inMemoryStream, 
                    imageDecoder);
            imageEncoder.BitmapTransform.ScaledWidth = (uint)(imageDecoder.OrientedPixelWidth * 0.5);
            imageEncoder.BitmapTransform.ScaledHeight = (uint)(imageDecoder.OrientedPixelHeight * 0.5);
            await imageEncoder.FlushAsync();

            request.SetData(RandomAccessStreamReference.CreateFromStream(inMemoryStream));
        }
        finally
        {
            deferral.Complete();
        }
    }
}

Sharing content source app sample

Sharing and exchanging data

How to produce requested data asynchronously

Quickstart: Sharing content

DataPackage

Windows.ApplicationModel.DataTransfer

Windows.ApplicationModel.DataTransfer.Share