Tuesday, February 1, 2011

MVVM for non MVVM people : Part 2

Picking up after part 1, where our artistically challenged software developer just finished writing a view model. We will continue with the user interface designer where he will
  • Create a beautiful and eye catching user interface.
  • Use Blend Behaviors to handle button click events
  • Define a special behavior that passes a property to the view model
The UI artist is using Expression Blend. After several months of designing he ends up with this:

VideoPlayerView

With a layout like this:

VideoPlayerLayers

Our artist did not graduate at the top of his class.


Wiring Up Commands on the View


You need to first attach a DataContext to the UserControl. A DataContext will help the application bind to the commands we created and it is even helpful at design time when setting the bindings. You want to set the DataContext at the highest level you can so that all the child controls will have access to the DataContext as well. Select the [UserControl] or LayoutRoot in your layout and create a new DataContext from the properties window. You will see your view model (VideoPlayerViewModel) in the list of choices. This class was created in part one of this post. Now we can reference those commands we created in the model.
To allow those commands to be executed you need to add a behavior to the element you want to make the command call. Click the PlayButton and add a behavior asset called “InvokeCommandAction”. Notice that it will become a child of the PlayButton.

Click on the command in your layout. Set the EventName that will invoke the command to be “Click”. Now all you need to tell it is what command to invoke. Click the tiny box on the right of the command property and click “Data Binding”. The Data Context tab reveals the relevant properties you can bind to. In this case you want to bind to the PlayCommand on your model. You can just as easily edit commands straight through Xaml if you prefer. Repeat these steps for your Pause and Stop buttons.

Now you have some great buttons but no media element source to control. The last command to hook up to the view is the MediaOpenedCommand. As you would expect, it is done the same way as the buttons only
with a different EventName. Select your MediaElement in the layout and add an InvokeCommandAction so that it is a child of the MediaElement. Change the EventName to be “MediaOpened” and set the command to be “MediaOpenedCommand” just as you did with the buttons. This just means as soon as the MediaElement has successfully loaded the source defined, invoke this action.

This command will have a catch though. If you remember, our developer is expecting us to provide our MediaElement so that it knows how to control the video. We can send this information to the model with this “MediaOpenedCommand”. You need to send the loaded media element as parameter. In the InvokeCommandAction properties, click the show advanced properties arrow in Common Properties to get more info. Click the tiny advanced options box to the right of the “CommandParameter” property. This time, we want to bind using the element tab and select the MediaElement in your view. Not a property on the MediaElement, just the MediaElement object.

VideoPlayerMediaOpenedParameter

Once the command parameter is defined you are finished.

You have just made the worst video player ever created, but you can feel good that you did it without writing any code behind and you were able to separate concerns between the developer and the user interface designer.

Sunday, January 30, 2011

Mix11 Picks

I just made my picks for MIX 2011 sessions. It is interesting to see the massive number of Silverlight sessions available. This MIX looks like it will have some great sessions that are going to be relevant to the work I am interested in.

Advanced Styling and Templating for Silverlight and WPF

WCF Patterns and Practices for Silverlight

Microsoft Surface v2 – designing for the new form factor

It's Only A Game

Using Metro Motion Design To Improve App Performance

HTML 5 for .NET Pros

Building an ASP.NET MVC 3 Mobile Application

Fun with ASP.NET MVC 3 and MEF

WebMatrix, ASP.NET & Razor - The easiest and fastest way to creating websites and dynamic webpages for beginners & professionals.

Coding Animation Across Silverlight, WPF, XNA and HTML5

Open Call voting closes February 4, 2011. What were your picks? I wish I could make it in person this year but that ticket price is just a bit too much for me. I'm still really looking forward to the web sessions though. 

Friday, January 28, 2011

MVVM for non MVVM people : Part 1

In my recent explosion of Silverlight development I have seen the term Model View ViewModel being thrown around a lot. I really enjoy writing Silverlight apps so I am interested in technologies that make that experience better/easier.

The MVVM pattern is easy to pick up for an ASP.NET MVC developer because both are working towards the same separation goals. While my use with MVVM has been relatively limited so far, I have found some great applications for it and I also recognize that it may not be the right tool for every Silverlight application.
I recently created a Silverlight Video Player from scratch for a presentation that follows the MVVM pattern and I thought it worked out great. I want to take you through that process now.

It is easy to visualize the result. I just want a way to wire up a video player. When the user presses play on the video player I want it to invoke some command and execute some code. Knowing that, I want to make a command for each button or each action that might normally have code behind in Silverlight.

VideoPlayerMockup 


Why Would You Do This?


Because MVVM is a buzz word and employers like it when you know buzz words! That may be true, but some people are turned off of by buzz word topics. If you are one of those people, just try and think of MVVM as a separation of concerns between a UI artist and a developer. You don’t want the artist messing around with your code, but if your code is in the code behind you do not have a choice. Your UI artist will be editing the xaml directly which will affect how the code behind file is generated. Alternatively, the UI developer does not want the untrained eye of the software engineer to alter the artist’s work.
There are also benefits with testing. Some may not find value in that benefit but you do if you regularly test code. MVC developers should appreciate and completely understand.


Getting Started


There are a few general thoughts on how to get started. Some say to start with your view and create your model off of the components you can see that you need to wire up. For example, if you drop on a play, stop and pause button on your UI, you know that your model needs to support a play, stop, and pause command that the UI will call.
Others prefer to create the model before creating the UI. This way you can build the UI off of the public exposure of the model.
I actually prefer creating the view before the model, but in this case I will be creating the model before the UI.


Creating the Model


To create the model you need to remember that the model is just a collection of commands that the view will be calling. Literally, they are commands! The command object descends from the ICommand interface. That means each of our commands will be implementing CanExecute and Execute. You’ll need to create a class that implements these (which is not difficult) or use a third party command. Either way, you need a way to create an ICommand descendant. I will be using the Mvvm Light Toolkit.

As you have probably figured out by now, your model will be doing the direct two way communication with the view. Because of this, the model class will need to descend from the INotifyPropertyChanged interface. From there you can just go nuts and define your public interface. Just think of whatever you might need to do in your player. If you’re new to MVVM you might think it will only take a few simple video navigation commands to get this working, but you need to remember that the goal is to not have code behind. That means, you should not be writing code for loading or initializing either. If your model needs to grow to accommodate, that is fine.

public ICommand PlayCommand { get; set; } public ICommand PauseCommand { get; set; } public ICommand StopCommand { get; set; } public ICommand MediaOpenedCommand { get; set; } public ICommand SetVideoSourceCommand { get; set; }
Then your constructor can create the concrete implementation or you can use dependency injection or an Inversion of Control container if you prefer.

PlayCommand = new RelayCommand(PlayVideo, CanPlayVideo); PauseCommand = new RelayCommand(PauseVideo, CanPauseVideo); StopCommand = new RelayCommand(StopVideo, CanStopVideo); MediaOpenedCommand = new RelayCommand<MediaElement>(MediaOpened, CanMediaOpened); SetVideoSourceCommand = new RelayCommand<string>(SetVideoSource, CanSetVideoSource);
Now we can get down to writing some code for those fancy commands. When conceptually thinking about how to tell the video how to play, you have to wonder, what is the video? Video in Silverlight is displayed in a MediaElement. We will need one of those in our class.

private MediaElement _mediaElement;
The PlayVideo() method has a really easy implementation!

public void PlayVideo() { _mediaElement.Play(); }
If you want to make it more complicated you can… I like this implementation. From that I am guessing you can figure out what the implementation of the pause and stop buttons will be. One last thing to do for the play button, you will want to implement the CanPlayVideo() method. For me, I only want to play the video if it is not already playing.

private bool CanPlayVideo() { return (_mediaElement != null && _mediaElement.CurrentState != MediaElementState.Playing) }
MediaOpenedCommand is intended to be invoked when the MediaElement has been loaded with a video. This code will need to remember what MediaElement has been loaded so the other commands can work properly. You can assume that the view will be passing in the MediaElement as the parameter. We set this requirement in the view models constructor when we passed the MediaElement type to the RelayCommand.

private void MediaOpened(MediaElement mediaElement) { _mediaElement = mediaElement; }
One command that may not be intuitive is the SetVideoSource() method. It is still simple though.

public void SetVideoSource(string source) { MediaSource = new Uri(source, UriKind.Relative); }
When the view calls the SetVideoSource command it will be passing a video source location in the form of string. This code will convert that to a Uri and set it to a public property on our model that we have not defined yet. Here it is:

private Uri _mediaSource; public Uri MediaSource { get { return _mediaSource; } set { _mediaSource = value; NotifyPropertyChanged("MediaSource"); } }
This code makes me angry. It is so close to being an automatic property using { get; set; } but the NotifyPropertyChanged messes everything up! Not only that, but I do not like having to pass in “MediaSource” as a string to the changed method. I think there is a lambda equivalent but I have a short attention span and I lost interest when Googling it.

Now for the confession: you do not really need to bind all UI elements to a command on a view model. You can bind the UI to anything on your model. It can be a string, Uri, a custom Silverlight type, or whatever you want as long as your view knows how to interpret the result. The commands are used for more of a method call where you are expecting an action to be performed (like the play button). You can use other types of binding to determine:
  • The duration of the video source
  • The current location in the video timeline
  • Does the video currently require buffering
  • If the video is buffering, how much longer will it be buffering
The list goes on, but really any information you want to show or bind to on your player is what you can define in your model.

As a developer you can now tell the UI artist to slap on their squiglies, rainbows, and unicorns… or whatever it is they do. The cool part is that they can use your model and it is simple enough that they can even wire it up without writing code. That is sweet.

Thursday, January 27, 2011

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

MoviePlayerNoTheme
  • 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<Comment> Comments { get; set; } // public IEnumerable<Review> Reviews { get; set; } // public IEnumberable<CastMember> 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<Movie>) we need to convert them to data transfer objects (IEnumberable<MovieDto>). 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’.”

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

“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:

MoviePlayerProjectRelation



  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<T>. 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<MovieDto> 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!

MoviePlayerDesignData

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.

MoviePlayerCommandParameter

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. I will write another post showing what sort of UI is possible when starting with a base like this.

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

Tuesday, January 25, 2011

WPF Borderless Window Controls

WPFControlBoxThe first challenge I tackled when writing Metro Stack was creating that Microsoft Zune player feel with no border while still supporting a draggable and resizable window. I have created an isolated project to demonstrate how you can do this.
Creating a borderless window that supports resizing is simple. Here is the code:

<Window ... ResizeMode="CanResizeWithGrip" WindowStyle="None" AllowsTransparency="True"> ... </Window>
Just three properties can get you a borderless window like you would expect. WindowStyle will remove the standard control box. AllowsTransparency will remove the border. ResizeMode will give you a drag option in the bottom right of the window.

When I initially thought of creating a custom control box for the minimize, maximize, and close buttons I started thinking how annoying it would be to find or create images for each function and possibly for each state of the button. I got less interested in making them the more I thought about it. Then I heard about people doing this using Wingdings and I thought that was a great idea! This would also allow me to handle different effects for MouseOver and Pressed events easier. Getting this going was rather simple. I just created a TextBlock, changed the font to Wingdings and entered the appropriate character for the symbol I am looking for. For example, the close button would look like this:

<TextBlock x:Name="CloseButton" Text="r" FontFamily="Webdings" Foreground="Gray" Margin="5,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Top" MouseLeftButtonUp="CloseButtonMouseLeftButtonUp" />
I decided to go with traditional code behind because I find it appropriate that these controls are the concern of the view.

Accomplishing the window drag was not too bad either. For my application, I wanted just a portion of the window to be dragable. To do this I created a new grid to act as the window drag handle.

<Grid x:Name="LayoutRoot" Background="White"> <Grid x:Name="HeaderGrid" Height="50" VerticalAlignment="Top"> <Grid x:Name="DragableAreaGrid" Background="White" MouseDown="DragableGridMouseDown"/> ... </Grid> </Grid>
There were a few tricks I had to play to get the dragable area working with the control box. Notice that the DragableAreaGrid has a background of white, which is really just on top of another grid that is white. This is done so that the user actually has some surface area of the DragableAreaGrid to grab on to. If this were a transparent background you would never be able to invoke the MouseDown event of the grid.

Here is the full XAML code for this sample:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="Metro.CustomControlBox" x:Name="Window" ResizeMode="CanResizeWithGrip" WindowStyle="None" AllowsTransparency="True" Width="640" Height="480"> <Grid x:Name="LayoutRoot" Background="White"> <Grid x:Name="HeaderGrid" Height="50" VerticalAlignment="Top"> <Grid x:Name="DragableArea" Background="White" MouseDown="DragableGridMouseDown"/> <StackPanel Orientation="Horizontal" Margin="0,5,5,0" HorizontalAlignment="Right" VerticalAlignment="Top" Background="White"> <TextBlock x:Name="ChangeViewButton" Text="2" FontFamily="Webdings" Foreground="Gray" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Right" MouseLeftButtonUp="ChangeViewButtonMouseLeftButtonUp" /> <TextBlock x:Name="MinimizeButton" Text="0" FontFamily="Webdings" Foreground="Gray" Margin="5,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Top" MouseLeftButtonUp="MinimizeButtonMouseLeftButtonUp" /> <TextBlock x:Name="MaximizeButton" Text="1" FontFamily="Webdings" Foreground="Gray" Margin="5,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Top" MouseLeftButtonUp="MaximizeButtonMouseLeftButtonUp" /> <TextBlock x:Name="CloseButton" Text="r" FontFamily="Webdings" Foreground="Gray" Margin="5,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Top" MouseLeftButtonUp="CloseButtonMouseLeftButtonUp" /> </StackPanel> </Grid> </Grid> </Window>
Here is the code behind for this XAML window:

using System.Windows; using System.Windows.Input; namespace Metro { public partial class CustomControlBoxWindow : Window { public CustomControlBoxWindow() { InitializeComponent(); } private void CloseButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Close(); } private void MaximizeButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { WindowState = WindowState.Maximized; } private void ChangeViewButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { WindowState = WindowState.Normal; } private void MinimizeButtonMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { WindowState = WindowState.Minimized; } private void DragableGridMouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) DragMove(); } } }
The code behind is just slap happy code, but really there is not much more to it. Notice the condition in the DragableGridMouseDown event. You will get an exception if you try and call DragMove() if the user was dragging with any other button other than the primary button. InvalidOperationException: Can only call DragMove when primary mouse button is down.

DragMoveException

Checking for the LeftButton is probably not the best idea but it demonstrates something to watch out for. I doubt this code would work with a left handed mouse as the input device may consider the RightButton to be the primary button.

Other tricky parts to watch out for is how your DragableAreaGrid interacts with your new control box. If the controls are within the DragableAreaGrid the grid will handle your mouse events for the controls. Because of this, I have added my controls side-by-side (rather than the controls in the dragable grid) with my DragableAreaGrid. You will also run into the same background problem with the control grid if your controls sit on top of your DragableAreaGrid. Unless you put a background color on your controls you will only get a click event on your controls if you hit it perfectly on the wingding character. Don’t be sad though. There are tricks inside of tricks for that. If you truly do not want a color for your background but you need some surface area to be able to catch that mouse input you can set the alpha channel of the background to be 1% so you can’t even see the background but it’s there as a clickable space.