An Advanced Video player

This is part 2 to the article Silverlight 4 Video Player. That article focused on the View Model Style (Model-View-ViewModel) pattern, and how it supports Designer / Developer collaboration and workflow. However, when others tried to use that project for a real website, they found they needed the full set of controls that a video player normally provides. I decided to use this as an opportunity to dig deeper into View Model Style and demonstrate how easy it truly is, when using Microsoft Expression Blend 4 (or higher). This time we will cover the "hard stuff". However, implementing this will be surprisingly easy.

This project creates a fully "Designable" Silverlight Video Player. This is not to be confused with a "Skinable" Video Player. A Skinable Video Player allows you to change the look and feel of the existing controls of the Video Player. A Designable player, allows a Designer to use ANY set of controls to implement the Video Player. For example, a Designer could replace all the buttons with a single dial that a user rotated into different positions, or they could change the method a user uses to select from the list of available videos. This can be done in Expression Blend without writing any code.

Additional Downloads

View Model Style (An overview)

The View Model Style pattern allows a programmer to create an application that has absolutely no UI. The programmer only creates a ViewModel and a Model. A designer with no programming ability at all, is then able to start with a blank page and completely create the View (UI) in Microsoft Expression Blend 4 (or higher).

Yes, you could just put your code in the code behind (for example, you place a button on a page and then double-click on it, and it wires up an event handler in the code behind), but then a Designer could only use a button for that task. They could not change the element to a Dial. Most importantly, the Designer would need to know a bit about coding because they would be directly interacting with the code. With View Model Style (or Data Driven Application as it is now referred to in the Expression Blend templates), basically:

The Programmer creates code that consist of Properties, Collections, and Commands.

The Designer uses Expression Blend, and creates the complete UI, without writing any code.

The Model is where the data goes, usually this is a web service. The View is the UI, or the "front end". The ViewModel is where the "magic" happens. It consists of:

Properties – One of something. This could be a String or an Object. Implements INotifyPropertyChanged, so that any element bound to it, is automatically notified whenever it changes.

Collections – A collection of something. This is of type ObservableCollection, so that any element bound to it, is automatically notified whenever it changes.

Commands – An event that can be raised. Also, one parameter of type Object can be passed. This implements ICommand.

The Starter Solution

We will start with the solution contained in the MVVMVideoPlayer_Starter.zip file. When you run the project, the web service in the web project, will detect what videos are in the "Video" folder, and pass them to the Silverlight application.

The list of videos will show in the combo box dropdown.

A progress bar will show the current progress.

The exact location of the video will display above the video.

The current and total time of the video will also display.

The Play button will play the video

The Stop button will stop the video.

This is what the Silverlight 4 Video Player article covered. To make a full featured video player, we need to add the following features:

Volume control - A user needs the ability to change the volume of the video.

Pause button - A user needs to be able to pause the video and resume play by clicking the pause button again, or by clicking the play button.

"Seek" control - A user should be able to easily skip forward and back through a video even while the video is playing.

"Video Buffering" notification - When the video is loading, a notification needs to appear, and indicate how much longer it will take for the video to start playing.

Full Screen Video - When viewing a video, it is nice to have the option to click a button and see the video full screen. You need to be able to switch back and forth seamlessly. Also, only the video should be full screen, not the entire Silverlight application.

We will implement the code for all of this by only adding code to the ViewModel. the Model is not changed at all. The code is already in the MVVMVideoPlayer_Starter.zip file (it will be covered in detail at the end of this article). For now, we will start with the steps the Designer would use to implement this additional functionality. To start, the Designer opens the project up in Expression Blend...

The Volume Control

This control actually does two things:

Indicates what the current volume is

Allows the volume to be changed

In Expression Blend, in Assets, get a Slider control.

Drop it in the [StackPanel] in the Objects and Timeline window.

Click the "V" key on the keyboard to switch to the Selection tool. Then on the design surface, use the mouse to widen the slider a bit.

In the Properties for the Slider, set the left Margin to 5

Set the Maximum level to 1 and Minimum to 0 (the volume on a Media Element actually goes only from 0 to 1. For example it starts at 0.5.)

Click the Advanced options box next to Value (in Common Properties).

Select Data Binding...

Select the Element Property tab

Select mediaElement in the Scene elements section

Select Volume in the Properties section

Select TwoWay for Binding direction

Click the OK button

Hit F5 to build and run the project. The web browser will open and you will now be able to control the volume.

The Pause Button

Again, there is more than meets the eye:

When you click Pause the video should stop

If the video is paused and you click Pause again, it should continue from where it left off

If the video is paused and you click Play, it should still just continue from where it left off

In Assets, get a Button control.

Drop it between the PlayButton and the StopButton, in the Objects and Timeline window. Right-click on it and rename it to "PauseButton".

In the Properties, set the Content to "Pause" and set the left Margin to 5.

In the Assets, get an InvokeCommand behavior.

Drop it under the PauseButton (remember you renamed it in the earlier step), in the Objects and Timeline window.

In the Properties for the behavior, select Data bind next to Command.

Select PauseVideoCommand in the Data Context window, and click the OK button.

Hit F5 to build and run the project. The web browser will open and you will now be able to Pause the video.

The Seek Control

We want to provide the ability to skip forward and backward while the video is playing. This functionality can be implemented using any control. For example, you could use a Slider control or a custom dial control. In this example, we will just use the ProgressBar control. The ProgressBar control will still display the progress, but when you click on the control, it will navigate to the section of the video.

In the Assets, get an InvokeCommandAction behavior.

Drop it under the mediaElement in the Objects and Timeline window. Right-click on it and rename it to "SetSeekControl".

In the Properties for the behavior:

Set the EventName to MediaOpened (this event is fired automatically when a video is set as a source for the MediaElement and it has found the video and is ready to play it).

Click the Data bind button next to Command.

Bind it to SetSeekControlCommand.

Click the Advanced options box next to CommandParameter.

Select the Element Property tab

Select progressBar in the Scene elements section

Select ProgressBar in the Properties section

Click the OK button

Hit F5 to build and run the project. The web browser will open and you will now be able to skip to parts of the video by clicking on the ProgressBar control.

Video Buffering Notification

When the MediaElement is playing a video, it loads a bit of the video "ahead" of the part that is playing. If it is not able to load the video fast enough, it will stop and "buffer" the video and then resume playing after buffering is complete (when buffering has reached 100%). We want the buffering notification to perform the following functions:

Display when the video is being buffered

Disappear when the video is not being buffered

Display the percentage that it has buffered, in real time, as it is being buffered

Bind the IsBusy property to MediaBufferingProperty (this controls when the buffering box displays).

You will know you have bound the properties because they will have a gold box around them.

Full Screen Video

For the full screen video functionality we want the following things:

When in full screen mode, we want to only see the video, not the other controls

When we switch back and forth, we want the video to continue playing seamlessly

We want the video to go to full screen, if we click on the video, or the Full Screen button

Switching a Silverlight application to full screen mode is very simple. Switching only one element of a Silverlight application, involves a few extra steps. We need to create a Grid, then instruct the ViewModel to use this Grid when going into full screen mode. The ViewModel will automatically create a VideoBrush and set it's source to the MediaElement so that the video stays in sync in both modes.

Ion the Panel section on the Tools bar, select Grid.

Double-click on Grid to insert it onto the design surface.

in the Properties for the Grid:

Set the Width and Height to Auto

Set the RowSpan to 2

Set HorizontalAlignment and VerticalAlignment to Stretch

Drag and drop an InvokeCommand behavior under the MediaElement and rename it to "FullScreen".

In the Properties for the behavior, bind the Command to the SetFullScreenCommand.

Bind the CommandParameter to the [Grid].

This will enable full screen mode when you click on the video. Now to trigger a return to normal mode...

Drag and drop an InvokeCommand behavior under LayoutRoot in the Objects and Timeline window.

In the Properties for the behavior:

Set the EventName to SizeChanged

Bind the Command to ExitFullScreenCommand

Bind the Grid to CommandParameter

Hit F5 to build and run the project. When you click on the video it will switch to full screen mode.

Return to Expression Blend and perform the following functions to create a Full Screen button:

Place a button next to the Stop button and set it's content to "Full Screen"

Set the left Margin to 5

Drag and drop an InvokeCommand behavior under it

In the Properties for the behavior, bind the Command to the SetFullScreenCommand

Bind the CommandParameter to the [Grid]

The Code

- Designers can skip to the end, there is nothing to see here :) -

Let's look at the relevant code used to implement each section...

Volume

There is no code! The Slider control is bound directly to the Volume property of the MediaElement. I know... Wow!

Pause

In the constructor of the ViewModel, all the ICommands are set-up including the PauseVideoCommand:

The rest of the implementation is below. Basically it simply calls Pause() on the MediaElement. The rest of the code is handling if it's ok to pause, if it can pause, and if it is paused already, should it play instead.

Seek

This one is a bit interesting, Basically you first pass in a FrameworkElement that will be used to perform the Seek. Mouse events are attached to this Framework element, so that an event is raised if a person clicks on the element. When the event is raised, the width of the element is used to determine where they clicked on the element. That value is used to determine where to navigate in the video.

Note: In hindsight, it may be possible to implement this entire example without using a DispatcherTimer. An InvokeAction behavior on the MediaElement may be all that is required to update the properties... oh well, perhaps next time.

Full Screen Video

The Designer must specify a Grid to be used for full screen mode. This Grid must be set to stretch and fill the entire screen, blocking out the rest of the application (so yes, the rest of the application is actually still behind the Grid in full screen mode).

The code dynamically places a VideoBrush in the Grid and sets it's source to the MediaElement so that the video will stay in sync. It is possible to put other elements in the Grid, so that you could, for example, show player controls when the mouse is moved.

#region SetFullScreenCommand
public ICommand SetFullScreenCommand { get; set; }
publicvoid SetFullScreen(object param)
{
if (MyMediaElement != null)
{
// Put application in full screen mode
var content = Application.Current.Host.Content;
content.IsFullScreen = true;
// Set the Video Brush to the content of the MediaElement
VideoBrush objVideoBrush = new VideoBrush();
objVideoBrush.SetSource(MyMediaElement);
objVideoBrush.Stretch = Stretch.UniformToFill;
// A Grid to show in full screen needs to be passed as the parameter
// Set the background content of that panel to the VideoBrush
// Note: Other elements and controls can be placed on this Grid
// It does not have to be blank
Grid objGrid = (Grid)param;
objGrid.Visibility = Visibility.Visible;
objGrid.Background = objVideoBrush;
}
}
privatebool CanSetFullScreen(object param)
{
// Only allow full screen if not in full screen
var content = Application.Current.Host.Content;
return (!content.IsFullScreen);
}
#endregion

This is the code when you exit full screen:

#region ExitFullScreenCommand
public ICommand ExitFullScreenCommand { get; set; }
publicvoid ExitFullScreen(object param)
{
// A Panel to show in full screen needs to be passed as the parameter
// Set this element to invisible
Panel objPanel = (Panel)param;
objPanel.Visibility = Visibility.Collapsed;
}
privatebool CanExitFullScreen(object param)
{
// Only allow exit full screen if not in full screen
// This may seem odd, but this command is being called by a SizeChanged event
// If we ARE in full screen then we were actually just recently NOT in full screen
// We only want to fire the ExitFullScreen if we were actually just In full screen
// but NOT in full screen any more
var content = Application.Current.Host.Content;
return (!content.IsFullScreen);
}
#endregion

"Pimp" This Video Player, Please!

Are you a Designer, or do you want to be? Download this project and come up with a cool design. The code is Open Source so you can redo the application and post a tutorial or blog showing what you create. To learn how to use Expression Blend, all you have to do is go to:

Love to get time to Style this, but that Sacha guy is a hard task master!Bet he's laughing his arse off: That his request for me to document Styling something like a ListBox, is going to be maybe take me 4 articles...

Anyway, I'll have a think, but anyone else who wants to jump in and use the WCDoor Button go for it, as it is a toggle button. And from Michael tells me, requires a bit more work (I got confused!)