Hi - I am Matt Small, a Sr. Escalation Engineer on the Microsoft Windows Store Developer Solutions team.  I have been working on Windows 8 since before the Release Preview, and I have spent many hours working in the Store forums, primarily in the C# forum.  I’ve seen a *lot* of posts on the C# forum regarding the WebView control and thought it would be good to compile the information we’ve collected regarding Webview. Since this article is rather long, I've indexed it for you:

 

  1. WebView is not a general-purpose browser
  2. WebView always renders on top of XAML
  3. WebView doesn’t do Flash.  Or Silverlight.  Or PDF.  Or any other ActiveX control or plugin.
  4. How to invoke Javascript inside the displayed webpage of a Webview
  5. How to receive information from the WebView
  6. How to inject javascript into a WebView page
  7. How to clear the WebView cache
  8. How to embed a font into your app to be used by WebView
  9. Launching other apps from a link inside WebView
  10. How to get rid of those annoying JavaScript exceptions when debugging

  

  1. WebView is not a general-purpose browser

    The WebView control is intended to allow a Store app developer to embed some part of the web inside the application, but should not be added to an application to allow browsing to any site on the Internet - it should be used to link specifically to pages hosted on your own website for the purposes of being displayed within your Store application.

    For example:  if you have content that you think may change quite often, it could be easier to place this information in a WebView control rather than update your application via the store. 

    However, what it is not intended to be is a portal from a Store app into a Web app.  In fact, your Store app will be rejected from listing in the Store if this is your primary use of the WebView control.

    Aside from that, you are going to find that some things simply don’t work the way that they do in the full Internet Explorer browser. It’s not that these changes are intentional; it’s that this is a different (but similar) codebase – it is not IE.  In fact, the documentation is clear about the limitations of this control:

    WebView always uses Internet Explorer 10 in document mode. Additionally, WebView does not currently support HTML5, AppCache, IndexedDB, programmatic access to the Clipboard, or geo location, and supports only the Document Object Model (DOM) properties that are supported in Windows Store apps using JavaScript.

    As a part of your overall application, the content displayed inside the WebView control should conform to the Webview’s limitations.

  2. WebView always renders on top of XAML

    Again, the documentation is clear on this, but it bears repeating since many (including me) have been caught by this particular aspect of the WebView:  Any element that would normally overlap the space occupied by the WebView and thus cover or partially cover that area, will find that the WebView will always be on top of the XAML - this is called an "Airspace" issue.  This is because the WebView is not subclassed from the Control object, and it is drawn separately from the rest of the XAML space. Using this code, if WebView were a regular control, we would see it behind the TextBox:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
    <Grid>
    <WebView x:Name="MyWebview" Width="500" Height="500" Source="ms-appx-web:///HTMLPage1.html"/>
    <TextBox Width="800" Height="30" Text="This control would be in front of the WebView if WebView were a normal control."/>
    </Grid>
    <Button x:Name="MyButton" Margin="10" Content="Invoke the WebViewBrush control" Click="MyButton_Click_1" HorizontalAlignment="Center"/>
    </StackPanel>
    </Grid>


    You can see the problem in this image:





    There is a workaround to this this issue, and that is to use the WebViewBrush control.  In a nutshell, the WebViewBrush copies the content of an existing WebView control, and displays it inside a Rectangle object – thus allowing other XAML content to render over it.  Here's how we can show the contents of the Webview on top of the TextBox:

    Add a Rectangle object to the XAML:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
    <Grid>
    <WebView x:Name="MyWebview" Width="500" Height="500" Source="ms-appx-web:///HTMLPage1.html"/>
    <Rectangle x:Name="MyRectangle"/>
    <TextBox Width="800" Height="30" Text="This control would be in front of the WebView if WebView were a normal control."/>
    </Grid>
    <Button x:Name="MyButton" Margin="10" Content="Invoke the WebViewBrush control" Click="MyButton_Click_1" HorizontalAlignment="Center"/>
    </StackPanel>
    </Grid>


    Inside the Click event handler of the button:

    private void MyButton_Click_1(object sender, RoutedEventArgs e)
    {
    WebViewBrush MyWebViewBrush = new WebViewBrush();
    MyWebViewBrush.SourceName = "MyWebview";
    MyWebViewBrush.Redraw();
    MyRectangle.Fill = MyWebViewBrush;
    MyWebview.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    }


    Now when we click the button, we will see the TextBox over what was over the WebView.  Only thing, is, the WebView itself is Collapsed, while the Rectangle containing the WebViewBrush is displayed.




  3. WebView doesn’t do Flash.  Or Silverlight.  Or PDF.  Or any other ActiveX control or plugin.

    ActiveX controls have made Internet Explorer an incredibly useful tool for applications that need to be widely distributed.  However, it has also been an attack vector used by malware developers.  In the development of the WinRT APIs, it was decided that the WebView control – like the Immersive Internet Explorer – would not host any ActiveX controls, which is how older versions of IE hosted Flash and Silverlight, for safety and performance reasons. Additionally, although the RTM version of IE10 includes Flash as a built-in feature, this did not make it into the WebView control.

  4. How to invoke Javascript inside the displayed webpage of a Webview

    Now that we understand that the content of the WebView should be a part of the app experience, we ought to know how to manipulate the HTML so it is useful to the greater application.  Let’s try this out:

    We create an HTML page that contains a Javascript function:

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta charset="utf-8" />
    <title>Matt's Webview Content Page</title>
    <script lang="en-us" type="text/javascript">
    function TimeUpdate() {
    var TimeTextbox = document.getElementById("TheTime");
    TimeTextbox.value = new Date().toTimeString();
    }

    </script>
    </head>
    <body>
    <h2>Matt's Webview Content Page</h2>
    <h5>The current time is: <input type="text" Id="TheTime" /> </h5>
    <button onclick="TimeUpdate()">Update the time!</button>
    </body>
    </html>

    Now we create a XAML page that contains a WebView which loads the HTML page:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
    <WebView x:Name="MyWebview" Width="500" Height="500" Source="ms-appx-web:///HTMLPage1.html"/>
    <Button x:Name="MyButton" Margin="10" Content="Invoke the TimeUpdate Javascript function from C# using this button" Click="MyButton_Click_1" HorizontalAlignment="Center"/>
    </StackPanel>
    </Grid>

     

    Notice that the source of the WebView uses the “ms-appx-web:///” protocol and that there are *three* forward slashes used for this.

    Finally, create the code that calls into the Javascript from the C# page:

    private void MyButton_Click_1(object sender, RoutedEventArgs e)
    {
    MyWebview.InvokeScript("TimeUpdate", null);
    }

    The Button in the XAML page invokes it’s Click event handler, which calls the InvokeScript method on the WebView control.  The InvokeScript method has two parameters: the name of the Javascript function to call, and an array of strings to be used as parameters to be passed to the function.

    This is what you’ll see when we run this code:

     

  5. How to receive information from the WebView

    Just as important as being able to invoke operations in the webpage is the ability to retrieve information from the webpage for use in your C# code.  Here is how it’s done:

    First, we need to way for the webpage to tell our Store app what it needs to know.  This is done using the window.external.notify(string) function.  I modified the TimeUpdate function to look like this:

     

     function TimeUpdate() {
    var TimeTextbox = document.getElementById("TheTime");
    TimeTextbox.value = new Date().toTimeString();
    window.external.notify(TimeTextbox.value);
    }

    Next, I added the event declaration to the XAML, and also added a new textbox by which I can see the information passed to the C# code:

    <WebView x:Name="MyWebview" Width="500" Height="500" Source="ms-appx-web:///HTMLPage1.html" ScriptNotify="MyWebview_ScriptNotify_1"/>

    <TextBox x:Name="MyTextBox" Width="200"/>

    Finally, I added the ScriptNotify event handler method:

    private void MyWebview_ScriptNotify_1(object sender, NotifyEventArgs e)
    {
    MyTextBox.Text = e.Value;
    }


    When I execute the code and click the “Update the Time!” HTML button, I receive the information as though my code were executing as part of the webpage:



  6. How to inject javascript into a WebView page
    Now that it’s possible to send and receive information into a webpage hosted by WebView, we need to consider the fact that there may be situations where we want to retrieve information from a webpage where we don’t own the content.  What we need to do in this situation is inject some code into the webpage for our own use.

    First, in order to inject the content, I can’t simply set the source of the WebView to that page – I won’t have a way to modify the page’s HTML source.  I need to download the content of the page into a string.  For simplicity’s sake, I will continue to use the same code that I have been using, although in a different way. Here’s the psuedocode:

    1. Download/obtain the HTML source of the page into a string.
    2. Find an insertion point for your new script to be put into the page.
    3. Insert your new script into that page.
    4. Set the WebView source to that string using the NavigateToString method.



    Here it is in action:

    Remove the source attribute from the WebView object in the XAML page.  This is what I have left:

    <WebView x:Name="MyWebview" Width="500" Height="500" ScriptNotify="MyWebview_ScriptNotify_1"/>

    Now perform the magic of obtaining the string of the webpage and modifying it:

    async protected override void OnNavigatedTo(NavigationEventArgs e)
    {
    Windows.Storage.StorageFile MyWebPageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///HTMLPage1.html"));
    string MyWebPageString = await FileIO.ReadTextAsync(MyWebPageFile);
    string ScriptTagString = "<script lang=\"en-us\" type=\"text/javascript\">";
    int IndexOfScriptTag = MyWebPageString.IndexOf(ScriptTagString);
    int LengthOfScriptTag = ScriptTagString.Length;
    string InsertionScriptString = "function SayHelloWorld() { window.external.notify(\"Hello World!\");} ";
    MyWebPageString = MyWebPageString.Insert(IndexOfScriptTag + LengthOfScriptTag + 1, InsertionScriptString);
    MyWebview.NavigateToString(MyWebPageString);
    }

    Lastly, I changed the InvokeScript method to call my new Javascript function:

    private void MyButton_Click_1(object sender, RoutedEventArgs e)
    {
    MyWebview.InvokeScript("SayHelloWorld", null);
    }

    Now, when I run this code, I have two functions:

    1. The original HTML “Update the time!” button, which will update the time in the textbox on the webpage as well as the XAML textbox.
    2. The XAML “Invoke the TimeUpdate Javascript function…” button, which invokes the “HelloWorld” function, and updates only the XAML textbox.




      You can download all three complete projects in the WebviewJavascript.zip file at the bottom of this post.

  7. How to clear the WebView cache

    Unfortunately, there is no programmatic way to clear the WebView cache from a Store app, and neither is there a nice UI-style way (i.e. a button).  However, it can be achieved by going to this path at a command prompt:

    C:\Users\<username>\AppData\Local\Packages\<PackageName>\

    Now run the following command:

    Attrib –H –S /S /D

    This removes the hidden and system attributes from files and folders in that location.  If successful, you may see the following folder structure:



    You can then delete the Cache, Cookies and History for the embedded WebView control in this app.

  8. How to embed a font into your app to be used by WebView

    This works for ttf fonts.  Use it inside your CSS file or <style> tag.

    @import url(ms-appx:///<fontname>.ttf)

    (10/24/12) OK, you got me.  It turns out I was wrong about how this works, so I've written a new post describing how to actually get fonts embedded in your webview.  I hope this helps.

     

  9. Launching other apps from a link inside WebView
    As with all Store applications, there is no way to directly open a specific application.  However, the concepts of default Protocol Handlers and default Extension handlers work just as well for Webview content. Links inside an application will open up using the default protocol handler or extension handler for that type. For example, when I click on a file with the extension .PDF, it automatically opens the built-in document viewer. 

  10. How to get rid of those annoying JavaScript exceptions when debugging

    When you are debugging your WebView-enhanced application, and the webpage hosted inside the WebView has a JavaScript exeception, it will throw up a horrible Debugger dialog like this one:




    It's not really a helpful dialog in the context of your C#/XAML application. You can get rid of this annoying box by disabling Script Debugging in Visual Studio:


    Debug > Options and Settings > Debugging > Just-In-Time > Uncheck "Script":




    You will no longer be bothered by that again.


I hope that this information is useful to you while you are developing your Windows Store XAML/C# application.  Comments are encouraged, and feel free to tweet to me at WinDevMatt.  Also, this blog's hashtag is #WSDevSol.