Displaying HTML Content in Windows Phone 7

In the run up to the launch of Windows Phone 7, we did a lot of work with customers building early applications to help them bring their app to market for launch and to provide guidance and assistance in terms of application design and compliance with the Application Certification Requirements.

TelegraphOnce such application was the Telegraph Fashion app from Telegraph Media Group. They spent some time with Martin Beeby and Dave Brown in our Microsoft Technology Centre in Reading and they hit a particular problem trying to display rich HTML content.

The information displayed in the app comes from an RSS feed which contains formatted HTML content. The obvious way to display this in a Silverlight application is to use a WebBrowser control and NavigateToString() passing the content to be displayed.

Adopting this approach, the team immediately hit upon a couple of problems:

  • It’s not possible to make the background of a WebBrowser control transparent
  • The customer wanted to disable zooming in the browser

 

Let’s take those one at a time. It was desirable to make the WebBrowser control appear transparent so it would match the user selected theme. A white browser on a black background and vice-versa was to be avoided. In order to make the WebBrowser control “match” with the current theme, the team came up with some detection code to check the current background colour. This was converted to a value that could be used to style the page background in CSS.

Here’s the code – I think it could perhaps be refactored a bit to simplify things – but the essence is that when the WebBrowser control loads, we determine what font colour and background colour should be used and then set those in the string passed to the WebBrowser control (htmlConcat). Ignore the script for now – we’ll come to that next.

 private string FetchBackgroundColor()
{
  return IsBackgroundBlack() ? "#000;" : "#fff";
}

private string FetchFontColor()
{
  return IsBackgroundBlack() ? "#fff;" : "#000";
}

private static bool IsBackgroundBlack()
{
  return FetchBackGroundColor() == "#FF000000";
}

private static string FetchBackGroundColor()
{
  string color;
  Color mc =
    (Color)Application.Current.Resources["PhoneBackgroundColor"];
  color = mc.ToString();
  return color;
}

private void wb1_Loaded(object sender, RoutedEventArgs e)
{
  string fontColor = FetchFontColor();
  string backgroundColor = FetchBackgroundColor();

  SetBackground();

  var html = "<p>Lorem ipsum dolor sit amet, consectetur " + 
    "adipiscing elit. Mauris sit amet dignissim purus. " + 
    "Pellentesque habitant morbi tristique senectus et " +
    "netus et malesuada fames ac turpis egestas. " +
    "Curabitur ante mauris, tempor congue lobortis id, " +
    "gravida nec mi. Sed laoreet neque eget lacus " +
    "vestibulum vel euismod sapien elementum. Maecenas " +
    "malesuada, orci id facilisis volutpat, dui dui " +
    "cursus nulla, luctus congue magna ligula sed urna." +
    "</p>" + 
    "<p>Suspendisse luctus rutrum quam non rutrum. " +
    "Maecenas sed mauris id metus sodales lobortis eu sit " +
    "amet nibh. Lorem ipsum dolor sit amet, consectetur " +
    "adipiscing elit. Donec convallis vehicula lacinia. " +
    "Duis blandit vestibulum tristique. Morbi tincidunt " +
    "lacinia condimentum. Morbi quis ipsum lorem, mollis " +
    "lobortis quam. Curabitur ac lectus justo, non " +
    "placerat sapien. Integer non sem nec elit fermentum " +
    "placerat. Vivamus id metus quam. Aliquam erat " +
    "volutpat. Cras et mauris cursus lectus dignissim " +
    "commodo varius nec ligula.</p>";

  var htmlScript = "<script>function getDocHeight() { " +
    "return document.getElementById('pageWrapper').offsetHeight;" + 
    "}" +
    "function SendDataToPhoneApp() {" +
    "window.external.Notify('' + getDocHeight());" +
    "}</script>";

  var htmlConcat = string.Format("<html><head>{0}</head>" + 
    "<body style=\"margin:0px;padding:0px;background-color:{3};\" " +
    "onLoad=\"SendDataToPhoneApp()\">" +
    "<div id=\"pageWrapper\" style=\"width:100%; color:{2}; " +
    "background-color:{3}\">{1}</div></body></html>", 
    htmlScript, 
    html, 
    fontColor, 
    backgroundColor);
  webBrowser1.NavigateToString(htmlConcat);
  webBrowser1.IsScriptEnabled = true;
  webBrowser1.ScriptNotify += 
    new EventHandler<NotifyEventArgs>(wb1_ScriptNotify);
}

private void SetBackground()
{
  Color mc =
    (Color)Application.Current.Resources["PhoneBackgroundColor"];
  webBrowser1.Background = new SolidColorBrush(mc);
}

For the 2nd issue, the initial approach was to disable hit testing on the WebBrowser control (set IsHitTestVisible=”False”). Of course this disabled scrolling as well so the WebBrowser control was embedded in a ScrollViewer. The challenge now was to set the WebBrowser control height to the height of the content.

Two ways of doing this were considered. The first is to write a function that measures the length of the string, but this could be complicated as we would have to work out the height of the images that may also be included in the feed.

The other was to pass the content to the WebBrowser control then use a JavaScript function running in the control to measure the height of the content, pass this info back to Silverlight and then set the height of the WebBrowser control. This is what the script does in the code above. The ScriptNotify handler looks like this:

 private void wb1_ScriptNotify(object sender, NotifyEventArgs e)
{
  // The browser is zooming the text so we need to
  // reduce the pixel size by the zoom level... 
  // Which is about 0.50
  webBrowser1.Height = Convert.ToDouble(e.Value) * 0.50;
}

This worked fine until dealing with longer bodies of content; when the height of the WebBrowser control was set to more than 1800px the the application would crash. As it turned out, there’s a much easier way to achieve the same effect.

Instead of disabling hit testing on the WebBrowser control (and then embedding it in a ScrollViewer to re-enabled the scrolling experience), it’s possible to set meta tags in the HTML to declare that the user should not be able to zoom the content.

 <meta name="viewport" content="width=320" />
<meta name="viewport" content="user-scalable=no" />

or

 <meta name="viewport" content="width=320,user-scalable=no" />

These meta tags set the width of the viewport to 320px (to avoid horizontal scrolling) and specify that the user should not be able to scale the viewport. Add these meta tags to the HTML injected into the WebBrowser control and the desired behaviour is achieved.