Click here to Skip to main content
15,867,141 members
Articles / Multimedia / Audio

Build a Pandora clone with Silverlight 4

Rate me:
Please Sign up or sign in to vote.
4.94/5 (51 votes)
23 Oct 2012Ms-PL23 min read 190.3K   78   84
Exercising Silverlight 4 to build a fun, real-world app

Chavah screen capture

For the uninitiated, Pandora is a popular Flash-based internet radio service: users create their own radio stations by seeding an artist or song, and by giving a thumb-up or thumb-down to played songs. The software responds by playing more of what the user likes based off these inputs.

In this article, I'm going to show you how to build a Pandora-like music service using Silverlight 4, WCF, and Entity Framework. The end result is Chavah ("HA-vah"), a fun Silverlight clone of Pandora which can be seen live at judahhimango.com/chavah.

Introduction

Why build a Pandora clone, you ask?

I have a selfish motivation: Pandora doesn’t play the music I’m most interested in. As a Jewish follower of Yeshua (Jesus), I enjoy a tiny genre of religious music called Messianic Jewish music, a genre so small Pandora doesn’t know about any of our music.

I thought, “Why not build a Pandora clone to play some great Messianic Jewish tunes?” It would serve myself primarily, but also others in our community. And it'd raise the awareness of some of the great Messianic music out there to boot.

But a more general motivation is, “Why not?” If anything, it’s a great learning experience to expose one’s self to a full client/server application using Silverlight, WCF, and Entity Framework. These technologies are currently popular and in demand by employers, so it’s great résumé fodder to boot.

Out of these motivations, the Chavah project was born. Chavah is my attempt to build a Pandora-like clone that plays Messianic Jewish music, and is the subject of this article.

Why Silverlight?

During the 2010 Professional Developer’s Conference, Microsoft hyped up IE9 and its HTML 5 support, without commenting much on Silverlight. Because of this, there has been speculation as to whether Silverlight would fade out in favor of HTML 5; folks questioning why to use Silverlight over HTML 5.

While Microsoft has since reiterated their long term support for Silverlight, the question of why use Silverlight was a very real question when starting a web app like Chavah.

My reasons for ultimately choosing Silverlight are pragmatic:

  • Silverlight is more cross-platform than HTML 5. Most of my target audience is still running browsers with limited or no HTML 5 support. And even for the few that do have HTML 5-compatible browsers , the support is neither consistent across browsers, nor stable. My target audience is Windows and Mac, and Silverlight runs great on both, right now, in most any web browser.
  • The tooling kicks ass. Visual Studio + Blend is a hard combo to beat. C# is a great language with great tool and refactoring support. Animations, paths, soft UI edges, glow effects, drop shadows, and all things UI sexy are easy with Blend.
  • Silverlight will always be a step ahead of the HTML spec. By the time HTML 5 has broad reach, Silverlight will have features that only HTML 6 will bring. This is the nature of design-by-committee of the HTML spec. Silverlight will innovate and bring new features plain old HTML is missing, and will deliver it faster and broader than the HTML spec ever will.
  • Silverlight is an app development platform; HTML was intended for documents. Chavah is an application, not a document. HTML + Javascript + CSS hacks can turn a document into an app, but ultimately, a platform built for apps is more compelling for an application like Chavah.
  • Since Chavah is an application, I want users to be able to install Chavah locally onto their desktops. With Silverlight’s out-of-browser capabilities, users can optionally install Chavah as a native application. This simply would not be possible with HTML + CSS + Javascript, barring browser-specific hacks like IE9’s “pinned sites” or Google Chrome’s “application shortcuts”.

\

Points of Interest

In showing how Chavah is built, this article will show you how you can simulate the look and feel of apps like Pandora – smooth UI layout, animations, fluid feel – all in Silverlight 4. I’ll show you how a real-world application can make use of some of the new features in Silverlight, like out-of-browser functionality, easing animations, and GPU acceleration.

Additionally, we’ll cover communication with the backend WCF web service where the “pick a song based off the user’s likes and dislikes” logic is located.

I’ll show you how to add some fun social features to your application using WCF, Entity Framework, and SQLite.

Also, I’ll touch on some upcoming sexy tech -- Reactive Extensions and Code Contracts – and offer my take on their viability and utility in modern apps.

One thing to note: while Chavah is inspired by Pandora, be advised that, unlike Pandora, Chavah does not use Music Genome Project to predict what kind of music you’ll like. That is beyond the scope of this project. Chavah instead plays everything until you start thumbing up and down songs, at which point it uses a special algorithm to choose a song based on your preferences. We’ll cover this simpler algorithm later in this article.

Using the Code

When it comes to visuals, the meat-and-potatoes of Pandora are individual song tiles appearing on the right side of the screen, fluidly moving into position. Once in position, the song starts playing. The user can then pause the song, skip the song, thumb-up the song, or thumb-down the song.

Pandora's tile animation

This is the first part we’ll tackle – how do we build a UI control like Pandora’s song tile pictured above? And how can we fluidly move it into place like that?

Building a Song Tile

The first order of business is building a song tile.

As a testament to Silverlight’s extensible UI framework, we’re going to build the song tile control entirely from the built-in controls in the Silverlight framework. We’ll customize the look and feel for some of the components, but there’s no special 3rd party toolkits used here, it’s all in the box.

The control outline is quite simple:

Chavah song tile XAML outline

As you can see, there’s nothing too fancy going on here: We’ve got a Border that holds the whole tile. Achieving the rounded corner effect is simple as setting the Border’s CornerRadius property.

The only part of this UI that required any amount of work is the thumb-up and thumb-down buttons. Functionally, they are RadioButtons: if one is checked, the other is unchecked. (That is, you cannot simultaneously thumb-up and thumb-down a song.)

Obviously, the built-in RadioButton’s look and feel won’t do. We need to change the look of the radio button to show the thumb up/down.

To accomplish this, you can build a custom ControlTemplate for the radio buttons. For Chavah’s “thumb up” button, it’s dead simple:

XML
<ControlTemplate x:Key="ThumbUpStyle" TargetType="RadioButton">
  <Grid>
      <Image x:Name="imageUnchecked" Width="16" Height="16" 
            Source="http://judahhimango.com/Chavah/asyncImages/thumbUp.png" />
      <Image x:Name="imageChecked" Width="16" Height="16" 
           Opacity="0" Source="http://judahhimango.com/Chavah/asyncImages/thumbUpChecked.png" />
      <VisualStateManager.VisualStateGroups>
          <VisualStateGroup x:Name="CheckStates">
              <VisualState x:Name="Checked">
                  <Storyboard>
                      <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="imageChecked" 
                           Storyboard.TargetProperty="(UIElement.Opacity)" To="1" />
                  </Storyboard>
              </VisualState>
              <VisualState x:Name="Unchecked">
                  <Storyboard>
                      <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="imageChecked" 
                            Storyboard.TargetProperty="(UIElement.Opacity)" To="0" />
                  </Storyboard>
              </VisualState>
          </VisualStateGroup>
      </VisualStateManager.VisualStateGroups>
  </Grid>
</ControlTemplate> 

The control template defines 2 images: a “checked” thumb up, and an “unchecked” thumb up. When the RadioButton goes from checked to unchecked and vice-versa, we simply hide one image and show the other using an opacity animation. End result is a decent-looking thumb-up control with a nice animation effect when switching between checked states:

ChavahThumbUp.png

Silverlight’s control templating made it dead simple to create a custom radio button similar to Pandora’s thumb up control.

You’ll notice we have a number between the thumb up and thumb down buttons. That’s part of a fun social feature we’ll cover momentarily.

Animating the song tiles into position

I mentioned earlier that when Pandora plays a song, rather than the song tile suddenly appearing out of nowhere in a jarring fashion, an edge of the tile appears on the right-side of the screen and fluidly moves into place.

This effect is part of a broader effort to build fluid UIs, something gaining popularity in the software development world, particularly in the rich internet application niche.

How can we achieve the same effect in Silverlight?

My first thought was to use Silverlight’s built-in animation system to animate the margin of the tile: make the song title’s margin a large number so that it’s off screen, then animate it into position.

In WPF, we’d accomplish this by animating the song control's margin using a ThicknessAnimation.

Unfortunately, as of Silverlight 4, Silverlight does not have a ThicknessAnimation.

Having consulted the mighty google gods, I found others have worked around this problem using a custom ThicknessAnimation. I’ve customized it further with some easing animations, more on that in a minute:

C#
public class ThicknessAnimation 
{ 
   // The time along the animation from 0-1
    public static DependencyProperty TimeProperty = DependencyProperty.RegisterAttached("Time", 
      typeof(double), typeof(DoubleAnimation), new PropertyMetadata(OnTimeChanged));
    // The object being animated
    public static DependencyProperty TargetProperty = DependencyProperty.RegisterAttached("Target", 
      typeof(DependencyObject), typeof(ThicknessAnimation), null);
    // The thickness we're animating to
    public static DependencyProperty FromProperty = DependencyProperty.RegisterAttached("From", 
      typeof(Thickness), typeof(DependencyObject), null);
    // The tickness we're animating from
    public static DependencyProperty ToProperty = DependencyProperty.RegisterAttached("To", 
      typeof(Thickness), typeof(DependencyObject), null);
    // The target property to animate to.  Should have a property type of Thickness
    public static DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
      "TargetProperty", typeof(DependencyProperty), typeof(DependencyObject), null);
    
    public static Timeline Create(DependencyObject target, DependencyProperty targetProperty, 
      Duration duration, Thickness from, Thickness to)
    {
        DoubleAnimation timeAnimation = new DoubleAnimation() { From = 0, To = 1, Duration = duration };
        timeAnimation.EasingFunction = new ExponentialEase() { Exponent = 9, 
          EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut };
        timeAnimation.SetValue(TargetProperty, target);
        timeAnimation.SetValue(TargetPropertyProperty, targetProperty);
        timeAnimation.SetValue(FromProperty, from);
        timeAnimation.SetValue(ToProperty, to);
        Storyboard.SetTargetProperty(timeAnimation, new PropertyPath("(ThicknessAnimation.Time)"));
        Storyboard.SetTarget(timeAnimation, timeAnimation);
        return timeAnimation;
    }
 
    private static void OnTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DoubleAnimation animation = (DoubleAnimation)sender;
        double time = GetTime(animation);
        Thickness from = (Thickness)sender.GetValue(FromProperty);
        Thickness to = (Thickness)sender.GetValue(ToProperty);
        DependencyProperty targetProperty = (DependencyProperty)sender.GetValue(TargetPropertyProperty);
        DependencyObject target = (DependencyObject)sender.GetValue(TargetProperty);
        target.SetValue(targetProperty, new Thickness((to.Left - from.Left) * time + from.Left,
                                                      (to.Top - from.Top) * time + from.Top,
                                                      (to.Right - from.Right) * time + from.Right,
                                                      (to.Bottom - from.Bottom) * time + from.Bottom)); 
    }
 
    public static double GetTime(DoubleAnimation animation)
    {
        return (double)animation.GetValue(TimeProperty);
    }
 
    public static void SetTime(DoubleAnimation animation, double value) 
    { 
        animation.SetValue(TimeProperty, value); 
    }
}   

Getting funky with Silverlight easing animations

Draw your attention to the animation creating code, you’ll notice we’re using Silverlight’s new easing animations:

C#
timeAnimation.EasingFunction = new ExponentialEase() 
{ 
   Exponent = 9, 
   EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut 
}; 

An easing animation is a kind of animation that, like the name suggests, eases from the source state to the target state. Easing animations are important in that it helps build a fluid feel to the application.

Without any animation, a song tile just appearing out of nowhere feels jarring.

With plain vanilla animations, sliding in the song tile will feel mechanical.

Only with easing animations can we achieve the warm fuzzy gooey fluidity that makes users sh*t their pants in awe.

Easing animations are comprised of 2 parts:

  1. Easing function
  2. Easing mode

For easing functions, you’ve got an impressive list of options (hat tip Mike Snow):

  • BackEase – Moves the element backwards by an amount specified through its amplitude before moving forward.
  • BounceEase – Creates an effect like a bouncing element.
  • CircleEase – Accelerates the animation based upon a circular function.
  • CubicEase – Accelerates the animation based upon a cubic function.
  • ElasticEase – Uses springiness and oscillation to animate.
  • ExponentialEase – Accelerates the animation based upon an exponent value.
  • PowerEase – Accelerates the animation based upon a power of time.
  • QuadraticEase – Accelerates the animation based upon the square of time.
  • QuarticEase – Accelerates the animation based upon the cube of time.
  • QuinticEase – Accelerates the animation based upon the time to the power of 5.
  • SineEase – Accelerates the animation along a sine wave.

And for easing modes,

  • EaseIn – start the animation slow, then accelerate to full speed
  • EaseOut – start the animation full speed, then decelerate to end
  • EaseInOut – start the animation slow, accelerate to full speed, then decelerate to end

For Chavah, we’ll use the ExponentialEase function with the EaseOut mode. The result is a song tile that shows up in the UI quickly, then slowly rests into place. Nice and smooth. The users will have to change their underwear after seeing this.

Using the animation

Now that we’ve done the hard work of defining a ThicknessAnimation, and got all gooey about the easing animation stuff, actually using the animation is a piece of cake:

Create a storyboard, add our thickness animation to the storyboard, and .Begin() the animation:

C#
var animationTime = TimeSpan.FromSeconds(1.5); 
var timeline = ThicknessAnimation.Create(control, SongControl.MarginProperty, 
   new Duration(animationTime), new Thickness(5, 5, -150, 5), new Thickness(5, 5, 5, 5)); 
Storyboard storyBoard = new Storyboard(); 
storyBoard.Children.Add(timeline);
storyBoard.Begin();  

We’re animating the song tile’s right margin from -150 (e.g. pushed off the right side of the screen) to 5, such that when the song is added to the UI, it will shift all the existing song tiles over, smoothly making room for the new UI. Nice and fluid.

Using these effects, we’ve imitated Pandora’s song tile control, including its rounded look and fluid animations with very little developer effort. Pure woot!

Installing Silverlight apps onto users desktops

Since Chavah is an application, I’d like users to be able to run the application from their desktops without having to deal with browsers, addresses, bookmarks, etc. They should be able to run my app without having to launch a web browser.

One relatively new feature of Silverlight is the out-of-browser support. With out-of-browser, you can give users the opportunity to install your application with optional shortcuts on the desktop and start menu.

For Chavah, I wanted the install process to be completely optional and, from a UX point of view, out of the way. For many users, in-your-face installation prompts are scary and generally a pain point with desktop software. My target audience may not be software-savvy, so I do not wish to scare them away with installation prompts.

To avoid the in-your-face scary installation prompts, Chavah will be installed only if the user deliberately clicks an innocuous, subtle link:

InstallChavah.png

We provide a tool tip for that link, “Install Chavah onto your desktop”.

Clicking that link will trigger will run installation code. Silverlight out-of-browser installation code is trivial:

C#
if (!Application.Current.IsRunningOutOfBrowser && 
       Application.Current.InstallState != InstallState.Installed)
{
   var success = Application.Current.Install();
   …
}   

The Application.Current.Install() method is all it takes. When invoked, Silverlight will prompt the user to install. Here comes the scary installation prompt:

InstallChavahPrompt.png

Not too bad. Again, users only get here if they deliberately try to install it.

The install experience is nice: it takes milliseconds to install, the prompt does not have any scary red X’s about my app harming the user’s system, and the user doesn’t have to do anything besides click “OK”.

Contrast this with typical installs, where you’ve got an admin privileges prompt, a frightening screen asking if you’re sure you want to download an .exe which will destroy your C:\ drive, a multi-step installation wizard, EULA agreements, install directory choices, install component customization, and other superfluous stuff that makes users afraid to install desktop applications on Windows.

Yeah, Silverlight is a step up from that, thank the good Lord. It's close to the painless, frictionless experience you get with iPhone/iPad app installations. 

If the user clicks OK to install, Chavah will be installed like a typical application, with shortcuts on the start menu and desktop:

ChavahStartMenu.png

Additionally, the application shows up in the installed programs like a typical native application:

UninstallChavah.png

If the user runs the app from their desktop or start menu, it looks like a standard desktop application, no browser chrome needed:

ChhavahOutOfBrowser.png

With Silverlight’s out-of-browser functionality, you can turn your web app into a desktop app with native window chrome, start menu and desktop shortcuts, app icon, all that you’d expect from a normal install, but without the security problems.

Perfect, and exactly what I was hoping for with Chavah.

Making use of your $500 video card

Unlike WPF, which is Windows-only and has full access to DirectX and the power of all your local hardware at its fingertips, Silverlight runs mostly on the CPU.

But with a recent addition to Silverlight, you can now offload portions of your UI to the GPU for better rendering performance.

There is a lot of confusion and misinformation on the web about how to do this. (Here’s to hoping I don’t contribute to that, cheers! )

Most people seem to think passing the “EnableGPUAcceleration=true” parameter to your Silverlight object in the HTML page is all that’s required.

This is a false assumption.

To make use of the GPU, 2 things must happen:

  • Pass the “EnableGPUAcceleration=true” parameter to your Silverlight object in the HTML page. (If you’re running out of browser, you’ll also need to set the “Enable GPU Acceleration” option in your out-of-browser publish settings in Visual Studio.)
  • Set the CacheMode to BitmapCache for all the UI elements (or a top-level element) you want accelerated by the GPU.

For Chavah, I wanted the song tiles to be GPU accelerated; I am animating them, I’m changing their opacity, etc. This seems like a good candidate for GPU acceleration.

To do this, I made these changes:

In the HTML document containing the Chavah app, I’ve passed in the parameter indicating GPU acceleration:

HTML
<object data="data:application/x-silverlight-4," 
           type="application/x-silverlight-4" width="100%" height="100%"> 
     <param name="EnableGPUAcceleration" value="true" /></object> 

Additionally, I’ve set the CacheMode on our song tile control:

XML
<UserControl x:Class="JudahHimango.Chavah.SongControl"
   ...
   CacheMode="BitmapCache"
   ...
</UserControl>  

Also, since we’re potentially running out of browser, we need to enable GPU acceleration there, too. You can do this through the Silverlight project’s properties page:

ChavahOutOfBrowserGpuAcceleration.png

As great as Silverlight GPU acceleration is, the whole world is not [double] rainbows and unicorns; there are some important caveats as of Silverlight 4:

Some UI pieces cannot be hardware accelerated:

  • Effects (Blur, DropShadow, etc)
  • WriteableBitmap
  • PerspectiveTransforms
  • Non-rectangular clipping
  • OpacityMasks

Also, some hardware/OS combinations cannot do GPU acceleration at all:

  • On Windows XP, if your video card is not from NVidia, ATI, or Intel, or if your driver date is older than November 2004, you won’t get any GPU acceleration.

These restrictions aren’t too terrible. The only one that affects Chavah is the Effects limitation – we use the DropShadowEffect pretty heavily, and those won’t be accelerated. That said, we have few enough effects on screen at once that the load on the CPU is minimal. In fact, at runtime, Chavah typically runs at < 1% of my 2.8 GHZ CPU. Not bad at all.

So, with all these caveats and steps to getting GPU acceleration in your Silverlight app, how do you know it’s working? How do you know you set everything up correctly and that your desired elements are being passed into the GPU pipeline?

The answer is provided through a debug parameter you can send to your Silverlight object in the HTML:

HTML
<param name="enableCacheVisualization" value="true" />

With that debug parameter in place, Silverlight will tint each element that is not hardware accelerated. Running Chavah with that in place, you’ll see our song tiles are indeed accelerated:

ChavahGpuTint.png

Silverlight has tinted all non-accelerated elements. As you can see, the song tiles are not tinted (ignore the opacity on the already-played ones). This indicates Silverlight is sending these UI element bitmap caches through the GPU hardware acceleration pipeline.

For more information about Silverlight’s GPU acceleration capabilities, I encourage you to read MSDN's comprehensive article.

Social cloud collaboration = buzzword overload (but a cool feature)

With Chavah, I wanted to go a step further than Pandora and promote a sense of community through the software. With Chavah being targeted at a small, niche community, wouldn’t it be cool to have some features where the community works together to exalt the good songs and bury the bad?

In particular, I wanted to add some social features to Chavah to promote community and raise awareness of great Messianic Jewish music:

  • Messianic community’s combined rank for each song (up vote = +1, down vote = -1)
  • Messianic community’s top-ranked songs
  • Messianic community’s trending songs (recently up-voted songs)
  • Total songs streamed

And of course, we want Chavah to be intelligent and play more of what the user likes, and less of what he doesn’t like.

To accomplish these features, I used a WCF web service on the backend. This works nicely; instead of writing code to communicate with the server, Silverlight can generate a nice asynchronous API to perform calls on the server and get results back.

For data storage on the server, because Chavah doesn’t need huge scale, I didn’t need the full SQL server. Even SQL Express was more than I needed.

What I really wanted was a low-impact, single-file database, no services required. Also, I didn’t want to map objects to the database by hand; I wanted some kind of ORM on top that made it real easy.

While I toyed with the idea of going No-SQL, what ended up working for me was going with the free SQLite database. The ADO.NET driver for this is also free: System.Data.SQLite.

For object-relational mapping, I was hoping to avoid writing ADO.NET data reader code by hand. I was pleasantly surprised to find Entity Framework can be used atop SQLite, complete with full design-time support. Entity Framework will generate all the mapping code for you, leaving you to write simple LINQ queries to get at your data. Perfect.

The end result is nice clean code that looks like this:

C#
public Song[] GetTrendingSongs(int count)
{
   using (var entities = new ChavahEntities())
   {             
      var trendingSongs = from like in entities.Likes
                          where like.LikeStatus == true
                          orderby like.Id descending
                          select like;
                   
      var songs = from like in trendingSongs
                  where like.LikeStatus == true
                  join song in entities.SongInfos on like.SongId equals song.Id
                  select song;
                   
      return songs                       
        .Take(count)
        .ToArray();         
   }
} 

Look ma, no ugly SQL strings! Just sexy, intellisense-friendly LINQ.

Building the social features like trending songs, community rank, top-rated songs, and so forth, were created with ease thanks to Entity Framework and nice integration between Silverlight and WCF.

The Super Secret Song Selection Algorithm

Psst. Come ‘ere. Yeah, you. Can you keep a secret? A’ight.

I’m no algorithm wizard, but I was able to come up with (read: steal from the software community) an algorithm that picks a song based on the user’s likes and dislikes.

It’s worth mentioning again that, unlike Pandora, Chavah does not use the Music Genome Project to predict music that you’ll like. Instead, Chavah uses a much simpler algorithm that plays mostly music you like, some music you haven’t rated, and very rarely plays music you dislike.

Enter the song weight algorithm.

For every song, assign a weight:

  • Liked songs get a heavy weight (e.g. 5)
  • Unranked songs get a normal weight (e.g. 1)
  • Disliked songs get a low weight (e.g. 0.01)

To pick the next song to play, we line up all the songs, sum their weights, and pick a random number between 0 and the sum.

For example, pretend there are only 3 songs in our entire song repository: Shalom, Blessings, and King.

If the user has liked Shalom, Blessings is unrated, and King is disliked, our algorithm would assign 0 through 5 to Blessings, 5 through 6 to Blessings, and 6 to 6.01 to King. We’ll generate a random number from 0 to 6.01. If the random number is 3, we’ll play Shalom. If it’s 5.5, we’ll play Blessings. If it’s 6.01, we’ll play King.

Simple, yet effective. End result is that liked songs are more likely to play than normal songs, and normal songs are more likely to play than disliked songs. Also, if the user hasn’t rated many songs, he’s still get a good mix of liked songs and unrated. Controlling the “how much more” threshold is a simple matter of adjusting the weight constants for liked, unrated, and disliked songs.

CPian dessert special: Code Contracts whip cream with an Rx cherry on top

While not directly related to building a Pandora clone, I want to touch on 2 topics that may be impactful to the .NET software world in the next 5 years: Code Contracts and the Reactive Extensions (Rx) project. Using them in a real world Silverlight app like Chavah has been enlightening, and I wish to pass on my enlightened zen awesomeness to you fine CPians.

Reactive Extensions (Rx)

The Reactive Extensions (Rx) framework is taking the idea of LINQ and applying it to events. By “events” I do not mean standard .NET events only, but rather, really anything that comes in in an asynchronous, unpredictable way: queue messages, mouse clicks, key presses, user gestures, object change notifications, server messages, you name it.

The “reactive” part is key: rather than polling for data, like you do with LINQ and IEnumerable, Rx lets you react to changes in data by observing an IObservable<T>, a new type in .NET 4.

Rx is currently a Microsoft research project, however, it has some pieces already baked into the .NET framework. Some of the folks behind the Rx project – Wes Dyer, Bart deSmet, Eric Meijer – are a few of the brilliants minds that gave us LINQ , which has proven an overwhelming success.

Where Rx really shines is in composition. Imagine drag and drop handling code: you might store a field where the mouse was first pressed. You’d listen for mouse down. You’d listen for mouse move. You’d listen for mouse up. You’d coordinate the whole process. And when you were done, there’s a whole slew of event handlers, mutable state, and general ugliness; you can no longer see the forest for the trees.

By contrast, Rx can compose the MouseUp, MouseDown, MouseMove events together in a single declarative call: voila! You’ve implemented drag and drop.

In Chavah, I used Rx to observe changes to my view models, thus replacing the repeated plumbing of change notifications. I also used it to loosely communicate between components: rather than view model 1 talking to view model 2, I instead publish a message to a mediator, then interested parties can compose and handle that message. For example, to listen for when we start playing the Shalom song, I can write

// Use Rx to filter and react to messages, listening for the "Shalom" song to play.
mediator.Messages
 .OfType<SongPlayingEventArgs><playsongeventargs>()
 .Where(s => s.Name == "Shalom")
 .Subscribe(OnShalomPlaying);

Rx joins the power of compositional LINQ with events. Turns out, this is a powerful combination that proved quite useful in this Silverlight project.

Just as LINQ changed the way we deal with data, I predict Rx will change the way we deal with events. I’m at the point now where it’s hard to look at a problem and not see it as an operation over a stream of events.

I suggest Microsoft graduate Rx as a first-class citizen of the .NET Framework in the near future.

Code Contracts

Code contracts is a new addition to the .NET 4 framework (and Silverlight 4). It was born out of another research project at Microsoft: the Spec# language, an attempt at merging C# and design-by-contract principles. Code Contracts, however, opted for the language-agnostic route, implemented only as an API in the .NET framework without any language support.

The idea of code contracts is not new: design-by-contract programming, where the developer specifies required conditions – about methods, classes, interfaces – right in the code. Then, a tool runs during compilation to verify your conditions will never be violated.

The end result, in theory, is code that has fewer bugs, as all of your assumptions are now codified as contracts and verified by a tool. No more NullReferenceExceptions, no more ArgumentExceptions – wouldn’t that be nice?

In Chavah, I used Code Contracts primarily on the Silverlight end, with mixed results. Occasionally, Code Contracts catches a bug – which is great! But other times I’m writing lots of contracts and just not feeling the love.

You see, Code contracts works well when you control the whole API. For example, if your entire project is using APIs that utilize code contracts, you’ll get a great experience and will have fewer bugs in your code.

But that, in essence, is the problem: for Code Contracts to really work, the ocean must be boiled. That is to say, all the APIs you talk to need to utilize Code Contracts, otherwise you’re going to get a lot of noise.

Even though Microsoft owns the ocean, they haven’t done much boiling.

The reality is that much of the .Net framework is missing contracts; I’ve personally ran into some not-too-obscure Silverlight APIs that were obviously missing contracts. As a result, I had to write code to reassure the contract checker that one .NET API really will return a non-negative number.

Additionally, the code contracts framework just doesn’t have broad support right now, which introduces yet more noise. For example, Chavah uses the Ninject inversion of control framework for dependency resolution and injection, yet Ninject has no contracts, so once again, to avoid the noise I must write more code to reassure the contract tool that the sky is not, in fact, falling.

Another hardship with Code Contracts is that there is no support from the language side. C# has no love for Code Contracts. When putting contracts on interfaces, for example, you must jump through hoops and write more patty-cake code. Another example is how the contract checker doesn’t understand C# constructs like this:

C#
private readonly string foo = "bar"; 

“Oh noes!” says the contract checker, “foo might be null!”

Uhhh…no, foo is assigned just once, but the contract checker tool, being insecure about himself, blusters on about foo possibly being null. Again, C# and Code Contracts have no love for each other, and their marriage problems are passed onto you, the developer.

Bottom line: Code Contracts has potential, but until it gets broad framework support, it’s too much noise. Also, it's not smart enough to see some things won't be null. (Do I really have to check that some event handler's EventArgs is not null? Really?) Some language integration would help, too.

Summary

Building a Pandora-like music app in Silverlight was surprisingly simple. I wrote the first version in about one weekend.

Silverlight, combined with WCF integration, can be used to make client/server apps that run great on the web or on your desktop. It’s a decent choice for building applications that run on the web.

Building nice, fluid UIs in Silverlight came natural, even for this designer-challenged programmer. Control templates, data templates, easing animations, and commanding support make for nice UIs with very little code behind.

Entity Framework is maturing. Combining Entity Framework with WCF made getting data back to the client dead-simple.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer 3M
United States United States
Front end developer, RavenDB enthusiast, blogger, musician, husband, and father of 3.

Comments and Discussions

 
GeneralMy vote of 5 Pin
John Adams2-Mar-11 18:27
John Adams2-Mar-11 18:27 
GeneralRe: My vote of 5 Pin
Judah Gabriel Himango6-Mar-11 7:47
sponsorJudah Gabriel Himango6-Mar-11 7:47 
GeneralAmazing work Pin
ShimiZaki7-Feb-11 10:38
ShimiZaki7-Feb-11 10:38 
GeneralRe: Amazing work Pin
Judah Gabriel Himango7-Feb-11 10:52
sponsorJudah Gabriel Himango7-Feb-11 10:52 
GeneralRe: Amazing work Pin
ShimiZaki7-Feb-11 11:03
ShimiZaki7-Feb-11 11:03 
GeneralRe: Amazing work Pin
Judah Gabriel Himango7-Feb-11 11:28
sponsorJudah Gabriel Himango7-Feb-11 11:28 
QuestionWhat happens to animated tiles? Pin
Your Display Name Here27-Dec-10 3:39
Your Display Name Here27-Dec-10 3:39 
AnswerRe: What happens to animated tiles? Pin
Judah Gabriel Himango28-Dec-10 4:48
sponsorJudah Gabriel Himango28-Dec-10 4:48 
GeneralRe: What happens to animated tiles? Pin
Your Display Name Here30-Dec-10 4:23
Your Display Name Here30-Dec-10 4:23 
General5 from me Pin
Gandalf_TheWhite20-Dec-10 19:03
professionalGandalf_TheWhite20-Dec-10 19:03 
GeneralRe: 5 from me Pin
Judah Gabriel Himango21-Dec-10 8:25
sponsorJudah Gabriel Himango21-Dec-10 8:25 
GeneralMy vote of 5 Pin
Abhinav S11-Dec-10 22:03
Abhinav S11-Dec-10 22:03 
GeneralRe: My vote of 5 Pin
Judah Gabriel Himango15-Dec-10 5:24
sponsorJudah Gabriel Himango15-Dec-10 5:24 
QuestionAny Chance we can get a link to download code? Pin
Your Display Name Here1-Dec-10 20:37
Your Display Name Here1-Dec-10 20:37 
AnswerRe: Any Chance we can get a link to download code? Pin
Judah Gabriel Himango2-Dec-10 4:38
sponsorJudah Gabriel Himango2-Dec-10 4:38 
GeneralRe: Any Chance we can get a link to download code? Pin
lizethbabe25-Feb-11 13:52
lizethbabe25-Feb-11 13:52 
GeneralExcellent Demo! Pin
Your Display Name Here30-Nov-10 4:04
Your Display Name Here30-Nov-10 4:04 
GeneralRe: Excellent Demo! Pin
Judah Gabriel Himango30-Nov-10 4:30
sponsorJudah Gabriel Himango30-Nov-10 4:30 
GeneralRe: Excellent Demo! Pin
Your Display Name Here1-Dec-10 7:38
Your Display Name Here1-Dec-10 7:38 
GeneralRe: Excellent Demo! Pin
Judah Gabriel Himango1-Dec-10 10:29
sponsorJudah Gabriel Himango1-Dec-10 10:29 
GeneralMy vote of 5 Pin
Greg Read29-Nov-10 4:07
Greg Read29-Nov-10 4:07 
GeneralRe: My vote of 5 Pin
Judah Gabriel Himango30-Nov-10 4:32
sponsorJudah Gabriel Himango30-Nov-10 4:32 
GeneralMy vote of 5 Pin
Sean Holm29-Nov-10 2:32
professionalSean Holm29-Nov-10 2:32 
GeneralRe: My vote of 5 Pin
Judah Gabriel Himango30-Nov-10 4:30
sponsorJudah Gabriel Himango30-Nov-10 4:30 
GeneralMy vote of 5 Pin
David Ly28-Nov-10 4:17
David Ly28-Nov-10 4:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.