This is a series of posts of my journey learning Silverlight one weekend:



After spending the day on Saturday consuming a lot of info on Silverlight development, Sunday I intended to get my own application up and running. Tasking myself with this was tricky as ideally I’d do something that hasn’t been done a million times before like the same old, bog standard twitter client, flickr client, RSS reader blah blah blah boring boring boring… Unfortunately I couldn’t think of anything so I decided to go for all three anyway!

Using the excellent Balsamiq mockups, I put together the following mockup as a rough guide for what I wanted to achieve:


The idea being this could eventually become some sort of blog/website sidebar widget.

Armed with an objective I set out to accomplish the task in hand. Unfortunately, I had a very slow start to the day running into issues with VS templates and the visual studio productivity tools extension. In the end I had to delete a corrupt template from:

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ProjectTemplatesCache

and

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ProjectTemplates

then run the following cmd to reinstate the desired templates

devenv.exe /installvstemplates

With that sorted I ended up just removing the Productivity Tools extension as although it’s very useful and aspires to do many cool things it seemed to be really buggy and slow. This could well be to do with it competing with Resharper that I installed recently and as that delivers everything I need and more I decided to lose the productivity tools.

Anyway, finally we got going and I was able to start piecing my layout together. I grabbed the Tab control which was fairly self explanatory and referencing back to the first tekpub video was able to start putting together a simple twitter feed. I used the DesignContext which is a really nice way to work with real data at design time and think that will be hugely valuable in Blend.

Twitter feed

First thing after roughly putting together an interface, I started to consume my twitter feed. As I wanted to use Json I started to explore the DataContractJsonSerializer and JsonObject, however I was unable to reference it properly. I found out you need to reference the System.ServiceModel.Web assembly to be able to add the needed references.

I initially tried to use the twitter api to retrieve my own tweets from the url:
http://api.twitter.com/1/statuses/user_timeline.json?screen_name=dannyt

However, in order to perform cross domain web service calls Silverlight requires a policy file explicitly enabling such (Silverlight has it’s own format but will also work with the crossdomain.xml format). The twitter api domain (api.twitter.com) doesn’t offer an open policy and I would need to setup oAuth and jump through a load of authorisation hoops to be able to use that feed. Fortunately however, the twitter search api has an open policy file granting public access from anywhere, after a little investigation I found the advanced search facility on twitter which shows you can search without a search term and filter the results by user, perfect!

The Tekpub videos had all the info I needed to get my search results into the app so that objective was achieved very easily which was nice.

Flickr

Next onto Flickr. I don’t use Flickr anywhere near as much as I’d like and wasn’t very familiar with the API so I set about reading up on it. It took me a while but it appears you make your webservice calls and get back the data you need to then piece together your own urls to request images with. A quick search of some other samples using Flickr confirmed this with the following format being the norm:

string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_s.jpg", Farm, Server, Id, Secret);

I tried to get the data in Json again but there seemed to be some validation issue that I couldn’t figure out so I switched to plain old REST. This turned out to be a good thing as I learned a little more of the XDocument class with LinQ.
It did however bother me that I couldn’t get the Json approach working so dug a little deeper and in the end I figured out that the flickr api returns json wrapped in a callback function which the json serializer didn’t approve of. Simply append &nojsoncallback=1 to the querystring and everything was working again. Still, two nice examples achieving the same result.

RSS

Finally I got the RSS feed working which was really just more of the same only this time using the built-in SyndicationFeed class. I found some interesting challenges with the actual post content but in the end I just stripped the html and returned the first 200 characters as a summary. Silverlight really needs some form of basic html rendering for such things.

The App

I did manage to get most of this completed on the Sunday but needed to tidy a few things up such as figuring out how best to embed silverlight into the blog (see Tim Heuers awesome SL for WP plugin). So apologies for the delay but here is the fruits of my labours from one weekend learning Silverlight:

Install Microsoft Silverlight

Okay so it’s not the most original or ground-breaking thing in the world but not bad for a few hours work eh? I’m also very aware my Flickr feed isn’t currently the most thrilling 😀

Code

Here are some of the key code snippets that might be of use to others

Loading an RSS feed

private void LoadBlogPosts()
{
  var client = new WebClient();
  client.DownloadStringAsync(
    new Uri("http://danny-t.co.uk/?feed=atom"));
  client.DownloadStringCompleted += BlogPostsLoaded;
}
 
void BlogPostsLoaded(object sender, DownloadStringCompletedEventArgs e)
{
  var reader = XmlReader.Create(new StringReader(e.Result));
  var feed = SyndicationFeed.Load(reader);
  viewModel.BlogPosts = feed.Items.Select(post => new BlogPost
  {
    Description = StripHtmlTags(HttpUtility.HtmlDecode(
      (post.Content as TextSyndicationContent).Text)
      ).Substring(0, 200),
    Link = post.BaseUri.AbsoluteUri,
    PubDate = post.PublishDate.Date,
    Title = HttpUtility.HtmlDecode(post.Title.Text)
  });
}
 
// courtesy of http://silverlike.net/strip-html-tags/
private string StripHtmlTags(string value)
{
  var length = 0;
  int.TryParse(value, out length);
 
  // Remove HTML tags and empty newlines and spaces and leading spaces
  var formattedValue = Regex.Replace(value as string, "<.*?>", "");
  formattedValue = Regex.Replace(formattedValue, @"\n+\s+", "\n\n");
  formattedValue = formattedValue.TrimStart(' ');
  formattedValue = HttpUtility.HtmlDecode(formattedValue);
  if (length > 0 && formattedValue.Length >= length)
    formattedValue = formattedValue.Substring(0, length - 1);
  return formattedValue;
}

Loading JSON from twitter search

private void LoadTweets()
{
  var webClient = new WebClient();
  var uri = new Uri("http://search.twitter.com/search.json?q=&from=dannyt");
  webClient.OpenReadCompleted += TweetsLoaded;
  webClient.OpenReadAsync(uri);
}
 
private void TweetsLoaded(object sender, OpenReadCompletedEventArgs e)
{
  // grab the result stream
  var stream = e.Result;
  if (stream == null) return;
  // extract json array of tweets
  var jsonTweets = (JsonArray)JsonValue.Load(stream)["results"];
  if (jsonTweets == null) return;
  // convert json tweets to strongly typed collection and set as DataContext
  viewModel.Tweets = jsonTweets.Select(jsonTweet =>
    new Tweet
    {
      Message = HttpUtility.HtmlDecode(jsonTweet["text"]),
      Published = jsonTweet["created_at"]
    });
}

Loading Images from Flickr (REST)

private void LoadImages()
{
  var webClient = new WebClient();
  var uri = new Uri(
"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=[yourapikey]&user_id=[youruserid]"
  );
  webClient.DownloadStringCompleted += ImagesLoadedXml;
  webClient.DownloadStringAsync(uri);
}
 
private void ImagesLoadedXml(object sender, DownloadStringCompletedEventArgs e)
{
  var document = XDocument.Parse(e.Result);
  var flickrImages = from found
    in document.Descendants("photo")
    .Take(20)
    select new FlickrImage
    {
       Id = (string)found.Attribute("id"),
       Farm = (int)found.Attribute("farm"),
       Server = (string)found.Attribute("server"),
       Secret = (string)found.Attribute("secret"),
     };
 
    viewModel.FlickrImages = flickrImages;
}

Same as above but with Json

private void LoadImages()
{
  var webClient = new WebClient();
  var uri = new Uri(
"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=[yourapikey]&user_id=[youruserid]&format=json&nojsoncallback=1"
  );
  webClient.OpenReadCompleted += ImagesLoadedJson;
  webClient.OpenReadAsync(uri);
}
 
private void ImagesLoadedJson(object sender, OpenReadCompletedEventArgs e)
{
  // grab the result stream
  var stream = e.Result;
  if (stream == null) return;
  // extract json array of images
  var jsonImages = (JsonArray)JsonValue.Load(stream)["photos"]["photo"];
  if (jsonImages == null) return;
  // convert json images to strongly typed collection and set as DataContext
  viewModel.FlickrImages = jsonImages
    .Take(20)
    .Select(image =>
      new FlickrImage
      {
        Id = image["id"],
        Farm = image["farm"],
        Server = image["server"],
        Secret = image["secret"]
      });
}

UI
Finally, the xaml markup for the tab control:

<sdk:TabControl Name="LayoutRoot">
 <sdk:TabItem Header="Twitter" Name="TwitterTab">
  <Grid Name="TwitterGrid">
   <Grid.RowDefinitions>
    <RowDefinition Height="238*" />
    <RowDefinition Height="28*" />
   </Grid.RowDefinitions>
   <ScrollViewer>
    <ItemsControl ItemsSource="{Binding Tweets}" Grid.RowSpan="2">
     <ItemsControl.ItemTemplate>
      <DataTemplate>
       <StackPanel Name="TweetsPanel">
        <TextBlock Text="{Binding Message}" 
         TextWrapping="Wrap" 
         FontSize="14"/>
        <TextBlock Text="{Binding Published, StringFormat='{}{0:ddd, dd MMM HH:mm}'}" 
         FontStyle="Italic"/>
       </StackPanel>
      </DataTemplate>
     </ItemsControl.ItemTemplate>
    </ItemsControl>
   </ScrollViewer>
   <HyperlinkButton Grid.Row="1" 
    NavigateUri="http://twitter.com/dannyt" 
    Content="@DannyT" 
    HorizontalAlignment="Right"
    FontSize="14"
    FontWeight="Bold"/>
   </Grid>
 </sdk:TabItem>
 <sdk:TabItem Header="Flickr" Name="FlickrTab">
  <ItemsControl ItemsSource="{Binding FlickrImages}">
   <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
     <Controls:WrapPanel />
    </ItemsPanelTemplate>
   </ItemsControl.ItemsPanel>
   <ItemsControl.ItemTemplate>
    <DataTemplate>
     <Image Source="{Binding ImageUrl}" Width="88" />
    </DataTemplate>
   </ItemsControl.ItemTemplate>
  </ItemsControl>            
 </sdk:TabItem>
 <sdk:TabItem Header="Blog" Name="BlogTab">
  <Grid Name="BlogGrid">
   <Grid.RowDefinitions>
    <RowDefinition Height="238*" />
    <RowDefinition Height="28*" />
   </Grid.RowDefinitions>
   <ScrollViewer>
    <ItemsControl ItemsSource="{Binding BlogPosts}">
     <ItemsControl.ItemTemplate>
      <DataTemplate>
       <StackPanel Name="PostsPanel">
        <HyperlinkButton NavigateUri="{Binding Link}">
         <TextBlock Text="{Binding Path=Title}" 
         TextWrapping="Wrap" 
         FontSize="14"/>
        </HyperlinkButton>
        <TextBlock Text="{Binding PubDate, StringFormat='{}{0:ddd, dd MMM HH:mm}'}" 
         FontStyle="Italic"/>
        <TextBlock Text="{Binding Description}"
        TextWrapping="Wrap" />
       </StackPanel>
      </DataTemplate>
     </ItemsControl.ItemTemplate>
    </ItemsControl>
   </ScrollViewer>
   <HyperlinkButton Grid.Row="1" 
    NavigateUri="http://danny-t.co.uk/" 
    Content="danny-t.co.uk" 
    HorizontalAlignment="Right"
    FontSize="14"
    FontWeight="Bold"/>
  </Grid>
 </sdk:TabItem>
</sdk:TabControl>

If you have any questions post them in the comments, I can give more detail or provide the sln if it’s really wanted :).

What did I think?

I’ve really enjoyed familiarising myself with Silverlight the past few days, it’s evolving at such a rapid pace and has come so far in a short space of time. The main thing for me however, is that the tools really sets Silverlight apart from Flash/Flex and AJAX development.

In terms of capabilities there really isn’t much in it between Flash and Silverlight, each has its own unique features and for some scenarios one will stand out from the other but for day to day applications development they are fair on par in terms of plugin features. When it comes to the “reach” argument there’s no doubt Silverlight is bottom of the pile versus Flash or AJAX applications when a broad an audience as possible is of significance.

But from a developers standpoint, developing for Silverlight is an absolute joy. Powerful tools, language and fast compilation makes for an extremely efficient development story, far more so than Flash or AJAX dev in my experience. There’s still lots to do, and I’m not ready to ditch any one technology at the moment. It’s just nice to have so many great options available to us 🙂

Where next?

I didn’t get onto skinning/styling which is next on the agenda so will probably continue to use this application as a base for those. I also still want to have a play with WM7 so that’s on the cards too.