Metro WebView Source and HTML workarounds

WebView Workarounds

In the process of writing my first Windows 8 Metro style app using C# and XAML, I came across some limitations of the current WebView control’s implementation.  The flow for the feature I was working on is fairly simple:

  1. Content for a webpage is displayed in a WebView (using the WebView’s Source property).
  2. User browses the page until they find something they want to save.
  3. User clicks a save button and the app parses the information into a readable format for saving in the app’s storage.

This is a pretty straightforward concept and I thought the implementation would be just as intuitive. Judging by the similarity of the WebView control to the Silverlight WebBrowser control, I went about implementing this functionality as I would have for Silverlight.  I set the WebView’s Source property to the initial Uri where I wanted the browsing to begin.  I wired up a test button that I thought would kick off the parsing process.  That’s when I started to run into problems.

The first problem – No SaveToString()

My initial attempt at the parser was based on the premise that I would have direct access to the HTML that was being rendered in the WebView control.  Initially, I thought the WebView would support something similar to WebBrowser’s SaveToString() method which returns the HTML being displayed currently as a string.  It doesn’t.

The second problem – Source doesn’t update

Since I didn’t have direct access to the HTML directly, my next attempt was going to be to pass the Uri for the content that’s currently being displayed.  Again, in the Silverlight WebBrowser control this would be as simple as using the Source property which updates as the user browses inside the WebBrowser control.  Unfortunately, the Metro WebView’s Source property does not update as the user browses and instead locks itself to whatever it was initially set to.  Stymied again.

Workaround #1 – Retrieving the HTML using InvokeScript

Thankfully, the WebView does support a handy method called InvokeScript which allows you to run a script on the page that is being displayed.  Combining that with the JavaScript eval function, we can call arbitrary JavaScript code on the content.  This JavaScript snippet will allow us to get the HTML that’s being rendered:

document.documentElement.outerHTML;

All we need to do is call that script on the page using InvokeScript() and it will return the HTML in a string:

string html = webview.InvokeScript("eval", new string[] {"document.documentElement.outerHTML;"});
Parse(html);

Workaround #2 – Keeping track of the Source

I still needed to solve the second problem because the image links in the HTML were relative.  I still needed access to the Source Uri.  It turns out there’s a slightly non-intuitive way to keep track of this by wiring up the WebView’s LoadCompleted event which fires when top-level navigation completes and the content loads into the WebView control.  The NavigationEventArgs in the LoadCompleted event handler contains a Uri property which represents the Uri that was just loaded in the WebView.  The following snippet will fetch the Uri:

Uri source = null;

webview.LoadCompleted += (s,e) => {
     source = e.Uri;
}

Conclusion

We are still in the very early days of Windows 8 Metro style app development and there are a lot of rough edges in the SDK and documentation (more in the XAML side than the HTML5 side but more on that later…).  Some things will simply not work the way we expect them to and we will have to adapt and work around them until they are hopefully fixed in future releases.

No related posts.