UWP — Build a download progress

It’s been a week since I started coding a new wallpaper app and I wanted to display a progress bar when downloading images or setting them as wallpapers and lock screen backgrounds.

Hangon

Since a download can take undefined time due to internet latency or large image’s size, I wanted to inform the user of what going on in the app. Visual feedback is good to avoid user’s frustration.

In the following lines, I’ll describe the steps I followed to build a visual progress.

XAML: ProgressBar

Simple way

Just add a ProgressBar control to your XAML page.

<ProgressBar x:Name=”ProgressDeterminate” IsIndeterminate=”False”>

More complete way

As I needed to display a message with the progress, I’ve built a more complete solution with a FlyoutPresentercontrol.

In your XAML page, first add a Flyout control which will host your ProgressBar and TextBlock.

Add two ProgressBar controls in a Grid: one with the IsIndeterminate value to true, and the other to false. Then, set the property FlowDirection="RightToLeft" on the ProgressBar with IsIndeterminate="True" .

I used these two ProgressBar to have a nice effect: it gives the impression that the dots fill the determinate ProgressBar.

Hangon — ProgressBar

Because I also wanted to display a message, I added a TextBlock control on top of the progress.

C#: Code-behind

Show the progress control

In order to start the download process, add a Button in your XAML page and the associated Tapped event handler in code-behind.

Hangon — Download event handler

CmdDownload_Tapped handler fires the following operations:

  1. Show the progress flyout with ShowProgress() method
  2. Start the download with await Wallpaper.SaveToPicturesLibrary(...) ( Wallpaper class represents my utility class to download images)
  3. Hide the progress flyout with HideProgress() method

NOTE: CurrentPhoto is an object model which represents an image in my app. You can replace it with a Stringtargeting an online image.

Use Windows.Web.Http.HttpRequest

In UWP (Universal Windows Platform) app, you can use at least two namespaces to send HTTP requests:

I chose to go with the second namespace as System.Net.Http doesn’t provide a native progress handler.

In the Wallpaper class, add the SaveToPicturesLibrary(...) method taking two parameters as arguments:

  • A photo object model that you can replace with a string targeting an online image
  • A method callback that will be fired on every HTTP update
Hangon — Download image with progress

NOTE: If you don’t want to use a FileSavePickerin your app, skip the first three steps. But you must provide a StorageFile object to save the image to the device’s storage.

Here is a quick sample:

using System;
using Windows.Storage;
// Get the app's local folder to use as the destination folder.
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
// Create a new file in the app's local folder.
string fileName = "test.jpg";
StorageFile file = await
localFolder.CreateFileAsync(newFileName);

Step by step:

  1. Create a FileSavePicker object to let the user choose the file’s destination
    NOTE: You need to add Pictures Library capability to give your app read & write accesses to the Pictures Library
var savePicker = new Windows.Storage.Pickers.FileSavePicker(){        
SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.PicturesLibrary
};

2. Add the supported file’s extensions (e.g. jpg, pngfor images) and a suggested name to the file picker

savePicker.FileTypeChoices
.Add("Picture", new List<string>() { ".jpg" });
savePicker.SuggestedFileName = photo.Id; // pre-fill file's name

3. Retrieve the FileStorage object

StorageFile file = await savePicker.PickSaveFileAsync();

4. Initialize the HttpClient and set the progress handler

var client = new HttpClient();
Progress<HttpProgress> progressCallback = 
new Progress<HttpProgress>(httpProgressCallback);

5. Get the response headers

var request = 
new HttpRequestMessage(HttpMethod.Get, new Uri(photo.Urls.Raw));
HttpResponseMessage response = 
await client.SendRequestAsync(request)
.AsTask(tokenSource.Token, progressCallback);

6. Read the data stream received by the HTTP request

IInputStream inputStream = 
await response.Content.ReadAsInputStreamAsync();

7. Open a read & write stream on the StorageFile object returned by the FileSavePicker and copy the data from the HTTP request to the StorageFile

IOutputStream outputStream = 
await file.OpenAsync(FileAccessMode.ReadWrite);
await RandomAccessStream
.CopyAndCloseAsync(inputStream, outputStream);

8. Ensure resources are released

client.Dispose();
inputStream.Dispose();
outputStream.Dispose();

Callback

Now let’s see how to update the ProgressBarcontrol with the method handlerHttpProgressCallback .

Handon — Progress handler and UI update

Each time the HTTP request is updated, the HttpProgressCallback is called. Thus, you can give user interface (UI) updates to your users.

At the request’s beginning, the TotalBytesToReceive is null, so you can skip UI updates.

The next lines simply set the Minimum , Maximum and current Value of the ProgressBar control until the download is finished.

Hangon — ProgressBar

All done!

Thank you for reading this far and don’t hesitate to comment, share and like this article if you find it useful.

More