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.
Once 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.
Comments
Anonymous
December 15, 2010
As a small alteration to the above, I'd recommend using "PhoneDarkThemeVisibility" (or PhoneLightThemeVisibility or PhoneDarkThemeOpacity or PhoneLightThemeOpacity) rather than Resources["PhoneBackgroundColor"] as the conversion/comparison will be quicker/easier.Anonymous
December 15, 2010
Interesting - as I'm working lots with the browser right now for the editor in iron7. To make fixed width HTML work with orientation, I'm using: <meta name='viewport' content='width=device-width,height=device-height' /> And I'll try adding your zoom fix too. Working with javascript in a browser control is working quite well - my only big problem with "WP7 pocket IE" at the moment is debugging javascript - when it fails it's hard to work out why - there's no way I've found to get trace info out.Anonymous
December 16, 2010
Hi Mike - I'm the PM for the WebBrowser control. You don't need to make the META tags separate. You can separate the values with a comma and it will work, as Stuart has done above.Anonymous
December 16, 2010
Joe/Mike - is there any way to disable the zoom behaviour when the user clicks on a control and the keyboard comes up? Ideally I'd like to allow "pinch" zooming but disable the automated zoom. Also, where should I put any bugs I find? Currently I've got an odd one which is to do with web browser control resizing if you bring up the keyboard, then dismiss it using the back hard key, then bring up the keyboard again - the web browser control doesn't resize correctly the second time (or any time afterwards)Anonymous
December 16, 2010
@Matt - Thanks Matt. Duly noted! @Stuart / Joe Apologies for that - was using the wrong delimeter in my code. I'll correct the post. Thanks for pointing it out. Mike