How to Create a Twitter Client on Windows Phone 7

It was only days ago when I installed the Windows Phone Developer Tools CTP and began playing around with the SDK.  After reading a tutorial on ScottGu’s blog, I finally decided to try and build a small Twitter client with simple functionality like retrieving a list of feeds and updating your status.

I started by creating a Windows Phone List Application since I wanted the user to be able to navigate back and forth between the user_timeline screen and the update screen.

The first thing I noticed was that the ListBox was pre-filled with more items than I needed.  You can make changes to them by going into the ViewModels folder and editing MainViewModel.cs.


public MainViewModel() {
    Items = new ObservableCollection() {
        new ItemViewModel() { LineOne = "user_timeline", LineTwo = "20 most recent statuses posted", },
        new ItemViewModel() { LineOne = "update", LineTwo = "Updates user's status", },
    };
}

Since I wanted to keep this tutorial very simple, I added two items:

Next, I created two Windows Phone Portrait Pages: UserTimeline.xaml and Update.xaml. I needed to make sure that I would be navigating to the correct page when I click on an item in the ListBox, so I added the following code to MainPage.xaml.cs to check the index of the item selected.


private void PageTransitionList_Completed(object sender, EventArgs e) {
    // Set datacontext of details page to selected listbox item
    if (ListBoxOne.SelectedIndex == 0) {
        NavigationService.Navigate(new Uri("/UserTimeline.xaml", UriKind.Relative));
    }
    else if (ListBoxOne.SelectedIndex == 1) {
        NavigationService.Navigate(new Uri("/Update.xaml", UriKind.Relative));
    }
    else {
        // error handling here
    }

    FrameworkElement root = Application.Current.RootVisual as FrameworkElement;
    root.DataContext = _selectedItem;
}

I would then get a preview that looks something like this:

001

Setting up the navigation seemed easy enough. Let’s move on to implementing the feed retrieval. I started by adding a TextBox, a Button and a ListBox to UserTimeline.xaml.






Double-click on the button to automatically create the event handler for the button’s click event. Here, we will create an instance of the WebClient class and download a Twitter feed asynchronously. This is very simple since Twitter provides the user_timeline method for you in their REST API.


private void btnSubmit_Click(object sender, RoutedEventArgs e) {
    textBlockPageTitle.Text = "loading feed...";
    WebClient twitter = new WebClient();
    twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
    twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=" + tbUsername.Text));
}

Remember to add the DownloadStringCompleted event handler before you make a call to DownloadStringAsync. The DownloadStringCompletedEventHandler will fire after the feed is returned. From within this event handler, we can parse the XML reponse by using LINQ to XML.


void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) {
    try {
        XElement element = XElement.Parse(e.Result);
        lbContent.ItemsSource = from tweet in element.Descendants("status")
                                select new Tweet {
                                    UserName = tweet.Element("user").Element("screen_name").Value,
                                    Message = tweet.Element("text").Value,
                                    ImageSource = tweet.Element("user").Element("profile_image_url").Value,
                                    TweetSource = FormatTimestamp(tweet.Element("source").Value, tweet.Element("created_at").Value)
                                };
        textBlockPageTitle.Text = tbUsername.Text;
    }
    catch (Exception ex) {
        textBlockPageTitle.Text = "ERROR: " + ex.Message;
    }
}

If you notice from the above code, I wrote a helper class called Tweet which contains 4 properties: UserName, Message, ImageSource, and TweetSource. These are the items that I would want to display for each Twitter update in my ListBox.


public class Tweet {
    public string UserName { get; set; }
    public string Message { get; set; }
    public string ImageSource { get; set; }
    public string TweetSource { get; set; }
}

Since the “source” element returned by the XML response may contain URL encoded characters and HTML tags, I used the following method to properly decode this string and strip all HTML tags using a regular expression.


private string StripHtml(string val) {
    return Regex.Replace(HttpUtility.HtmlDecode(val), @"<[^>]*>", "");
}

The format of the date in the “created_at” element returned by the XML response would have to be parsed correctly as well. Below is an example of how I parsed the string into a DateTime object with a specified format.


private string DateFormat(string val) {
    string format = "ddd MMM dd HH:mm:ss zzzz yyyy";
    DateTime dt = DateTime.ParseExact(val, format, CultureInfo.InvariantCulture);
    return dt.ToString("h:mm tt MMM d");
}

Finally, this method below formats the timestamp into something that’s more similar to what you see on Twitter’s site (e.g. 11:05 AM Mar 24th via API)


private string FormatTimestamp(string imageSource, string date) {
    return DateFormat(date) + " via " + StripHtml(imageSource);
}

Going back to UserTimeline.xaml, I added a ListBox template that formats all the data from the XML reponse that’s similar to Twitter’s site. Since we set the DataSource of our ListBox to point to an instance of our Tweet class, we can bind these values by using meta tags like: {Binding UserName}, {Binding Message}, etc.



    
        
            
                
                
                    
                    
                    
                
            
        
    


That’s all there is too it. Once you compile and run the application in the emulator, you should get something that looks like this:

002

(update status blog goes here)

By: ken on: