Report progress – IAsyncActionWithProgress

One of the best things you can do for your app’s users is to report the progress of an operation. Showing a progress bar can greatly increase the patience of the user, not knowing how long something is going to take can make the operation just feel longer.

Luckily, many WinRT Tasks will return an IAsyncActionWithProgress. I’ve seen many blog posts about this topic, but most are deeply involved and weren’t suited for a quick lookup. This post will show you a quick example, which you can apply in your app..

A quick summary is that you have two main options with one of these actions; await the action result directly or don’t await and hook into the Progress event and Completed event. As an example, I will use the MediaComposition class available for Windows Phone 8.1.

If you wanted to save the composition but still want to catch render failure, you’d do something like this:


var transcodeFailureReason = await mediaComposition.RenderToFileAsync(storageFile);

if(transcodeFailureReason == TranscodeFailureReason.None)
{
//success!
}

However, the rendering could take a long time, it’s better to show the user the progress of the operation. RenderToFileAsync will also give you an IAsyncActionWithProgress if you don’t use await. You’d hook in to the events I mentioned above. Here is what that looks like:


var result = mediaComposition.RenderToFileAsync(storageFile);
result.Progress += Progress;
result.Completed += Completed;

The progress event actually gives you the percentage completed of the operation in the second parameter. Just make sure you marshal back to the UI thread when updating the UI. Here is an example of using a TextBlock and ProgressBar to report progress.


private void Progress(IAsyncOperationWithProgress<TranscodeFailureReason, double> asyncInfo, double progressInfo)
{
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ProgressTextBlock.Text = string.Format("{0}% complete", progressInfo);
ProgressBar.Value = progressInfo;
});
}

Finally, in the Completed event, make sure the rendering happened without error and move on. Note that you are still on a different thread, if you have a list of items in the ViewModel to update you need to marshal it as well.


private async void Completed(IAsyncOperationWithProgress<TranscodeFailureReason, double> asyncInfo, AsyncStatus asyncStatus)
{
if (asyncInfo.GetResults() != TranscodeFailureReason.None)
{
//operation failed
}

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await UpdateMyViewModelCollectionWithNewThing();
});
}

Showing the user progress adds professionalism to your app and makes the user’s experience much better.

Enjoy!

How to disable display timeout in WinRT

For those of you who are building multimedia related apps, you’ll want to consider disabling the display timeout of the user’s device. This prevents the screen from dimming or locking while the user is playing/recording media.

In my case, I have a view where the user is doing some video recording. so I don’t want the screen to time out on that page

1)  Create a class scope variable::

private DisplayRequest dRequest;

2) On your OnNavigatedTo event, grab a new DisplayRequest and get a deferral

if (dRequest == null)

{

     dRequest = new DisplayRequest();

     dRequest.RequestActive();

}

3) when the user is done, set it back to normal in the OnNavigatingFrom event:

if (dRequest != null)

{

     dRequest.RequestRelease();

     dRequest = null;

}

That’s it!

I’ll be also using this on media playback page, but in that case I wont use a “page loaded/unloaded” approach. Instead, I’ll wait for the media to be loaded into the MediaElement before getting the deferral and get the release on media ended.

Remember to be respectful when playing with power like this, do not do this for your whole app. Be mindful and skillful and apply it to just the parts that truly need it.

Read-only Address Bar for Webview

If you are having a Windows Store application fail certification or being unpublished because you have violated Policy 2.1.2  – requires a visible address bar that displays a secure connection to users when they enter financial information or complete a transaction (see policy definitions).

The reason for this rule is because you are bringing the user to a site that is using HTTPS and the user may be entering in secure information. You need to give the user confidence that you are not spoofing the website and stealing the entered information.

This is easily remedied by adding a TextBlock (which is read-only) to the page and displaying the web address every time a page loads. Conveniently, the WebView has a perfect event handler for this: OnNavigationStarting.

You can get the web address through the Uri property of the WebViewNavigationStartingEventArgs and then set it to the Text property of your TextBlock. This is better explain with some example code.

Let’s say you have a page with a webview, you want to add a TextBlock to the top. You’ll want to create a Grid with two rows, set the first row’s height to Height=”Auto”. Put the TextBlock in Row 0 and the WebView in Row 1.

Now, let’s hook into NavigationStarting of the WebView, and in the event handler grab the Uri and set it to your TextBlock.Text property.

Here is what you should have (put some placeholder text so you can see what it looks like, also use gray for a foreground text color so the user knows it is read-only).

<Grid Background="White">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock x:Name="MyAddressBarTextBlock" 
                           Text="http://www.somewebsite.com" 
                           Foreground="Gray" />
            <WebView x:Name="MyWebView" 
                     NavigationStarting="MyWebView_OnNavigationStarting"
                     Grid.Row="1"/>
</Grid>

…and here is what your event handler should look like:

private void MyWebView_OnNavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
     try
     {
        //the event args contain the web address, get it from args.Uri and hold it in a local variable
        string websiteAddress = args.Uri.ToString();

        //now set the address to the Textblock's text property
        MyAddressBarTextBlock.Text = websiteAddress;
      }
      catch (Exception ex)
      {
         //do something with the error
      }
}

That’s all there is to it! Every time the WebView is going to load a page, the NavigationStarted event will fire and your TextBlock will show the upcoming address.

Here are some screenshots from the my sample…

WinAddressBar2 WinAddressBar

PhoneWebAddressBar2 PhoneWebAddressBar

Happy Coding!
Lance

Extra Credit and Sample Universal App:
This example is barebones, I challenge you to make a user control that houses the WebView and TextBlock, then you can reuse it across your app instead of doing this on every page. I’ve written a demo Universal app for you that has the following:

  • UserControl in the Shared folder (ReadOnlyWebView.xaml)
  • ProgressRing for loading content
  • Extends Source DependencyProperty and exposes Refresh method
  • Instantiates the custom control in MainPage.xaml for both Phone and Windows projects

This should wet your appetite and show you how to extend it further.

DOWNLOAD THE SAMPLE UNIVERSAL APPLICATION