I recently researched displaying HTML from within a Silverlight 2 application. We have a huge amount of content that is currently rendered as HTML and Silverlight XAML is well suited for displaying UI elements and animations but does not have the kind of layout support we expect from HTML browsers.
Accessing the host page
A Silverlight application is hosted in an HTML page as an OBJECT element. Silverlight 2 provides an easy way to access the host page from managed code, through the HtmlPage class. Once the Silverlight plug-in is loaded, the host page document object model (DOM) can be accessed using the static HtmlPage.Document property (in the System.Windows.Browser namespace). For example, you can place HTML controls on the page and then access these controls through the DOM, create elements, set styles, attach event handlers, etc. You can register managed code to enable it to be called from script, using HtmlPage.RegisterScriptableObject (see Calling Managed Code from Client Script). You can also call script from your Silverlight code using HtmlPage.Window.Invoke (see Calling Client Script from Managed Code). You will need to set the EnableHtmlAccess property to "true" if you want to make any changes to the DOM.
Overlaying HTML
If you are building a Silverlight application but want to display existing HTML content, you can place a DIV/IFRAME pair in the host page and position the DIV over your Silverlight application. Note that the Windowless property needs to be set to "true" on the Silverlight object. The following code shows how this would be declared in an ASP.NET page.
<div style="height: 100%;"> <asp:Silverlight ID="Xaml1" runat="server" EnableHtmlAccess="true" Source="/ClientBin/HtmlOverlay.xap" Version="2.0" Width="100%" Height="100%" Windowless="true" /> <div id="HtmlContainer" class="contentHost"> <iframe id="HtmlFrame" class="contentFrame" scrolling="no" frameborder="0"></iframe> </div> </div>
Once the Silverlight plug-in is loaded, you can locate the DIV/IFRAME elements and reposition them over your application. Use an XAML panel (e.g. Grid) to position the DIV according to your XAML layout.
In your panel constructor:
this.Loaded += new RoutedEventHandler(Content_Loaded); this.SizeChanged += new SizeChangedEventHandler(Content_SizeChanged);
In Content_Loaded:
this.container = HtmlPage.Document.GetElementById("HtmlContainer"); this.containerFrame = this.container.Children[0];
In Content_SizeChanged:
UIElement root = Application.Current.RootVisual as UIElement; GeneralTransform gt = this.LayoutRoot.TransformToVisual(root); Point pos = gt.Transform(new Point(0, 0)); this.container.SetStyleAttribute("left", pos.X.ToString() + "px"); this.container.SetStyleAttribute("top", pos.Y.ToString() + "px"); this.container.SetStyleAttribute("width", this.LayoutRoot.ActualWidth.ToString() + "px"); this.container.SetStyleAttribute("height", this.LayoutRoot.ActualHeight.ToString() + "px");
This way, every time your panel is resized, the DIV is repositioned. To navigate the IFRAME to a web page, you'll need to access the window object:
ScriptObject frames = (ScriptObject)HtmlPage.Window.GetProperty("frames"); HtmlWindow wnd = frames.GetProperty(this.containerFrame.Id);
~or~
HtmlElement iframe = HtmlPage.Document.GetElementById(this.containerFrame.Id); HtmlWindow wnd = iframe.GetProperty("contentWindow") as HtmlWindow;
Then call the Navigate method on the window object:
wnd.Navigate(new Uri("http://www.microsoft.com/"));To be informed when the page is loaded:
this.containerFrame.AttachEvent("onload", new EventHandler<HtmlEventArgs>(Content_PageLoaded););
From this point, you have the same kind of control over the HTML DOM that you would have from script/DHTML. When the HTML page has loaded, you can access the document:
HtmlDocument doc = wnd.GetProperty("document") as HtmlDocument;
Note that the document object is only available if the IFRAME is navigated to a page in the same domain as your host page.