Silverlight 2.0 Twitter Widget

by Ryan Lane on April 16, 2008

I’m on an RSS widget roll.  I put together a widget that is much like the Flickr widget only it uses Twitter as the source.  Now Twitter had to go and give me a hard time by recently changing their cross domain policy file to only allow specific sites. Because of this I had to add a proxy to this solution to get it to work. 

Let’s get to the code.  If you need an aspx proxy this should do the trick for you.

   1: using System;
   2: using System.Configuration;
   3: using System.Data;
   4: using System.Linq;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.HtmlControls;
   9: using System.Web.UI.WebControls;
  10: using System.Web.UI.WebControls.WebParts;
  11: using System.Xml.Linq;
  12: using System.IO;
  13: using System.Net;
  14:  
  15: public partial class _Default : System.Web.UI.Page 
  16: {
  17:     protected void Page_Load(object sender, EventArgs e)
  18:     {
  19:         // Load the URI from the Query String
  20:         string sourceUriString = Request.QueryString["Uri"];        
  21:     
  22:         // Clear the output buffer
  23:         Response.Clear();
  24:  
  25:         // Make new WebClient 
  26:         WebClient webClientRequest = new WebClient();
  27:  
  28:         // Download data from URI
  29:         byte[] requestByteArray = webClientRequest.DownloadData(sourceUriString);
  30:  
  31:         // Match the Mime Types
  32:         string contentType = webClientRequest.ResponseHeaders["Content-type"].ToString();
  33:         Response.ContentType = contentType;
  34:  
  35:         // Copy the Streams
  36:         int requestByteArrayLength = requestByteArray.GetLength(0);
  37:         Response.OutputStream.Write(requestByteArray, 0, requestByteArrayLength);
  38:         Response.OutputStream.Close();
  39:  
  40:         // Exit the Page
  41:         Response.End();     
  42:     }
  43: }

proxy/Default.aspx.cs

As you’ll see in the next bits of code that I’m just passing the Twitter RSS URL to my proxy to get around the current limits of the cross domain policy

Because of the way I set up this solution with the proxy running on another port I had to add a crossdomain.xml file and I went ahead and put in the clientaccesspolicy.xml for example too.

Back to the goods.  Let’s take a look at the Silverlight object embedded in the html page and how it’s being loaded to pass parameters as need.

   1: <div id="silverlightControlHost">
   2:         <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="100%" height="100%">
   3:             <param name="source" value="ClientBin/TwitterFeed.xap"/>
   4:             <param name="onerror" value="onSilverlightError" />
   5:             <param name="background" value="white" />
   6:             <param name="initParams" value="twitterID=futileboy,proxy=http://localhost:51591/Default.aspx?uri=,time=4" />
   7:             
   8:             <a href="http://go.microsoft.com/fwlink/?LinkID=108182" style="text-decoration: none;">
   9:                  <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
  10:             </a>
  11:         </object>
  12:         <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>
  13:     </div>

TwitterFeedPage.html

I’m using the initParams to pass my customizable variables in to Silverlight app.

  • twitterID – takes the user id that you would like to display.  Mine is futileboy.
  • proxy – is an optional parameter to handle a proxy. localhost:51591 is part of my example solution.
  • time – is the duration that you want your tweets to be displayed for.  The time parameter is in seconds.

From the HTML side the parameters are passed App.xaml.cs which reads them in and then hands it off to the Page.xaml.cs

   1: using System.Windows;
   2: using System;
   3:  
   4: namespace TwitterFeed
   5: {
   6:     public partial class App : Application 
   7:     {
   8:  
   9:         public App() 
  10:         {
  11:             this.Startup += this.OnStartup;
  12:             this.Exit += this.OnExit;
  13:  
  14:             InitializeComponent();
  15:         }
  16:  
  17:         private void OnStartup(object sender, StartupEventArgs e) 
  18:         {
  19:             //grab the params pased from the html page
  20:             string currentUserID = e.InitParams["twitterID"];
  21:             string currentProxy = e.InitParams["proxy"];
  22:             int currentDuration = Convert.ToInt32(e.InitParams["time"]);
  23:             // Load the main control here
  24:             this.RootVisual = new Page(currentUserID, currentProxy, currentDuration);
  25:         }
  26:  
  27:         private void OnExit(object sender, EventArgs e) 
  28:         {
  29:  
  30:         }
  31:     }
  32: }

App.xaml.cs

Here’s the real meaty part that you’ve all been waiting for.  I did my best to get all the comments in there. 

   1: using System;
   2: using System.Windows;
   3: using System.Windows.Browser;
   4: using System.Windows.Controls;
   5: using System.Windows.Documents;
   6: using System.Windows.Ink;
   7: using System.Windows.Input;
   8: using System.Windows.Media;
   9: using System.Windows.Media.Animation;
  10: using System.Windows.Shapes;
  11: using System.Windows.Threading;
  12: using System.Xml.Linq;
  13: using System.Net;
  14: using System.IO;
  15: using System.Xml;
  16: using System.Collections;
  17: using System.Text;
  18: using System.ServiceModel;
  19: using System.ServiceModel.Syndication;
  20:  
  21:  
  22: namespace TwitterFeed
  23: {
  24:     public partial class Page : UserControl
  25:     {
  26:         //Set up the global variables here
  27:         DispatcherTimer dt = new DispatcherTimer();
  28:         string currentTitle;
  29:         string currentDate;
  30:         string currentLink;
  31:         int currentDuration;
  32:         DateTime lastUpdate;
  33:         string[,] postArray;
  34:         Int32 feedIndex = 0;
  35:         Int32 x = 0;
  36:         Int32 feedItemAmount;
  37:  
  38:         string feedBaseUri = "http://twitter.com";
  39:         string feedProxyUri;
  40:         string feedPathUri;
  41:  
  42:         public Page(string userID, string proxyUri, int duration)
  43:         {
  44:             // Required to initialize variables
  45:             InitializeComponent();
  46:  
  47:             currentDuration = duration;
  48:             feedProxyUri = proxyUri;
  49:             feedPathUri = (string.Format("/statuses/user_timeline/{0}.rss", userID));
  50:  
  51:             this.Loaded += new RoutedEventHandler(Page_Loaded);
  52:  
  53:             // Make new WebClient
  54:             WebClient webClientRequest = new WebClient();
  55:             
  56:             LabelBlock.Text = "Loading...";
  57:  
  58:             //put the url together
  59:             string sourceUrl = feedProxyUri + feedBaseUri + feedPathUri;
  60:  
  61:             //Create the URI to send the request to
  62:             Uri feedURL = new Uri(sourceUrl);            
  63:  
  64:             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(feedURL);            
  65:                         
  66:             //Make the asynchronous request and register a callback.
  67:             request.BeginGetResponse(new AsyncCallback(responseHandler), request);
  68:  
  69:             //
  70:         }
  71:  
  72:         void responseHandler(IAsyncResult asyncResult)
  73:         {
  74:             //Define the responseHandler function and get the HttpWebResponse.
  75:             HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;            
  76:             HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
  77:  
  78:             //Get the Stream that contains the response.
  79:             Stream responseStream = response.GetResponseStream();
  80:  
  81:             //Get a XmlReader over the stream and load it into a SyndicationFeed class.
  82:             XmlReader responseReader = XmlReader.Create(responseStream);
  83:             SyndicationFeed feed = SyndicationFeed.Load(responseReader);
  84:  
  85:             //get the number of feed items returned
  86:             feedItemAmount = (((System.Collections.ObjectModel.Collection<SyndicationItem>)(feed.Items))).Count;
  87:             
  88:             postArray = new string[feedItemAmount, 3];
  89:  
  90:             foreach (SyndicationItem item in feed.Items)
  91:             {
  92:                 currentTitle = item.Title.Text;                
  93:                 currentDate = XmlConvert.ToString(item.PublishDate, "MM-dd-yyyy HH:mm");
  94:  
  95:                 postArray[feedIndex, 0] = currentTitle;
  96:                 postArray[feedIndex, 1] = currentDate;
  97:                 postArray.SetValue(feedBaseUri + item.Links[0].Uri.AbsolutePath, feedIndex, 2);
  98:                 feedIndex++;
  99:             }
 100:  
 101:             lastUpdate = DateTime.Now;
 102:  
 103:             dt.Interval = new TimeSpan(0, 0, 0, currentDuration);
 104:             dt.Tick += new EventHandler(dt_Tick);
 105:             dt.Start();
 106:         }
 107:  
 108:         void Page_Loaded(object sender, RoutedEventArgs e)
 109:         {
 110:             LabelBlock.MouseEnter += new MouseEventHandler(LabelBlock_MouseEnter);
 111:             LabelBlock.MouseLeave += new MouseEventHandler(LabelBlock_MouseLeave);
 112:             LabelBlock.MouseLeftButtonDown += new MouseButtonEventHandler(LabelBlock_MouseButtonDown);
 113:         }
 114:  
 115:         void dt_Tick(object sender, EventArgs e)
 116:         {
 117:             //this is our timer control, every time we update this will run
 118:             Update();            
 119:         }
 120:  
 121:         void Update()
 122:         {
 123:             //Updating the time
 124:             DateTime now = DateTime.Now;
 125:             TimeSpan elapsed = now - lastUpdate;
 126:             lastUpdate = now;
 127:             //do your loop processing here
 128:             if (x <= (feedItemAmount - 1))
 129:             {
 130:                 LabelBlock.Text = postArray[x, 0];
 131:                 Pub_Date.Text = postArray[x, 1];
 132:                 currentLink = postArray[x, 2];
 133:                 x++;
 134:             }
 135:             else
 136:             {
 137:                 x = 0;                
 138:             }
 139:         }
 140:  
 141:         private void LabelBlock_MouseEnter(object sender, MouseEventArgs e)
 142:         {
 143:             //stop the animation
 144:             dt.Stop();
 145:         }
 146:  
 147:         private void LabelBlock_MouseLeave(object sender, MouseEventArgs e)
 148:         {
 149:             //start it back up again
 150:             dt.Start();
 151:         }
 152:  
 153:         private void LabelBlock_MouseButtonDown(object sender, MouseButtonEventArgs e)
 154:         {
 155:             //follow the link of the current tweet
 156:             HtmlPage.Window.Navigate(new Uri(currentLink));
 157:         }
 158:  
 159:     }
 160: }

Page.xaml.cs

Last but not least here’s the XAML that is being used to display our lovely little widget.

   1: <UserControl
   2:     xmlns="http://schemas.microsoft.com/client/2007"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     x:Class="TwitterFeed.Page"
   5:     Width="150" Height="150" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
   6:  
   7:     <Grid x:Name="LayoutRoot" >
   8:         <Rectangle HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" RadiusX="8" RadiusY="8" x:Name="background">
   9:             <Rectangle.Fill>
  10:                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  11:                     <GradientStop Color="#FF213343"/>
  12:                     <GradientStop Color="#FF5C7D99" Offset="1"/>
  13:                 </LinearGradientBrush>
  14:             </Rectangle.Fill>
  15:         </Rectangle>
  16:         <Rectangle HorizontalAlignment="Stretch" Margin="4,4,4,71" VerticalAlignment="Stretch" RadiusX="8" RadiusY="8" d:LayoutOverrides="VerticalAlignment">
  17:             <Rectangle.Fill>
  18:                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  19:                     <GradientStop Color="#80FFFFFF" Offset="0.013"/>
  20:                     <GradientStop Color="#00FFFFFF" Offset="0.906"/>
  21:                 </LinearGradientBrush>
  22:             </Rectangle.Fill>
  23:         </Rectangle>
  24:         <TextBlock Margin="0,0,0,0" Text="test" 
  25:                    TextWrapping="Wrap"
  26:                    x:Name="LabelBlock" 
  27:                    Foreground="#FFFFFFFF" 
  28:                    FontSize="12" 
  29:                    FontFamily="Verdana" 
  30:                    VerticalAlignment="Top" 
  31:                    HorizontalAlignment="Left" 
  32:                    LineHeight="10" 
  33:                    Padding="11,11,11,10"
  34:                    Cursor="Hand"/>
  35:         <TextBlock Height="14" Margin="8,0,8,0" VerticalAlignment="Bottom" Text="" TextWrapping="Wrap" FontSize="9" FontFamily="Verdana" Foreground="#FFA0BEE1" x:Name="Pub_Date"/>
  36:     </Grid>
  37: </UserControl>

Page.xaml

I haven’t added this to my blog here yet, but I plan on putting up there in the upper right in place of the current twitter widget in the very near future.

Related posts:

  1. Silverlight 2.0 Flickr WordPress Widget
  2. My first Chumby widget
  3. SEO for Silverlight
  4. Light Strokes OptiPaint
  5. Learning Silverlight

Leave a Comment

{ 1 trackback }

Previous post:

Next post: