Build a Simple Twitter client using Silverlight 2

Posted by: Shoban Kumar , on 3/20/2009, in Category Silverlight 2, 3, 4 and 5
Views: 47698
Abstract: In this article I will show you how to build a simple Twitter client in Silverlight. We will also learn the shortcomings of WebClient Class in Silverlight.
Build a Simple Twitter client using Silverlight 2
 
In this article I will show you how to build a simple Twitter client in Silverlight. We will also learn the shortcomings of WebClient Class in Silverlight. ScottGu’s Silverlight 2 End to End Tutorial: Building a Digg Search Client is a wonderful article to start learning about Silverlight and its various controls. If you are new to Silverlight, I recommend you to read that article before you start building the Twitter Client shown here. For this article, I assume you have some experience in creating Silverlight projects and have worked with WebServices.
Here’s how the Twitter Client will look once it is made. Let us see the steps to get started with it.
Twitter Client
Step 1: Launch Visual Studio 2008 and choose File > New project > Silverlight > Silverlight Application.
 
Step 2: Choose location and name for your application and click OK.  Click OK again keeping the defaults in the next window. Now your solution explorer should look like the one shown below:
Twitter Solution Explorer
Step 3: Open ‘Page.xaml’ and add the following code
<Grid x:Name="LayoutRoot" ShowGridLines="False">
   <Grid.RowDefinitions>
      <RowDefinition Height="40"/>
      <RowDefinition Height="*"/>
   </Grid.RowDefinitions>
      <Grid Grid.Row="0" Margin="7" ShowGridLines="false">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="100"/>
          <ColumnDefinition Width="140"/>
          <ColumnDefinition Width="100"/>
          <ColumnDefinition Width="140"/>
          <ColumnDefinition Width="120"/>
        </Grid.ColumnDefinitions>
      <TextBlock Text="Username :" Grid.Column="0" Foreground="Black" HorizontalAlignment="Right" VerticalAlignment="Center"></TextBlock>
     <TextBox x:Name="txtUsername" Grid.Column="1" Width="140"></TextBox>
 
     <TextBlock Text="Password :" Grid.Column="2" Foreground="Black" HorizontalAlignment="Right" VerticalAlignment="Center"></TextBlock>
     <PasswordBox x:Name="txtPassword" Grid.Column="3" Width="140" ></PasswordBox>
     <Button x:Name="btnLogin" Grid.Column="4" Content="Login" Width="100" Click="btnLogin_Click" ></Button>
 </Grid>
 <data:DataGrid Name="FriendsTimeLine" Grid.Row="1"></data:DataGrid>
</Grid>
In the markup shown above, we have added one TextBox, PasswordBox and Button. We have also done some formatting to align our controls. ‘PasswordBox’is a new control in Silverlight 2. You can read more about it here. If you want to learn more about aligning controls, read ScottGu’s article.
Step 4: Before we move on to next step, let’s talk about the sample Digg app which is in ScottGu’s tutorial. If you have read the article you will see that Scott has used the WebClient Class to retrieve data from Digg.com. It’s actually very easy to request data using WebClient class, however when you are using WebClient in Silverlight, you will notice that some headers like ‘Authorization’ are classified as “Restricted” for Silverlight’s HttpWebRequest. It does not appear in Intellisense as well. But requesting friends timeline from Twitter requires Authentication!
 
One way to overcome this restriction is by adding a webservice to your application which can make the authentication for us. I have used this excellent example by Peter from www.eggheadcafe.com with his permission.
 
Step 5: Add a new webservice to your project by Right Click -> Add New Item. Add the following code to your webservice code file. (Refer Peter’s example for more information and how this works)
 
C#
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Services;
 
namespace GenericRequest
{
///<summary>
/// Generic Request Service for Silverlight Apps
///</summary>
[WebService(Namespace = "http://genericrequest.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
    ///<summary>
    /// Makes an HttpRequest.
    ///</summary>
    ///<param name="targetUrl">The target URL.</param>
    ///<param name="headerNames">The header names.</param>
    ///<param name="headerValues">The header values.</param>
    ///<param name="method">The method.</param>
    ///<param name="postValueNames">The post value names.</param>
    ///<param name="postValues">The post values.</param>
    ///<param name="userName">username.</param>
    ///<param name="password">password.</param>
    ///<returns>string (xml, html, etc</returns>
    [WebMethod]
    public string MakeRequest(string targetUrl, string[] headerNames, string[] headerValues,
        string method, string[] postValueNames, string[] postValues, string userName, string password)
    {
        string returnString = "Bad Request";
        // This is a GET request with no form values and optional Basic auth
        if (method.ToUpper() == "GET")
        {
            using (WebClient wc = new WebClient())
            {
                wc.Proxy = null;
                if (userName != null && password != null)
                    wc.Credentials = new NetworkCredential(userName, password);
                if (headerNames != null && headerNames.Length > 0)
                {
                    for (int i = 0; i < headerNames.Length; i++)
                    {
                        wc.Headers.Add(headerNames[i], headerValues[i]);
                    }
                }
                returnString = wc.DownloadString(targetUrl);
            }
        }
        // This is a POST request with form values and optional Basic auth
        if (method.ToUpper() == "POST" && postValueNames != null)
        {
            using (WebClient wc = new WebClient())
            {
                wc.Proxy = null;
                if (userName != null && password != null)
                    wc.Credentials = new NetworkCredential(userName, password);
                if (headerNames != null && headerValues != null)
                {
                    for (int i = 0; i < headerNames.Length; i++)
                    {
                        wc.Headers.Add(headerNames[i], headerValues[i]);
                    }
                }
                var data = new NameValueCollection();
                for (int i = 0; i < postValueNames.Length; i++)
                {
                    data.Add(postValueNames[i], postValues[i]);
                }
                byte[] result = wc.UploadValues(targetUrl, "POST", data);
                returnString = System.Text.Encoding.UTF8.GetString(result);
            }
        }
        return returnString;
    }
}
}
 
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports System.Linq
Imports System.Net
Imports System.Web
Imports System.Web.Services
 
Namespace GenericRequest
    ''' <summary>
    ''' Generic Request Service for Silverlight Apps
    ''' </summary>
    <WebService(Namespace:="http://genericrequest.org/"), WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1), System.ComponentModel.ToolboxItem(False)> _
    Public Class Service1
        Inherits System.Web.Services.WebService
        ''' <summary>
        ''' Makes an HttpRequest.
        ''' </summary>
        ''' <param name="targetUrl">The target URL.</param>
        ''' <param name="headerNames">The header names.</param>
        ''' <param name="headerValues">The header values.</param>
        ''' <param name="method">The method.</param>
        ''' <param name="postValueNames">The post value names.</param>
        ''' <param name="postValues">The post values.</param>
        ''' <param name="userName">username.</param>
        ''' <param name="password">password.</param>
        ''' <returns>string (xml, html, etc</returns>
<WebMethod()> _
Public Function MakeRequest(ByVal targetUrl As String, ByVal headerNames() As String, ByVal headerValues() As String, ByVal method As String, ByVal postValueNames() As String, ByVal postValues() As String, ByVal userName As String, ByVal password As String) As String
      Dim returnString As String = "Bad Request"
      ' This is a GET request with no form values and optional Basic auth
           If method.ToUpper() = "GET" Then
                Using wc As New WebClient()
                    wc.Proxy = Nothing
             If userName IsNot Nothing AndAlso password IsNot Nothing Then
                   wc.Credentials = New NetworkCredential(userName, password)
                    End If
             If headerNames IsNot Nothing AndAlso headerNames.Length > 0 Then
                        For i As Integer = 0 To headerNames.Length - 1
                            wc.Headers.Add(headerNames(i), headerValues(i))
                        Next i
                    End If
                    returnString = wc.DownloadString(targetUrl)
                End Using
            End If
            ' This is a POST request with form values and optional Basic auth
       If method.ToUpper() = "POST" AndAlso postValueNames IsNot Nothing Then
                Using wc As New WebClient()
                    wc.Proxy = Nothing
If userName IsNot Nothing AndAlso password IsNot Nothing Then
wc.Credentials = New NetworkCredential(userName, password)
                  End If
If headerNames IsNot Nothing AndAlso headerValues IsNot Nothing Then
                        For i As Integer = 0 To headerNames.Length - 1
                            wc.Headers.Add(headerNames(i), headerValues(i))
                        Next i
                    End If
                    Dim data = New NameValueCollection()
                    For i As Integer = 0 To postValueNames.Length - 1
                        data.Add(postValueNames(i), postValues(i))
                    Next i
            Dim result() As Byte = wc.UploadValues(targetUrl, "POST", data)
                  returnString = System.Text.Encoding.UTF8.GetString(result)
                End Using
            End If
            Return returnString
        End Function
    End Class
End Namespace
The web service shown above is a generic web service, that could make any kind of WebRequest call we wanted using the full Framework (without the "restrictions"), and return the appropriate results to our Silverlight apps.  We could give it the target url (including querystring), the method ("GET" or "POST"), username and password if authentication is required, the names and values of any request headers we want, and the names and values of any form POST values we needed.
Step 6: Right click the project > Add Service Reference to add a Service reference to the new webservice shown above.
Step 7: Open Page.xaml.vb and add the following code to your button’s click event.
C#
ServiceReference1.Service1SoapClient client = new ServiceReference1.Service1SoapClient();
string strURL = null;
 
strURL = "http://twitter.com/statuses/friends_timeline.xml";
 
client.MakeRequestCompleted += downloadComplete;
ArrayOfString headerNames = null;
headerNames = new ArrayOfString();
ArrayOfString headerValues = null;
headerValues = new ArrayOfString();
 
headerNames.Add("Cache-Control");
headerValues.Add("no-cache");
 
ArrayOfString formValueNames = null;
formValueNames = new ArrayOfString();
ArrayOfString formValues = null;
formValues = new ArrayOfString();
client.MakeRequestAsync(strURL, headerNames, headerValues, "GET", formValueNames, formValues, txtUsername.Text, txtPassword.Password);
 
VB.NET
Dim client As New ServiceReference1.Service1SoapClient
Dim strURL As String
 
strURL = "http://twitter.com/statuses/friends_timeline.xml"
 
AddHandler client.MakeRequestCompleted, AddressOf downloadComplete
Dim headerNames As ArrayOfString
headerNames = New ArrayOfString()
Dim headerValues As ArrayOfString
headerValues = New ArrayOfString()
 
headerNames.Add("Cache-Control")
headerValues.Add("no-cache")
 
Dim formValueNames As ArrayOfString
formValueNames = New ArrayOfString()
Dim formValues As ArrayOfString
formValues = New ArrayOfString()
client.MakeRequestAsync(strURL, headerNames, headerValues, "GET", formValueNames, formValues, txtUsername.Text, txtPassword.Password)
 
The code shown above will make a request to the webservice using ‘MakeRequestAsync’ along with the username and password. We also attach an event handler ‘downloadComplete’ which will be called as soon as the download is comeplete.
Step 8: Add a new class file to the project by Right Click - > Add new item. And add the following code to the class file
C# (2.0)
using System;
 
namespace TwitterApp
{
      public class Twitter
      {
            private string privateID;
            public string Id
            {
                  get
                  {
                        return privateID;
                  }
                  set
                  {
                        privateID = value;
                  }
            }
            private string privateMessage;
            public string Message
            {
                  get
                  {
                        return privateMessage;
                  }
                  set
                  {
                        privateMessage = value;
                  }
            }
 
      }
 
}
 
VB.NET
Imports Microsoft.VisualBasic
Imports System
 
Namespace TwitterApp
    Public Class Twitter
        Private privateID As String
        Public Property Id() As String
            Get
                Return privateID
            End Get
            Set(ByVal value As String)
                privateID = value
            End Set
        End Property
        Private privateMessage As String
        Public Property Message() As String
            Get
                Return privateMessage
            End Get
            Set(ByVal value As String)
                privateMessage = value
            End Set
        End Property
  
    End Class
 
End Namespace
We have just defined a new class that has properties which can be used to map the XML content from Twitter.
Step 9: Finally add the following code to Page.xaml.cs or Page.xaml.vb
C#
private void downloadComplete(object sender, MakeRequestCompletedEventArgs e)
{
            string strResult = null;
            strResult = e.Result;
            XDocument xmlStories = XDocument.Parse(strResult);
 
            var stories =
                  from story in xmlStories.Descendants("status")
                  where story.Element("text") != null
                  select new Twitter {Message = (System.Convert.ToString(story.Element("text"))).Trim(), Id = (System.Convert.ToString(story.Element("user").Element["name"])).Trim()};
            FriendsTimeLine.ItemsSource = stories;
 
 }
 
VB.NET
Private Sub downloadComplete(ByVal sender As System.Object, ByVal e As MakeRequestCompletedEventArgs)
        Dim strResult As String
        strResult = e.Result
        Dim xmlStories As XDocument = XDocument.Parse(strResult)
 
        Dim stories = From story In xmlStories.Descendants("status") _
                      Where story.Element("text") IsNot Nothing _
                       Select New Twitter With {.Message = (CStr(story.Element("text"))).Trim(), .Id = (CStr(story.Element("user").Element("name"))).Trim()}
        FriendsTimeLine.ItemsSource = stories
 
 End Sub
Once the download is complete, we use ‘XDocument.Parse’ to parse the XML message from Twitter. Then we run through all the nodes and add the values to the properties if the “text” element is not empty. ‘text’ element is the element containing your friend’s message. You can check out the Twitter API wiki to know more about Twitter API and how to use it.
Now run your project and see Twitter client in action. The source code of this article can be downloaded from here.
If you liked the article,  Subscribe to the RSS Feed or Subscribe Via Email  
 
Useful Links:
·         Twitter API
 
Shoban Kumar, is a 23 year old techie from Trivandrum mainly working on ASP, ASP.NET, VB, VB.NET and PHP. You can also find him blogging at www.crankup.net and www.codegeeks.net
 
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Chikku on Friday, March 20, 2009 5:38 AM
really interesting.... cool one.
Comment posted by Brian on Friday, March 20, 2009 10:50 AM
Thanks for the article. It was simple to follow. If anyone wants to style this control, here's a link from Scott Guthrie's site
http://weblogs.asp.net/scottgu/archive/2008/11/14/styling-a-silverlight-twitter-application-with-expression-blend-2.aspx
Comment posted by Riyas on Friday, March 20, 2009 1:26 PM
Good One..
Comment posted by Hummer on Monday, March 23, 2009 10:02 PM
Very good article. Sohban, how do you I perists this list if I want in my local machine?
Comment posted by Shoban Kumar on Friday, April 17, 2009 10:15 AM
Hi Hummer,
Silverlight 2 does not support out of browser facility. But Silverlight 3 has this support so you can create the same application and make it a desktop application.
Comment posted by kk18_b on Saturday, May 9, 2009 7:52 PM
Hello Shoban,

This is a wonderful article. I tried this on my local computer and now I am going to try the new facebook API.
Can you please tell me is there any other way to get the namespace for webservice instead of using http://genericrequest.org/, because I want to write an article on this and I don't know whether if I can use http://genericrequest.org/ link in my article to make it public. If there is any other way can you please let me know.
Comment posted by Paul on Thursday, September 17, 2009 4:17 AM
Fantastic article - thanks for putting it simply to help beginners such as myself!
Comment posted by Erika on Saturday, January 9, 2010 1:58 AM
Another cool Twitter client built on Silverlight is an App in the RedCritter App Gallery called Tweets. It runs inside Outlook and displays an email sender's recent tweets. There is also a RedCritter SDK for Outlook that lets you easily create your own Apps that run inside Outlook. It is free. You can see it at <a href="http://www.redcritter.com/App.aspx?i=6" target="_blank" class="">www.redcritter.com</a>
Comment posted by praveen on Wednesday, September 14, 2011 4:29 AM
ya..its really helpful to me...thanks a lot

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel