Watching Movies with WCF and Silverlight

I had a request from a member of the Silverlight Media Framework community to write a Silverlight application that retrieves a list of videos from a WCF service and plays the video using SMFPlayer. The member also had a requirement where he wanted to be able to have a separate Listbox show his available movies and when you click on a movie in the list it should load the movie into the player. This all started when I made a suggestion in the How to change Video Source thread and I said:

I store information about videos in a SQL Server database. I have a large number of available videos that get loaded at various times in my application. To do this I am using WCF RIA Services. The idea is that I can make a call to a WCF Service to return me a list of my videos. Then my Silverlight assembly can process those videos into PlaylistItems that get fed to the SMF player. - Steve Hook

I'm always happy to help if I can, and in this case I certainly can. Here is what you will learn in this tutorial.

Movie Player No Theme

  • Using the Silverlight Media Framework player.

  • Retrieve a list of Movies from a WCF service.

  • Creating a playlist outside of the SMFPlayer.

  • Changing the movie selection with a ListBox.

  • Keeping a strong separation of concerns when retrieving movies for playback in Silverlight.

  • Using data transfer objects to limit transfer sizes.

  • Know when to use a Silverlight Class Library and when not to.

  • Using ItemTemplates for generated ListBox items

  • Basic MVVM using MVVMLight via NuGet.

The full code is also available at my blog's bitbucket repository.

The Player

Create a Silverlight Application using Silverlight 4 and call it MoviePlayer. Host it in a web application when you are prompted. The web project will default to being named MoviePlayer.Web. It does not matter if you use ASP.NET or ASP.NET MVC or any other options. We will not be writing any web code. The test pages that are provided in the templates will be enough to demonstrate this example.

Make sure you already have the Silverlight Media Framework setup. You can download the binaries from their download page or grab them from my repository. Anyone who does Silverlight or WPF work knows that life is easier with Expression Blend. I will be using Blend for most of my XAML but it will not be necessary for this tutorial. Now, you can open either Blend or Visual Studio and start working on the MoviePlayer project. Open MainPage.xaml and define two columns on the LayoutRoot. The first column will hold the video player. The second column will hold our external playlist. Drop on the SMFPlayer in the first column. Drop a GridSplitter and Grid with a listListBox in the second column.

<Grid x:Name="LayoutRoot" Background="Black">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.673*"/>
        <ColumnDefinition Width="0.327*"/>
    </Grid.ColumnDefinitions>
    <smf:SMFPlayer x:Name="Player"/>
    <Grid
        Grid.Column="1"
        Margin="10,0,0,0">
        <ListBox />
    </Grid>
    <sdk:GridSplitter
        HorizontalAlignment="Left"
        Grid.Column="1"
        Width="10"
        Background="#FF323232"/>
</Grid>

Those namespaces are pulled from:

xmlns:smf="http://schemas.microsoft.com/smf/2010/xaml/player"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"

This will get you the basic layout of the page and we can come back to do the binding. If you were to run the application now you would see the video player with no video as well as a grid splitter that can change the size of the two columns. We know that we want to be using MVVM to bind our data to our views, so we can write some XAML in preparation for this. I like to write the view first because I feel that it creates a better, cleaner view model when you define the data the way the view would want to reference it. We know we want to provide the player a few things.

  • The playlist: The SMFPlayer needs a playlist of every movie you want it to play even if you are not using the built in playlist viewer.

  • The current playlist item: If you intend to change the currently playing movie from an external control (not from the built in playlist viewer from SMFPlayer) you will need some way of telling the player what movie you want to be watching.

  • Movies: The list of movies you have available to watch. This is not to be confused with the playlist. Perhaps you store more information about each movie than you provide in a playlist item. This list of movies can be your external playlist as demonstrated in the right column of the example screenshot above.

Writing binding support for the playlist items is simple. Modify your SMFPlayer to bind to these imaginary properties on our imaginary view model.

<smf:SMFPlayer
    x:Name="Player"
    CurrentPlaylistItem="Binding CurrentPlaylistItem, Mode=TwoWay"
    Playlist="Binding Playlist"/>

We bound the SMFPlayer property CurrentPlaylistItem to the view model's CurrentPlaylistItem property (which does not exist yet). Since the player can also change the current playlist item we have defined the binding to be two way so that the player can notify the view model that the item has changed. We bound the SMFPlayer property Playlist to the view model's Playlist property so we can provide the player a list of PlaylistItems it is permitted to show.

Binding the list of movies to the ListBox can be a bit more tricky to really show each movie in a way that you want. You can quickly just bind the ItemsSource property of the ListBox to the view model's property Movies (which does not exist yet) but that only gets you halfway there. What is a Movie? We have not really defined that yet. I envision a Movie to be a class with some basic properties on it such as title and a path to the movie's video file. The ListBox is not going to know how to display that class. You need to define a template for what each item looks like. In Blend, this is done by selecting the ListBox, right click > Edit Additional Templates > Edit Generated Items > Create Empty. Outside of blend you would need to write the code.

<UserControl.Resources>
    <DataTemplate x:Key="MovieListItem">
        <Grid>
            <TextBlock
                Text="Binding Title"
                Foreground="White"/>
        </Grid>
    </DataTemplate>
</UserControl.Resources>

<ListBox
    x:Name="listBox"
    Background="x:Null"
    BorderBrush="x:Null"
    Foreground="White"
    BorderThickness="0"
    ItemTemplate="StaticResource MovieListItem"
    ItemsSource="Binding Movies"/>

In this code I created a local resource then used it later in the ListBox by binding the ItemTemplate to the resource. You can also see the ItemsSource binding to the view model's Movies property. My template is very basic, but know that this could be anything you want. Your template needs to have a Grid, but inside that grid it can be however you would like to show what a Movie is. My template simply has a TextBlock that binds a Movie property Title (that does not exist yet). Notice in my binding of Title that I did not need to say it is Movie.Title or Movie[x].Title. The application knows that because I bound my ItemsSource of my ListBox to be a list of Movies and the template is for one item of the ListBox, that the template will only be dealing with one Movie. Therefore, I can write my binding code directly off of a Movie class. That's pretty awesome.

I think we have a good idea of what our models will look like now, but we still don't know what this movie class is and where it comes from.

The Domain

Your domain and data access layer should be separated from your implementation. Start by creating a new .NET class library project called MovieLibraryData. This project is to demonstrate a layer that you may not have much control over. This is likely driven by Entity Framework models or some other form of connecting to a database of Movies. In my case this will contain a basic Movie class.

public class Movie
{
    public string Title { get; set; }
    public string Path { get; set; }
    // public IEnumerable Comments { get; set; }
    // public IEnumerable Reviews { get; set; }
    // public IEnumberable Cast { get; set; }
    // public byte[] Poster { get; set; }
}

You should envision this class as having several complex types and possibly some methods/business logic. This could have collections of reviews or comments. Links to actors/actresses, producers, directors, etc. The point is, this could potentially be a complicated class.

For our movie player we really do not care about all that data. we just want the movie path and a little info about the movie. If you start thinking about where this data will come from you will realize it will be coming over an http request through a web service we have not created yet. So we know we need a simple class that is light weight and serializable. We need a data transfer object.

Create a new .NET class library project called MovieLibraryDto. This project will hold all of our light weight versions of the classes from MovieLibraryData so that we only have to send the properties we care about on the client. In the MovieLibraryDto project, create the class MovieDto.

[DataContract]
public class MovieDto
{
    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Path { get; set; }
}

You'll notice I did not use the [Serializable] attribute on the class. Instead I used a DataContract with DataMembers which comes from System.Runtime.Serialization. I have found this is a better approach. DataContracts give you control over the name and the namespace of the class and its properties as seen from the client. If you were to mark the class [Serializable] you will find many strange member names on the class when you get it on the client through a WCF service.

The Service

We need a way to get the movies from our domain/database to the client. Picture all of your movie watchers accessing a server out there somewhere that needs to give them the movies they want to watch. You need to create a service that can send those movies back to them. Then we'll get into how Silverlight can call this service.

Create a new WCF Service Application project and call it MovieService. Add a reference to MovieLibraryData and MovieLibraryDto. If you look at what you get with this template you will see an interface and a svc file that implements that interface. Open the interface and rename it (using ReSharper or Visual Studio refactor rename) to IMovieRetrievalService.cs. Clear out the sample code Visual Studio adds so that your class looks like this

[ServiceContract]
public interface IMovieRetrievalService
{
    [OperationContract]
    IEnumerable<MovieDto> GetMovies();
}

We just want a simple method to get all of our movies. Your method could get specific movies if you wanted. You should notice a few things about our interface.

  • This interface is defining what we want our service to be capable of.
  • We are using those ServiceContracts again. These are required by WCF.
  • We are sending back a Dto. This is great because they are so light weight.

Open the .svc file and rename it to MovieRetrievalService and have it implement our new interface IMovieRetrievalService. Even Resharper can get confused when renaming a service file. You should check the markup of the svc file to make sure it reads Service="MovieService.MovieRetrievalService". In the MovieRetrievalService.svc.cs file we can write code to pull out our movies from the domain level. Once we pull out the movies (IEnumerable) we need to convert them to data transfer objects (IEnumberable). Here is my code.

public IEnumerable<MovieDto> GetMovies()
{
    IEnumerable<Movie> movies = GetMoviesFromDatabase();

    // You could also use AutoMapper to map the domain model
    // to the data transfer objects
    return movies.Select(movie => new MovieDto
    {
        Title = movie.Title,
        Path = movie.Path
    });
}

I call a mysterious GetMoviesFromDatabase() method that returns domain level Movie objects where I project each movie into a new MovieDto object and return that list. Mapping the Movie class to the MovieDto class is tedious in this case because the field names match one to one. A cleaner approach may be to use AutoMapper to do that mapping for us.

My GetMoviesFromDatabase() method call is just creating a static list of movies because I did not want to go through the trouble of creating a database in this example. You could replace this with retrieving data from your favorite database provider or repository that likely lives on the domain side.

private IEnumerable<Movie> GetMoviesFromDatabase()
{
    return new List<Movie>
    {
        new Movie
        {
            Title = "Microsoft Wildlife sample",
            Path = "http://smf.vertigo.com/videos/wildlife.wmv"
        },
        new Movie
        {
            Title = "We're All In at PDC10",
            Path = "http://ecn.channel9.msdn.com/o9/ch9/2497/0e8a5166-3984-4a91-925d-9e1500f92497/SharePointSideshow02_ch9.wmv"
        },
        new Movie
        {
            Title = "Silverlight TV 50",
            Path = "http://ecn.channel9.msdn.com/o9/ch9/d274/e69539b7-2e75-462d-a614-9e150175d274/sltv50_ch9.wmv"
        },
        new Movie
        {
            Title = "Silverlight TV 16",
            Path = "http://ecn.channel9.msdn.com/o9/ch9/6/7/5/6/3/5/SLTV16SL4RCPanel_ch9.wmv"
        }
    };
}

It just so happens that each of my videos I selected are wmv files. This is not a requirement. Silverlight has several other supported video formats.

Before we move on you should be aware of access policies. You have the option to define who can see and call your service. This is done through a ClientAccessPolicy.xml file. Create this file with that exact name in root of the MovieService project. You can start with a base that gives everyone access to this service by using this code:

<xml version="1.0" encoding="utf-8"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="*">
                <domain uri="*"/>
            </allow-from>
            <grant-to>
                <resource path="/" include-subpaths="true"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

You should now be able to right click the MovieService project and debug run the service to open a port. If you are looking for the traditional welcome page from other types of web services you can click on the MovieRetrievalService.svc in the directory listing when the web page comes up.

Calling The Service

Now that you are capable of retrieving the list of movies, you just need to find something to call the service. Your first thought might be just add a service reference from MoviePlayer so that you can call the service from the Silverlight assembly. Unfortunately you cannot do this. You will get several errors:

Custom tool error: Failed to generate code for the service reference "ServiceReference1".

and

No endpoints compatible with Silverlight 4 were found. The generated client class will not be usable unless endpoint information is provided via the constructor.

and

Cannot import wsdl: binding ""

The point is, Silverlight is upset. You cannot call a WCF service directly from the Silverlight application. In fact, your Silverlight application can only interact with Silverlight class libraries. Create a new Silverlight class library project called MovieServiceClient. Right click the references in this new project and add a service reference. If you click discover you should see your localhost web service running from the previous steps. Name this reference MovieServiceReference. Once you click OK and get your service reference you will notice a new file called ServiceReferences.ClientConfig. This is an evil file. It stores all the connection information for if you ever need to update your service reference. This is evil because this information can change depending on your build configuration but it's not in the web config so you cannot really do a web config transform. We'll get back to that.

Now you can reference the MovieServiceClient project from MoviePlayer because they are two Silverlight assemblies talking to each other. Exciting, I know.

We can revisit the Silverlight UI, but before we do that I want to make sure a few things are clear. You have created six projects. It is important to understand each project and its role:

Movie Player Project Relation

  1. MoviePlayer.Web - Provides a way to show the Silverlight application in a browser.

  2. MoviePlayer - This is our Silverlight application that holds our movie and navigation controls. This is the user interface.

  3. MovieService - The service lives on the server. It communicates with the database/domain models to retrieve the movie objects, convert them to data transfer objects (dto) and send the light weight objects back to the client.

  4. MovieServiceClient - This is a Silverlight class library that is reverenced by MoviePlayer. It provides a web reference to MovieService to allow the Silverlight application to make a WCF service call.

  5. MovieLibraryData - A .NET class library to mimic a real and possibly complicated data access layer. The MovieService will be using the Movie class in this library and convert it to a MovieDto.

  6. MovieLibraryDto - A .NET class library that contains light weight object designed to be serialized. The MovieService will be using the MovieDto class in this library and send it back to the MovieServiceClient.

Setting up MVVM

Make sure you have NuGet installed and add a reference to Mvvm Light Toolkit in the MoviePlayer project by right clicking References and selecting "Add Library Package Reference". If you are not familiar with NuGet, you can think of it as a fancy way to add DLLs to your project.

Create a class called MoviePlayerViewModel.cs in the MoviePlayer project. This class will contain all the movie information you will be showing on the view. The view model just organizes this information so the view can simply reference a property on the view model to get its data. This class needs to implement the interface INotifyPropertyChanged found in System.ComponentModel.

public class MoviePlayerViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

Now you can add public properties to this class. Every public property you provide here will be available to the view. Lets add a few that we know we will need. We know we will have a playlist, and if you look at the SMFPlayer you will see that the Playlist property is expecting a collection of PlaylistItem. MVVM binding works off of a System.Collections.ObjectModel.ObservableCollection. Knowing this we can start to create some properties on our view model.

private ObservableCollection<PlaylistItem> _playlist;
public ObservableCollection<PlaylistItem> Playlist
{
    get { return _playlist; }
    set
    {
        _playlist = value;
        NotifyPropertyChanged("Playlist");
    }

private ObservableCollection<MovieDto> _movies;
public ObservableCollection<MovieDto> Movies  ... 

private PlaylistItem _currentPlaylistItem;
public PlaylistItem CurrentPlaylistItem  ... 

See the list of Movies? It is of type MovieDto that we created in the MovieLibraryDto project. You would think you would need to add a reference to that project to get this type, but remember that you cannot add a reference to a non Silverlight class library within a Silverlight application. You will get this type through the MovieServiceClient when you created the service reference to MovieService it recreated those types so the MovieServiceClient can provide those types to the assemblies that call it.

Create a constructor in the view model and lets get some movies from that service.

public MoviePlayerViewModel()
{
    ...
    PopulateMovies();
}

private void PopulateMovies()
{
    var movieService = new MovieRetrievalServiceClient(
    {
        new BasicHttpBinding(),
        new EndpointAddress("http://localhost:49885/MovieRetrievalService.svc"));
    };
    movieService.GetMoviesCompleted += GetMoviesCompleted;
    movieService.GetMoviesAsync();
}

We got a MovieRetrievalServiceClient class from MovieServiceClient.MovieServiceReference. Notice all the parameters on the constructor. You don't actually need these. Instead you could provide a copy of the ServiceReferences.ClientConfig file from the MovieServiceClient in the root of the MoviePlayer project. Why would you do that though? We have already established that file is evil. Create the bindings in code that way you could extract out that information into a config file or something where you could change the endpoint without rebuilding the solution or ripping apart the XAP file. Make sure your path points to the localhost port that was opened when running your WCF Service.

The client class has a GetMoviesAsync method on it that calls our GetMovies method on the MovieService. You need to give it a callback to execute once the asynchronous method call completes.

private void GetMoviesCompleted(object sender, GetMoviesCompletedEventArgs e)
{
    Movies = e.Result;
    if (Movies == null)
        return;

    var playlistItems = Movies.Select(movie => new PlaylistItem
    {
        Title = movie.Title,
        MediaSource = new Uri(movie.Path)
    });

    Playlist = new ObservableCollection<PlaylistItem>(playlistItems);
    CurrentPlaylistItem = Playlist.FirstOrDefault();
}

e.Result is our strongly typed list of ObservableCollection just like we defined on our Service interface. We can save off that list of movies but we also need a list of playlist items to feed to the Silverlight Media Framework player. We can project new playlist items from the list of movies as shown in the code snippet. Then create a new observable collection with this list of playlist items.

Connecting the View Model to the View

If you've been paying attention you know that we have never told the view how it retrieves its data. We have told it what properties to pull from, but never from what object. To do this we can simply add a DataContext to the LayoutRoot of the MainPage.xaml. Before you add the DataContext you should think about what that does when you add it. You are really saying everytime you show the MainPage I want you to create a new MoviePlayerViewModel and use that for your data. This literally calls the default constructor, even when in design mode (Expression Blend and Visual Studio designer). You need to be aware of this and handle it appropriately. For instance, if we are in the designer we do not want our code calling the service and pulling down movies. Although, it would be nice to see some sample data so we can see how the page is organized with data on it. There are mechanisms in place to do just that. You can rewrite your PopulateMovies() method to use DesignerProperties.IsInDesignTool.

private void PopulateMovies()
{
    if (DesignerProperties.IsInDesignTool)
        Movies = new ObservableCollection<MovieDto>
        {
            new MovieDto
            {
                Title = "Sample Movie 1",
                Path = "http://sample.path"
            },
            new MovieDto
            {
                Title = "Sample Movie 2",
                Path = "http://sample.path"
            },
            new MovieDto
            {
                Title = "Sample Movie 3",
                Path = "http://sample.path"
            }
        };
    else
    {
        ...
    }
}

Now you can safely attach your view model to your view. In Blend you can select the LayoutRoot of MainPage.xaml. Click new DataContext and select MoviePlayerViewModel.cs from the list. In xaml it looks like this:

<Grid x:Name="LayoutRoot" Background="Black">
    <Grid.DataContext>
        <local:MoviePlayerViewModel/>
    </Grid.DataContext>
    ...
</Grid>

with a namespace of xmlns:local="clr-namespace:MoviePlayer" defined. Once you add the DataContext to your xaml and you have your bindings defined from previous steps you will start to see some data in your view!

Movie Player Design Data

Run the Web application (MoviePlayerTestPage.aspx or MoviePlayerTestPage.html) and you will see even more. Now it knows you are not in the designer and uses the WCF service to pull down the list of movies. It populates a list of playlist items, a current playlist item, and the list of movies on the side just like the screenshot from the beginning of the post.

This is great progress, so congratulations.

Changing the Current Movie

If you click on the playlist built into the SMFPlayer and select a different movie you can change the currently playing movie. One of the goals of this tutorial was to have an external playlist. If you click on another movie in the list to the right of the SMFPlayer it will not change the currently playing movie. We can fix this.

This time let's start at the view model and create a new property for handling the change in movie.

public ICommand MovieSelectionChangedCommand { get; set; }

MVVM commanding works through the ICommand interface. ICommand works off of the idea that you have code you want to execute when this command happens and you also have code you want to execute to determine if you can execute the command. ICommand can also support parameters that the view can pass you. You could write your own implementation of an ICommand or you could use one from the Mvvm Light Toolkit (which you installed in the MVVM section). This toolkit has an implementation of ICommand called RelayCommand that supports defining the type of CommandParameter you expect the view to be sending you. In this case we want the view to tell us what movie the user selected. We can create a command for this in the constructor of the MoviePlayerViewModel.

public MoviePlayerViewModel()
{
    MovieSelectionChangedCommand = new RelayCommand<MovieDto>(MovieSelectionChanged, CanMovieSelectionChange);
    ...
}

I am using the generic version of RelayCommand to say that my view will be providing me a MovieDto as a parameter. The first parameter to the RelayCommand constructor is what method to execute when the command is called. The second parameter is what method to execute to determine if the command can be executed. You will need to implement these methods.

private bool CanMovieSelectionChange(MovieDto movie)
{
    return movie != null;
}

private void MovieSelectionChanged(MovieDto movie)
{
    var playlistItem = Playlist.Where(item => item.Title == movie.Title).FirstOrDefault();
    CurrentPlaylistItem = playlistItem;
}

I have a rather crude way of finding the playlist item from the playlist. You could change that to something more realistic if you had some unique identifier for a movie. Once you have the playlist item you just set the item to be the current playlist item which is bound to the view. When the CurrentPlaylistItem changes the NotifyPropertyChanged method in the setter will tell the view that it has a new playlist item and the SMFPlayer will change the movie.

Now that we have a command written up in the view model we can attach it to the view. In Blend, select the ListBox, go to Assets > Behaviors > double click InvokeCommandAction. This will nest the action with the ListBox. There are three properties you need to change.

  • Change the event to be SelectionChanged.
  • Click the advanced options of the Command property in Common Properties. Select Data Binding. Select the MovieSelectionChangedCommand from the Data Context tab and click OK. If you do not see the command in the list you may need to rebuild the solution.

  • Click the Show Advanced Options arrow in the Common Properties. Click the advanced options of the Command Parameter property. Select Data Binding. On the Element Property tab select the ListBox on the left panel and the SelectedItem property on the right panel.

Movie Player Command Parameter

Remember that we used a list of Movies as the ItemsSource for the ListBox. Even though the SelectedItem says it is of type Object, we know that is going to be a MovieDto and that's awesome. In the end we know that when the ListBox selection changes our command is going to call the view models CanMovieSelectionChanged(movieDto) if that is true it will call MovieSelectionChanged(movieDto) Here is the xaml you just generated.

<ListBox x:Name="listBox"
    ...
    ItemTemplate="StaticResource MovieListItem"
    ItemsSource="Binding Movies" BorderThickness="0">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction
                Command="Binding MovieSelectionChangedCommand"
                CommandParameter="Binding SelectedItem, ElementName=listBox"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

Once you have the command finished you can run it and see that if you click on a different movie it will change the movie in the player.

Summary

The goal of this post was to give you the functionality required to get a list of movies from a WCF service and feed them to a SMFPlayer. This base will allow you to make changes to suit your needs whether it is domain level or UI level.

You can download the full source from my bitbucket blog code repository