Monday, April 28, 2014

Formatting text to view in Windows Phone Internet Explorer

Sometimes you need to present formatted text to users in your apps, and it's a pretty big pain to implement your own formatting engine.  Rich Text is sometimes an option, but if you are starting with HTML formatted data then the obvious choice is a WebBrower control (even if it is a little heavy as a control).  You probably already know how to use the NavigateToString() method to load am HTML formatted string.  If not, there's little to it:

ctrl.NavigateToString("<i>This should be in italics</i>");

Notice it's from code, not something you can do from XAML.  The problem is that you end up with a plain white background and black text, regardless of phone theme.  If you'd like the text to match the phone theme, all it takes is a little CSS magic!

Theme Colors

The first step is to get the theme colors.  There are typically two colors you want: the background and the foreground.  These (and other built-in static style/theme resources) can be retrieved with the Resources collection attached to the application: App.Current.Resources

What you want are the SolidColorBrush resources named PhoneBackgroundBrush and PhoneForegroundBrush.  Anything you get from the Resources collection is an object, so you need to cast them accordingly:

SolidColorBrush bg = (SolidColorBrush)App.Current.Resources["PhoneBackgroundBrush"],
    fg = (SolidColorBrush)App.Current.Resources["PhoneForegroundBrush"];

CSS

The next step is to create a CSS snippet to reference the color values.  This ensures that you don't need to modify the original HTML string directly.  All you set is color and background-color.  This will affect any text that doesn't have its own local style override.  The CSS looks like this:

<style type='text/css'>body {{color: rgb({0}, {1}, {2}); background-color: rgb({3}, {4}, {5}); font-size: inherit; font-family:Segoe WP}}</style>

I couldn't find a simple way to spit out the #RRGGBB color, so I made it easy on myself and used the CSS rgb() function to take the three values separately.

HTML

Finally, just wrap the original text with the CSS and a basic HTML body.  Obviously if you are working with HTML that is already a complete document, it won't be this simple, but this works great with snippets!

var html = "<html><head><meta name=\"viewport\" content=\"width=device-width\" /><title></title><style type='text/css'>body {{color: rgb({0}, {1}, {2}); background-color: rgb({3}, {4}, {5}); font-size: inherit; font-family:Segoe WP}}</style></head><body>{6}</body></html>";

return string.Format(html, fg.Color.R, fg.Color.G, fg.Color.B, bg.Color.R, bg.Color.G, bg.Color.B, txt);

For my needs, I found it useful to include a viewport declaration too.  This ensures that the content properly fills the screen and doesn't need to be user-scaled right away.  You may or may not need it.

SOLUTION

The final version can be made into a simple function to pass the HTML to, then to the control:

string PrepareText(string txt)
{
    SolidColorBrush bg = (SolidColorBrush)App.Current.Resources["PhoneBackgroundBrush"],
        fg = (SolidColorBrush)App.Current.Resources["PhoneForegroundBrush"];

    var html = "<html><head><meta name=\"viewport\" content=\"width=device-width\" /><title></title><style type='text/css'>body {{color: rgb({0}, {1}, {2}); background-color: rgb({3}, {4}, {5}); font-size: inherit; font-family:Segoe WP}}</style></head><body>{6}</body></html>";
    return string.Format(html, fg.Color.R, fg.Color.G, fg.Color.B, bg.Color.R, bg.Color.G, bg.Color.B, txt);
}

var html = PrepareText("<i>This should be in italics</i>");
web.NavigateToString(html);

NEXT STEPS

This is a pretty good way to get HTML-formatted text to look more like native text in an app. If you are going for an ebook reader type of text presentation, then you might want to also create a helper class for this and create properties for the foreground and background colors.  That would make it easier to support a cream background, or a sepia foreground for better readability.