How To: Popup a XAML WebView UI async and wait for results

I needed to popup a WebView and wait on an event before continuing.  There really was no illustration on how to do this so here you all go!

In my scenario I wanted to Navigate to a Uri to do some forms authentication and get some cookies loaded.  I knew when I was redirected to a certain Uri the cookie was loaded.  To leverage the WebForms based UI I decided to use the WebView control.  I would navigate to the Uri to authenticate, then when the browser navigates to another know Uri I would close the browser and continue code execution.

WebView.Navigate returns immediately so you cannot await this call.  What you need to do is wait on an event flag then continue execution.

The code is relatively simple and well commented.  The key to awaiting this async is the line of code with the Task.Run call.  This spins off a thread that allows the UI to refresh.  To see the effect, remove the Task.Run and just await the AutoResetEvent.  You will see the UI freezes!

Code

Xaml

 <Page
    x:Class="AsyncWebUI.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AsyncWebUI"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
        <TextBlock VerticalAlignment="Top" HorizontalAlignment="Left" Margin="120,28,0,0" 
            Text="Wait for Event" FontSize="60"/>
        <Grid Margin="120,100,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="0,10" >
            <TextBox x:Name="theUri" Width="300" Text=https://blogs.msdn.com/wsdevsol
                 Margin="0,5,5,5"></TextBox>
            <Button Content="Navigate" Click="Button_Click" FontFamily="Global User Interface" Margin="5">
            </Button>
                <TextBlock Text="Navigate to https://bing.com to close browser" HorizontalAlignment="Center"
                 VerticalAlignment="Center" Margin="5,0,0,0"/>
            </StackPanel>
        <StackPanel x:Name="hidePanel" Grid.Row="1">
            <TextBlock Text="Here is some stuff to hide while navigating" Height="100"></TextBlock>
            <TextBlock Text="Here is some stuff to hide while navigating" Height="100"></TextBlock>
        </StackPanel>
            <WebView x:Name="MyWebView" Grid.Row="1" Visibility="Collapsed" 
              NavigationCompleted="MyWebView_NavigationCompleted"></WebView>
        </Grid>
    </Grid>
</Page>

C#

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;


namespace AsyncWebUI
{
    public sealed partial class MainPage : Page
    {
        AutoResetEvent waitForNavComplete;
        public MainPage()
        {
            this.InitializeComponent();
            // Create AutoResetEvent initially not set
            waitForNavComplete = new AutoResetEvent(false);
        }

        /// <summary>
        /// async so we can wait for somethign before proceeding (whatever that may be)
        /// </summary>
        async private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Hide XAML and bring up the WebView
            hidePanel.Visibility = Visibility.Collapsed;
            MyWebView.Visibility = Visibility.Visible;

            // Go to the site specified
            MyWebView.Navigate(new Uri(theUri.Text));

            // Wait for this AutoResetEvent to be Set
            // If you just did the Wait the WebView would not come up and the UI would freeze
            await Task.Run(() => { waitForNavComplete.WaitOne(); });

            // Reset the event (unset it)
            waitForNavComplete.Reset();

            // Swap back the UI
            MyWebView.Visibility = Visibility.Collapsed;
            hidePanel.Visibility = Visibility.Visible;
        }

        void MyWebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
        {
            // I am waiting for a specific Uri.  When I see it...
            if (args.Uri == new Uri("https://www.bing.com"))
            {
                // Reset the event so we close the browser and bring up the old XAML
                waitForNavComplete.Set();
            }
        }
    }
}

Summary

So it is fairly easy to do this.  The trick is really in the await Task.Run(() => call. Please drop me a line if you find this useful!