Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control

Blogs

Mike Taulty's Blog

Elsewhere

Note – these posts are put together after a short time with Silverlight 4 as a way of providing pointers to some of the new features that Silverlight 4 has to offer. I’m posting these from the PDC as Silverlight 4 is announced for the first time so please bear that in mind when working through these posts.

Until version 4, Silverlight does not really have a mechanism for displaying HTML based content.

Prior to version 4, you can have Silverlight controls and HTML UI co-existing in the browser and they can interoperate with each other in a rich way. You can even use transparency and windowless techniques to overlay them but you can’t really ask a Silverlight application to display HTML for you. You always have to get it done elsewhere and, furthermore, if you then packaged your application to run as a Silverlight Out-Of-Browser (SLOOB) app then those techniques wouldn’t help as you lose the ability to display HTML at all.

That changes with version 4 with the new WebBrowser control ( WPF also has a Frame and a WebBrowser control which both offer levels of HTML hosting ability – the Silverlight WebBrowser looks like a subset of the WPF version ).

The first thing to say about the WebBrowser control is that it only functions in Silverlight Out-Of-Browser applications ( SLOOBs ) – in the browser the control displays a blank UI so an application developer/designer would need to think about how that would work if the application can also be run within a browser window.

For an out-of-browser application, I can put a simple UI together;

<UserControl x:Class="SilverlightApplication16.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid
            x:Name="gridControls">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition
                    Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBox
                x:Name="txtUri"
                Margin="5"
                HorizontalAlignment="Stretch" />
            <Button
                x:Name="buttonGo"
                Grid.Column="1"
                Content="Go"
                Click="OnGo"
                Margin="5" />
        </Grid>
        <WebBrowser
            LoadCompleted="BrowserLoadCompleted"
            Grid.Row="1"
            Margin="5"
            x:Name="browserControl" />
    </Grid>
</UserControl>

and then add a little code-behind that;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightApplication16
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();
    }
    private void OnGo(object sender, RoutedEventArgs e)
    {
      buttonGo.IsEnabled = false;

      browserControl.Navigate(new Uri(txtUri.Text, UriKind.Absolute));
    }
    private void BrowserLoadCompleted(object sender, EventArgs e)
    {
      buttonGo.IsEnabled = true;
    }
  }
}

and ( out of browser ) that allows me to navigate to some HTML;

image

What might not be immediately obvious is that myPage.htm is part of the same web-site that the application originally came from ( i.e. site-of-origin ) and that an attempt to navigate to any site might fail ( subject to cross-domain policy ).

That can be circumvented by having the application also elevate itself. If the application is elevated then cross-domain policy isn’t applied and so we can go anywhere as in the example below ( the application now having been installed as an elevated SLOOB );

image

You might notice that in this example this is me with my out-of-browser application visiting Microsoft.com which is made up of HTML and Silverlight. So…we have one Silverlight application hosting HTML which hosts another Silverlight application. Similarly, I can visit sites that have “other plugins” like the Flash site below;

image

I’m not sure how much/little this control is integrated into the Silverlight rendering engine. I tried ( for fun ) applying a PlaneProjection to it and that didn’t really work out so I’m not sure that you’re meant to use the control in this way ( more on doing fancy things with HTML without trying to bend the control in a moment but back to regular stuff for a second… )

What you can also get the control to do is load HTML at runtime via its “NavigateToString” method so if I just change my code;

    private void OnGo(object sender, RoutedEventArgs e)
    {
      buttonGo.IsEnabled = false;

      browserControl.NavigateToString(txtUri.Text);
    }

then I can just type HTML straight into the textbox and that works out fine ( note – the application does not have to be elevated for this to work );

image

Now, in the WPF version of this control there are two other “interesting” functions on the control. One is called InvokeScript and that exists on the Silverlight control and can be used to invoke a piece of script inside the control.

This isn’t intended to work if you use the Navigate method but there’s another way to set the URL which is the Source property and so I can re-jig my code a little bit such that the code-behind looks like;

  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();
    }
    private void OnGo(object sender, RoutedEventArgs e)
    {
      buttonGo.IsEnabled = false;
      browserControl.Source = new Uri(txtUri.Text, UriKind.Absolute);
    }
    private void BrowserLoadCompleted(object sender, EventArgs e)
    {
      browserControl.InvokeScript("ChangeText", "Done");
      buttonGo.IsEnabled = true;
    }
  }

and if I then edit the HTML that I want to load so it looks like;

<html>
<head>
<script type="text/javascript">
    function ChangeText(text) {
        document.getElementById("myDiv").innerText = text;
    }
    </script>
</head>
<body>
<div id="myDiv">Hello World</div>
</body>
</html>

then when I load up that particular HTML file with the WebBrowser control I get the expected response straight away;

image

and so the Silverlight control loads the HTML and then runs the script which changes the DOM inside of the HTML. Note that this isn’t possible for cross-domain content.

It’s also possible to communicate “back” from the hosted Javascript to the Silverlight control although it’s not as functional as it is in WPF. If I re-work my HTML code to look like;

<html>
<head>
<script type="text/javascript">
    function buttonClick()
    {
        window.external.Notify("They clicked!");
    }
    </script>
</head>
<body>
    <input type="button" value="test" onclick="buttonClick()" />
</body>
</html>

and then change my Silverlight UI a little to add a new textbox;

<UserControl x:Class="SilverlightApplication16.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition
                Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid
            x:Name="gridControls">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition
                    Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBox
                x:Name="txtUri"
                Margin="5"
                HorizontalAlignment="Stretch" />
            <Button
                x:Name="buttonGo"
                Grid.Column="1"
                Content="Go"
                Click="OnGo"
                Margin="5" />
        </Grid>
        <TextBlock
            x:Name="txtStatus"
            Margin="5"
            Text="Not set"
            Grid.Row="1" />
        <WebBrowser
            LoadCompleted="BrowserLoadCompleted"
            Grid.Row="2"
            Margin="5"
            x:Name="browserControl"
            ScriptNotify="OnScriptNotify">
        </WebBrowser>
    </Grid>
</UserControl>

with code-behind;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightApplication16
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();
    }
    private void OnGo(object sender, RoutedEventArgs e)
    {
      buttonGo.IsEnabled = false;
      browserControl.Source = new Uri(txtUri.Text, UriKind.Absolute);
    }
    private void BrowserLoadCompleted(object sender, EventArgs e)
    {
      buttonGo.IsEnabled = true;
    }
    private void OnScriptNotify(object sender, NotifyEventArgs e)
    {
      txtStatus.Text = e.Value;  
    }
  }
}

then that allows the hosted Javascript to communicate through the WebBrowser control via the call to window.external.Notify() which gets picked up on the .NET side by the ScriptNotify event on the WebBrowser control and I just handle it in a little handler and change the text on my txtStatus TextBlock to represent whatever was passed from Javascript ( this is running out-of-browser but not elevated ).

The other “interesting” aspect of the HTML support is that Silverlight 4 also has an HTMLBrush which can take HTML content and paint areas so, whilst my previous attempt to integrated the WebBrowser control with the PlaneProjection system didn’t work very well, I can paint arbitrary areas with a brush. So, if I’ve got a little UI;

<UserControl
    x:Class="SilverlightApplication18.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Grid
        x:Name="LayoutRoot"
        Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <WebBrowser
            x:Name="myBrowser"
            Grid.RowSpan="2" />
        <Rectangle
            Grid.Column="1"
            Margin="10"
            RadiusX="10"
            RadiusY="10">
            <Rectangle.Fill>
                <HtmlBrush
                    x:Name="myBrush"
                    SourceName="myBrowser" 
                    Stretch="Fill"/>
            </Rectangle.Fill>
        </Rectangle>
        <Button
            Grid.Column="1"
            Content="Redraw"
            Grid.Row="1"
            Margin="5"
            Click="OnRedraw" />
    </Grid>
</UserControl>

with some code behind it ( note – I feel I should be able to set the WebBrowser.Source property from XAML but it’s not working for me at the time of writing );

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightApplication18
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();

      this.Loaded += (s, e) =>
        {
          myBrowser.Source = new Uri("http://www.microsoft.com", UriKind.Absolute);
        };      
    }
    private void OnRedraw(object sender, RoutedEventArgs e)
    {
      myBrush.Redraw();
    }
  }
}

then that gives me a UI where I’m painting the rectangle on the right hand side with that HtmlBrush pointing to the WebBrowser control on the left hand side;

image

Note – this isn’t a VisualBrush and so it doesn’t do “live updates”. You’ll notice the Redraw button and the call to HtmlBrush.Redraw and that’s the mechanism for keeping things in sync so it’s more like a snapshot than anything else but pretty interesting regardless.


Posted Wed, Nov 18 2009 10:52 AM by mtaulty
Filed under: ,

Comments

CoderX wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 12:18 PM

Limiting this HTML control to OOTB is a deal-killer! And I was so excited about this feature. How so very, very disappointing. Well, I guess there's always hope for Silverlight 5... excuse me while I go weep and mope... :{

mtaulty wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 3:40 PM

Take your point - it'd be _nice_ if this worked in both places but it does allow you to get HTML into your out-of-browser apps and you can achieve the same sort of thing in-the-browser through HTML techniques. So, it makes more scenarios possible than before which I'd take as a good thing :-)

Kevin Daly wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 4:34 PM

While it would certainly be nice to have this in-browser, I can also understand that having a browser session host a plug-in that itself hosts a browser session could be um, problematical.

James wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 4:49 PM

It's a pity to limit the WebBrowser only available for out-of-browser. Browser application also needs to embed HTML content inside Silverlight.

Michael wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 8:10 PM

===

Now, in the WPF version of this control there are two other “interesting” functions on the control. One is called InvokeScript and that exists on the Silverlight control and can be used to invoke a piece of script inside the control.  

...

It’s also possible to communicate “back” from the hosted Javascript to the Silverlight control although it’s not as functional as it is in WPF.

====

May I ask what functions are missing in SL4 comparing to WPF. Or, perhaps,  a link may be provided ?

Dennis wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Wed, Nov 18 2009 10:36 PM

I totally agree with CoderX , it is almost totally useless when it is only available in OOTB.

One of the biggest scenarios I can see with this is to gradually convert a large web application over to silverlight, but if it is only available in OOTB mode, that scenario isnt even available.

mtaulty wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Thu, Nov 19 2009 9:56 AM

Michael - I don't have an exact list of what's different between the SL and WPF versions right now.

Chris wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Thu, Nov 19 2009 1:55 PM

This is terrible that it is not available only in OOTB mode. Things like google AdSense would have been simple to inject in a silverlight app if it ran in browser mode. I hope they add this before Silverlight 4 is released....

Dmitry Lyalin wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Thu, Nov 19 2009 1:57 PM

I am not sure why everyone is calling the OOTB requirement a deal killer, I already am prototyping some very interesting scenarios within the current limitations, its NOT USELESS :)

Wardy wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Sat, Nov 21 2009 8:01 AM

Is there any chance that this will implemented so that I can run this within a browser?

I'm trying to build a CMS using silverlight and this would be perfect if i could use it.

ChrisTorng wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Sun, Nov 22 2009 7:33 PM

Does the WebBrowser control still work in Mac/Linux with Safari/FireFox? And window.external.Notify still work? How can I enable the autocomplete function of <input type="text"/> ? Can I have more way to control the WebBrowser control? like zoom...

And I also hope this control can run in browser, or it will prevent my app run inside the browser...

ChrisTorng wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Sun, Nov 22 2009 11:27 PM

Does the WebBrowser control still work in Mac/Linux with Safari/FireFox? And window.external.Notify still work? How can I enable the autocomplete function of <input type="text"/> ? Can I have more method/event to control the WebBrowser control?

And I also hope this control can run in browser, or it will prevent my app run inside the browser...It will become useless for my requirement...

mtaulty wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Mon, Nov 23 2009 5:09 AM

Chris - yes, as far as I know this is supported on the Mac with the same functionality as on the Windows version.

Mike.

xavi wrote re: Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
on Tue, Dec 15 2009 2:56 AM

I agree, it is not useful in most of scenarios. I was waiting this feature because our report generator only generates html code and I wanted to inject this code in a Silverlight page. Since this component only works out of browser, I am in the same position than before.

I hope that they will add this feature in the release version.