Skip to main content Skip to footer

Display RSS/Atom Feeds using C1RichTextBox

In this post I show you one of the neat usages of the C1RichTextBox control for the Windows Phone platform. C1RichTextBox is essentially an HTML editor; it displays and allows editing HTML content. Many social media sites, such as Facebook and Twitter, supply RSS/Atom feeds that contain blocks of HTML content. So you can use C1RichTextBox as a quick and simple way to display this rich content - complete with clickable hyperlinks and images. It’s quite possible for one Facebook or Twitter entry to have multiple URLs and images within them, so you can’t rely on a template solution just using a ListBox. In this post I will demonstrate how you can display an RSS feed from Facebook in a Windows Phone app using C1RichTextBox.

Step 1 – Obtain the feed URL

In this sample I will use the public ComponentOne Facebook feed. It will always have the last 20 posts made by ComponentOne. Here is the feed in Atom 1.0 format: http://www.facebook.com/feeds/page.php?id=131214763781&format=atom10 Facebook constantly changes their settings and interfaces, so there’s no point in even showing how I got the URL. Actually, from the time I wrote this sample to now writing this blog post, they changed it so I couldn’t even tell you if I wanted to. The easiest way is to take my URL above and replace the id number with your accounts id, and that should work. As a side note, there are two common feed formats supported by Facebook: Atom 1.0 and RSS 2.0. Here is the same link but using the RSS 2.0 format: http://www.facebook.com/feeds/page.php?id=131214763781&format=rss20 Originally when I wrote this sample I was using the Silverlight System.ServiceModel.Syndication classes to parse and read the RSS 2.0 format. Unfortunately it had issues with the rss20 feed from Facebook, so through trial and error I was forced to use the Atom format. I later removed my dependency on the Silverlight Syndication classes by using System.Xml.Linq instead. So long story short that is why I chose the Atom format. You can use either as it would only require some small code changes later on.

Step 2 – Read Content Asynchronously

If you’ve written any Windows Phone app before, you’ve probably made at least one asynchronous request. Microsoft makes it very easy to perform these types of requests from any .NET application. For example, the WebClient class provides asynchronous methods for fetching web pages and strings. Here is a code snippet of using a WebClient to download our Facebook content.


private const string FACEBOOK_RSS = "http://www.facebook.com/feeds/page.php?id=131214763781&format=atom10";  
private const string NAMESPACE = "http://www.w3.org/2005/Atom";  

// Constructor  
public MainPage()  
{  
    InitializeComponent();  

    // get facebook feed  
    WebClient client = new WebClient();  
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(facebook_DownloadStringCompleted);  
    client.DownloadStringAsync(new Uri(FACEBOOK_RSS));  
}  

private void facebook_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)  
{  

}  

Within the completed event, the e.Result parameter holds our content. Now we just need to just display it using C1RichTextBox. The NAMESPACE string will be used later to help with our linq code.

Step 3 – Setting up the UI

Add a C1RichTextBox control to your page. A few of the key properties we need to set here are:

  • IsReadOnly = “True” - we don’t need editing capability
  • Background=”{x:Null}” - I prefer a transparent background but this is optional
  • DisableSelection=”True”- we don’t require selection

Also add a C1ProgressBar to the page and set its IsIndeterminate property True. This will display as the content is downloading from the web to provide a nice user experience.


<my:C1RichTextBox Name="c1RichTextBox1"  
                          IsReadOnly="True"  
                          Background="{x:Null}"  
                          RequestNavigate="c1RichTextBox1_RequestNavigate"  
                          DisableSelection="True"/>  
<my1:C1ProgressBar Name="c1ProgressBar1"  
                           IsIndeterminate="True" />  

Also, I’ve set up the RequestNavigate event. This will enable us to capture the user clicking hyperlinks later.

Step 4 – Parsing the feed into C1RichTextBox

We can’t just set C1RichTextBox.Text = e.Result in the data completed event. We need to extract the data we want from the feed first. If you were to examine the XML from the feed you would notice that each item has a tag named “entry” and each entry has its content within a “content” tag. For this sample I will also take the “published” attribute for the date and time, but the rest of the data is not needed for this example. Here is a very basic code snippet for parsing the Atom feed into a C1RichTextBox.


private void facebook_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)  
{  
    if (e.Error != null)  
    {  
        MessageBox.Show("Facebook RSS down at the moment. Try again later.");  
    }  
    else if (!string.IsNullOrEmpty(e.Result))  
    {  
        var result = XElement.Parse(e.Result);  
        var feeds = from entry in result.Elements("{" + NAMESPACE + "}entry") select entry;  

        // build html content  
        string html = "";  
        foreach (XElement xe in feeds)  
        {  
            html += xe.Element(XName.Get("content", NAMESPACE)).Value;  
            html += "<br/>===============================<br/>";  
        }  

        // set total html to C1RTB  
        c1RichTextBox1.Html = html;  
    }  
}  

You will need to add a reference to the System.Xml.Linq assembly. We can use System.Xml.Linq classes (XElement) to parse the XML feed. We cycle through the feed entries and build a long string of HTML. Then we set the HTML to the Html property of C1RichTextBox. It’s that simple. If you are using Twitter or a different feed format like RSS 2.0, there might be some differences in the XML and namespace.

Step 5 – Formatting the Html content

If you just run that code you will not see much because the Text color is Black by default. C1RichTextBox is not completely like a regular TextBox, ie the font color is determined by CSS, so we need to apply a bit of style to our HTML. Open up your App.xaml.cs file and add the following public static properties. These will get the phone’s Foreground color and Accent color. We will use this to style the Html content appropriately, making use of the accent color for hyperlinks.


private static Color foregroundColor;  
private static Color accentColor;  

/// <summary>  
/// Returns the user defined forecolor theme  
/// </summary>  
public static Color ForegroundColor  
{  
    get  
    {  
        if (foregroundColor.A == 0 && foregroundColor.B == 0 && foregroundColor.G == 0 && foregroundColor.R == 0)  
            foregroundColor = (Color)Application.Current.Resources["PhoneForegroundColor"];  

        return foregroundColor;  
    }  
}  

/// <summary>  
/// Returns the user defined accent theme  
/// </summary>  
public static Color AccentColor  
{  
    get  
    {  
        if (accentColor.A == 0 && accentColor.B == 0 && accentColor.G == 0 && accentColor.R == 0)  
            accentColor = (Color)Application.Current.Resources["PhoneAccentColor"];  

        return accentColor;  
    }  
}  

Here is the updated DownloadStringCompleted event with the style and publish date added.


private void facebook_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)  
{  
    if (e.Error != null)  
    {  
        MessageBox.Show("Facebook RSS down at the moment. Try again later.");  
    }  
    else if (!string.IsNullOrEmpty(e.Result))  
    {  
        var result = XElement.Parse(e.Result);  
        var feeds = from entry in result.Elements("{" + NAMESPACE + "}entry") select entry;  

        // build html content  
        string html = "<style type=\\"text/css\\"> body { color: #" +  
            Utils.ColorToHexString(App.ForegroundColor) + " } a { color: #" + Utils.ColorToHexString(App.AccentColor) + " } </style>";  
        foreach (XElement xe in feeds)  
        {  
            html += xe.Element(XName.Get("content", NAMESPACE)).Value;  
            html += "<br/><span style='color: #9C9A9C'>Posted ";  
            string published = xe.Element(XName.Get("published", NAMESPACE)).Value;  
            DateTimeOffset dateOffset;  
            if (DateTimeOffset.TryParse(published, out dateOffset))  
            {  
                html += _fdc.Convert(dateOffset.UtcDateTime, typeof(string), null, null);  
            }  
            html += " by ComponentOne</span><br/>===============================<br/>";  
        }  

        // set total html to C1RTB  
        c1RichTextBox1.Html = html;  

        // collapse progress bar  
        c1ProgressBar1.Visibility = System.Windows.Visibility.Collapsed;  
    }  
}  

There are two small converters being used here. One converts the UtcDateTime into a social-media-friendly string like “6 hours ago” rather than 6/4/2012 12:00:00 AM. The other converts a Color object to a Hex string for the CSS style. Add the FriendlyDateTimeConverter class somewhere in your project, and instantiate an instance on the page containing your feed parsing code.


private FriendlyDateTimeConverter _fdc = new FriendlyDateTimeConverter();  

/// <summary>  
/// Converts a DateTime value to a friendly string like "8 hours ago" using UTC time  
/// </summary>  
public class FriendlyDateTimeConverter : IValueConverter  
{  
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)  
    {  
        if (value != null && value.GetType().Equals(typeof(DateTime)))  
        {  
            string totalTime = "";  
            TimeSpan ts = DateTime.UtcNow - (DateTime)value;  

            if (ts.Days > 1)  
            {  
                totalTime = totalTime + ts.Days.ToString() + " days ";  
            }  
            else if (ts.Days == 1)  
            {  
                totalTime = totalTime + "1 day ";  
            }  
            else  
            {  
                if (ts.Hours > 1)  
                {  
                    totalTime = totalTime + ts.Hours.ToString() + " hours ";  
                }  
                else if (ts.Hours == 1)  
                {  
                    totalTime = totalTime + "1 hour ";  
                }  
                else  
                {  
                    totalTime = totalTime + ts.Minutes.ToString() + " mins ";  
                }  
            }  

            totalTime = totalTime + "ago";  
            return totalTime;  
        }  
        else  
        {  
            return value.ToString();  
        }  
    }  

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)  
    {  
        throw new NotImplementedException();  
    }  
}  

Since CSS styles want Hex color codes, we need to also convert our Color object to a Hex string. The following ColorToHexString converter will do the trick.


public static class Utils  
{  
    static char[] hexDigits = {  
     '0', '1', '2', '3', '4', '5', '6', '7',  
     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};  
    /// <summary>  
    /// Convert a .NET Color to a hex string.  
    /// </summary>  
    /// <returns>ex: "FFFFFF", "AB12E9"</returns>  
    public static string ColorToHexString(Color color)  
    {  
        byte[] bytes = new byte[3];  
        bytes[0] = color.R;  
        bytes[1] = color.G;  
        bytes[2] = color.B;  
        char[] chars = new char[bytes.Length * 2];  
        for (int i = 0; i < bytes.Length; i++)  
        {  
            int b = bytes[i];  
            chars[i * 2] = hexDigits[b >> 4];  
            chars[i * 2 + 1] = hexDigits[b & 0xF];  
        }  
        return new string(chars);  
    }  
}  

Now our Facebook feed is nicely formatted and styled for any theme+accent the user chooses!

Step 6 – Handle Hyperlinks within the content

One of the main benefits of using C1RichTextBox to display RSS/Atom feeds is that it handles hyperlink creation and interaction for you. The only thing you need to do is subscribe to the RequestNavigate event and then go where you want. You could launch a separate page in your app that contains a WebBrowser control. Or you could use the WebBrowserTask class and launch the native Internet Explorer app on the users phone. I prefer the second option.


private void c1RichTextBox1_RequestNavigate(object sender, C1.Phone.RichTextBox.RequestNavigateEventArgs e)  
{  
    if (e.Hyperlink.NavigateUri.IsAbsoluteUri)  
    {  
        App.WebPageNavigate(e.Hyperlink.NavigateUri.AbsoluteUri);  
    }  
    else  
    {  
        // handle relative facebook urls  
        App.WebPageNavigate("http://www.facebook.com" + e.Hyperlink.NavigateUri.OriginalString);  
    }  
}  

The e.Hyperlink.NavigateUri contains the requested hyperlink address. The only catch is that some of the links may be relative to www.facebook.com, so we check for that and handle it appropriately. For this sample I wrote a single WebPageNavigate method in my App.xaml.cs file.


public static void WebPageNavigate(string path)  
{  
    var result = MessageBox.Show("This app is requesting to navigate to " + path, "Navigate", MessageBoxButton.OKCancel);  
    if (result == MessageBoxResult.OK)  
    {  
        // Create a new WebBrowserTask Launcher to navigate to the feed item.  
        WebBrowserTask webBrowserTask = new WebBrowserTask();  
        webBrowserTask.Uri = new Uri(path, UriKind.Absolute);  
        webBrowserTask.Show();  
    }  
}  

If you want you can remove the message box and just navigate immediately. I think sometimes people may accidentally tap a link so the confirmation can be nice.

Conclusion

Since the C1RichTextBox control can display Html content as native XAML, it can be a great tool for adding custom Html solutions to any XAML application. The same code could also work in Silverlight and WPF. It gives you the added benefit of being able to display rich, interactive html content without having to sacrifice and use a simple “text-here/link-there” template approach as is common with a ListBox. You can download the completed WP7 sample below. Download Sample To build the sample you will also need the C1RichTextBox and C1ProgressBar components. You need to download and install Studio for Windows Phone from the link below. http://www.componentone.com/wp7/ Note that a bug was recently fixed related to handling the hyperlinks so you should use a build of C1RichTextBox higher than 7.1.20121.246.

ComponentOne Product Manager Greg Lutz

Greg Lutz

comments powered by Disqus