Amazon.com Widgets Sample Scrolling Silverlight Video Playlist

WilliaBlog.Net

I dream in Code

About the author

Robert Williams is an internet application developer for the Salem Web Network.
E-mail me Send mail
Code Project Associate Logo

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2009

Sample Scrolling Silverlight Video Playlist

Click here to learn more on Verizon Online DSL.

 A real world example of this technique can be seen at http://www.thejamesbondmovies.com

TAKE ME TO THE VERSION FOR SILVERLIGHT 2

It only took me about an hour to create this example. I started by dragging all 8 videos into Microsoft Expression Encoder, selected the "Expression" player template and clicked encode. This gave me the full player functionality you see here, play, stop, pause, etc. and by default it simply played all 8 videos one after the other. But I wanted my users to be able to pick and chose which videos to play, so I opened the project in Microsoft Expression Blend 2 September preview and resized the outer, root canvas by setting the height to 593 to give me room to place the thumbnails, and gave it a black background color.

The XAML 

Next I created the arrows that move the playlist left and right. If you have arrows as PNG images you can use them, but I chose to create them using XAML by drawing a white square, filling it with white color converting it to a path, and then using the pen tool to delete a corner and thereby converting it to a triangle which I simply rotated, moved and sized until it looked right. Set the cursor property to "Hand" so that users know it is a button. I then copied it, rotated it to point the other way and moved the second arrow into position on the other side. This gave me the XAML below which appeared just before the closing </canvas> tag.

<!-- Playlist region starts here -->
<!-- Navigation Arrows  -->
<Path x:Name="LeftArrow" Opacity="0.74" Width="38" Height="38" Stretch="Fill" Stroke="#FF000000" Canvas.Left="11" Canvas.Top="514" Data="M37.5,0.5 L37.5,37.5 0.5,37.5 z" Fill="#FFFFFFFF" Cursor="Hand" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="134.119"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Path x:Name="RightArrow" Opacity="0.74" Width="38" Height="38" Stretch="Fill" Stroke="#FF000000" Canvas.Left="588" Canvas.Top="514" Data="M37.5,0.5 L37.5,37.5 0.5,37.5 z" Fill="#FFFFFFFF" Cursor="Hand" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="314.365"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Path.RenderTransform>
</Path>

Next I dragged the first thumbnail (for movie # 1) onto the area, roughly where you see it now, resized it and moved it into position. I named it "play0", set its opacity to 74% to make an easy rollover effect that you'll see later on; then went into the XAML editor and simply copied the Image element 3 more times, renaming each one in turn to play1, play2, etc. and updating the Source property of each to point to the right thumbnail image. Next I had to move the new images, since they all sat on top of one another, using the basic formula left=previousImageLeft + Image Width + 4 pixels. Then playing with the spacing between them until it was roughly even. Given the width of my thumbnail and the width of the canvas it turned out to be impossible without further resizing and in the end I decided that for this example it was good enough. So I now had my four thumbnail "buttons" making up my playlist and if you can fit all of your items on the screen you can skip ahead to the JavaScript, but if you want to have them scroll keep reading.

I grouped these four images into a canvas - have Blend do this for you by control + clicking on the object names (in this case play0, play1, etc.) in the Objects and Timeline area and then pressing ctl + G or right clicking and choosing Group Into > Canvas. It is better to have blend create the canvas for you as it will update all the canvas.left, canvas.top properties for you. I called this new canvas "playlist1", then using the XAML editor copied it and created "playlist2", naming the objects play4, play5, etc. and updating the Source of each as before. Then I moved this canvas off the screen by simply setting the canvas.Left Property to a value I knew would push it out of sight. Finally, I grouped playlist1 and playlist2 into another canvas, this time calling it "Library".

Using the timeline editor I created a simple animation that moved the "Library" Canvas 613 pixels to the left over the course of 2 seconds. If you have never done this before it is really easy:

The video content presented here requires JavaScript to be enabled and the latest version of the Macromedia Flash Player. If you are you using a browser with JavaScript disabled please enable it now. Otherwise, please update your version of the free Flash Player by downloading here.

As you can hopefully see on the video, you simply click on "Open, create or manage Storyboards", click "Create new", give it a name, make sure "Create as Resource" is checked so that we can access it through code, move 2 seconds into the timeline and add a keyframe to start the recorder, then make your changes. In this case we are changing the Left poperty so that it moves 613 pixels to the left - just far enough to bring the second "page" of buttons onto the screen. Stop the recorder and as you scrub through the timeline you can see the animation or click the play button to preview it.

Making it animate back the other way was a little trickier to do using the IDE, so I simply copied the XAML and changed the values myself.

Now if you have been trying this yourself you might be wondering why your thumbnails can be seen moving underneath, or even on top of the left and right arrows created earlier, while mine do not. The answer is that I have put my "Library" Canvas inside another Canvas called "ClippedCanvas" which has been "clipped", or cropped if you prefer using RectangleGeometry. Everything that falls outside the geometry you provide is hidden, or "clipped." The numbers represent X coordinates, Y coordinates, Width & Height in that order. X & Y in this case are relative to the container canvas ("ClippedCanvas"). So basically I am cropping an area from the top left of where Clipped Canvas begins, 550 pixels wide and 114 high, anything within the canvas that falls outside that region will not be seen. If you click on "ClippedCanvas" in the Objects and Timeline you will see it outlined in Blend and have a better understanding of where it is drawn.

So, the Final XAML for my Playlist region looks like this:

<!-- The outer canvas here is clipped: only the area defined by the rectangle geometry is visible  -->
<!-- This is necessary as when we animate the 'Library' canvas inside it we do not want to see the thumbnails slide under the navigation arrows and off the screen-->
<Canvas x:Name="ClippedCanvas" Canvas.Top="491" Canvas.Left="43" Width="550" Height="90">
<Canvas.Clip>
<RectangleGeometry Rect="0, 0, 550, 114"/>
</Canvas.Clip>
<!-- Animations to move the playlist left and right. They are numbered so that we can call them logically from code -->
<Canvas.Resources>
<Storyboard x:Name="MoveLeft01">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Library" From="13" To="-613" Duration="0:0:2" />
</Storyboard>
<Storyboard x:Name="MoveRight02">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Library" From="-613" To="13" Duration="0:0:2" />
</Storyboard>
</Canvas.Resources>
<!-- The Library Canvas groups the playlist buttons into a single element that can be easily animated left - right.  -->		
<Canvas Width="1157.275" Height="82.96" Canvas.Left="0" Canvas.Top="0" x:Name="Library">
<Canvas Width="550.275" Height="82.96" x:Name="playlist1">
<Image x:Name="play0" Opacity="0.74" Width="133.275" Height="82.96" Source="1.png" Stretch="Fill" Cursor="Hand" />
<Image x:Name="play1" Opacity="0.74" Width="133.275" Height="82.96" Source="2.png" Stretch="Fill" Cursor="Hand" Canvas.Left="139"/>
<Image x:Name="play2" Opacity="0.74" Width="133.275" Height="82.96" Source="3.png" Stretch="Fill" Cursor="Hand" Canvas.Left="278"/>
<Image x:Name="play3" Opacity="0.74" Width="133.275" Height="82.96" Source="4.png" Stretch="Fill" Cursor="Hand" Canvas.Left="417"/>
</Canvas>
<Canvas Width="550.275" Height="82.96" x:Name="playlist2" Canvas.Left="607">
<Image x:Name="play4" Opacity="0.74" Width="133.275" Height="82.96" Source="5.png" Stretch="Fill" Cursor="Hand" />
<Image x:Name="play5" Opacity="0.74" Width="133.275" Height="82.96" Source="6.png" Stretch="Fill" Cursor="Hand" Canvas.Left="139"/>
<Image x:Name="play6" Opacity="0.74" Width="133.275" Height="82.96" Source="7.png" Stretch="Fill" Cursor="Hand" Canvas.Left="278"/>
<Image x:Name="play7" Opacity="0.74" Width="133.275" Height="82.96" Source="8.png" Stretch="Fill" Cursor="Hand" Canvas.Left="417"/>
</Canvas>
</Canvas>
</Canvas>

The JavaScript

Code that I added or changed is in bold, the rest is straight from the Encoder's original output.

var curPos = 1; //track the current position of the playlists
var maxPos = 2; //How many pages of clips do we have?
var cVideos = 8; //How many video Clips do we have?
function get_mediainfo(mediainfoIndex) {
switch (mediainfoIndex) {        
case 0:
return  { "mediaUrl": "Movie1.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 1:
return  { "mediaUrl": "Movie2.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 2:
return  { "mediaUrl": "Movie3.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 3:
return  { "mediaUrl": "Movie4.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 4:
return  { "mediaUrl": "Movie5.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 5:
return  { "mediaUrl": "Movie6.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 6:
return  { "mediaUrl": "Movie7.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
case 7:
return  { "mediaUrl": "Movie8.wmv",
"placeholderImage": "",
"chapters": [               
] };                                                                
default:
throw Error.invalidOperation("No such mediainfo");
}
}
function StartWithParent(parentId, appId) {
new StartPlayer_0(parentId);
}
function StartPlayer_0(parentId) {
this._hostname = EePlayer.Player._getUniqueName("xamlHost");
Silverlight.createObjectEx( {   source: 'player.xaml', 
parentElement: $get(parentId ||"divPlayer_0"), 
id:this._hostname, 
properties:{ width:'100%', height:'100%', version:'1.0', background:document.body.style.backgroundColor, isWindowless:'false' }, 
events:{ onLoad:Function.createDelegate(this, this._handleLoad) } } );
this._currentMediainfo = 0;      
}
StartPlayer_0.prototype= {
_handleLoad: function(plugIn) {
this._player = $create(   ExtendedPlayer.Player, 
{ // properties
autoPlay    : true, 
volume      : 1.0,
muted       : false
}, 
{ // event handlers
mediaEnded: Function.createDelegate(this, this._onMediaEnded),
mediaFailed: Function.createDelegate(this, this._onMediaFailed)
},
null, $get(this._hostname)  ); 
//wire up the rollover and click events for each of our play buttons		
for (var i = 0; i < cVideos; i++)
{
var element = plugIn.Content.findName('play' + i);
element.addEventListener("MouseEnter", Function.createDelegate(this,this._rollOver));
element.addEventListener("MouseLeave", Function.createDelegate(this,this._rollOut));
element.addEventListener("MouseLeftButtonUp", Function.createDelegate(this,this._playX));
}
plugIn.Content.findName('LeftArrow').addEventListener("MouseEnter", Function.createDelegate(this,this._rollOver));
plugIn.Content.findName('LeftArrow').addEventListener("MouseLeave", Function.createDelegate(this,this._rollOut));
plugIn.Content.findName('LeftArrow').addEventListener("MouseLeftButtonUp", Function.createDelegate(this,this._slideLeft));
plugIn.Content.findName('RightArrow').addEventListener("MouseEnter", Function.createDelegate(this,this._rollOver));
plugIn.Content.findName('RightArrow').addEventListener("MouseLeave", Function.createDelegate(this,this._rollOut));
plugIn.Content.findName('RightArrow').addEventListener("MouseLeftButtonUp", Function.createDelegate(this,this._slideRight));
this._playNextVideo();     
},
_rollOver: function(sender, eventArgs) {
sender.opacity=1;
},
_rollOut: function(sender, eventArgs) {
sender.opacity=0.74;
},
_playX: function(sender, eventArgs) {
var X = Number(sender.Name.substring(4));
this._currentMediainfo = X;
this._player.set_mediainfo( get_mediainfo( X ));
sender.opacity=1;
},
_slideLeft: function(sender, eventArgs) {
switch(curPos)
{
case 1:
break;
default:
sender.findName("MoveRight0" + curPos).Begin();
curPos--;   
}
},  
_slideRight: function(sender, eventArgs) {
switch(curPos)
{               
case maxPos:
break;				
default:
sender.findName("MoveLeft0" + curPos).Begin();
curPos++;    
}
},
_onMediaEnded: function(sender, eventArgs) {
//window.setTimeout( Function.createDelegate(this, this._playNextVideo), 1000);
},
_onMediaFailed: function(sender, eventArgs) {
alert(String.format( Ee.UI.Xaml.Media.Res.mediaFailed, this._player.get_mediaUrl() ) );
},
_playNextVideo: function() {
if (this._currentMediainfo<cVideos)
this._player.set_mediainfo( get_mediainfo( this._currentMediainfo++ ) );    
}        
}

First I added a reference to the plug-in the HandleLoad function, as described here. Then, because I had named all of my play buttons sequentially, it was easy to loop though them all adding some event handlers for rollover effects and the click event. Next I added similar event handlers to the navigation arrows. The rollover effect, as hinted at earlier was simply to set the opacity to 100% on mouseover and back to 74% on mouse out.

The click event for the play buttons simply play the selected movie, based on the number parsed from the name of the sender ("play0", "play1", etc.). The navigation arrows call the _slideLeft and _slideRight functions which simply play the animations to move the buttons left and right. if you have more than 2 pages of play buttons, then it gets slightly more complicated, obviously you have to create more animations, and they have to be carefully numbered so that you play the appropriate animation based on which 'page' of buttons you are currently on. Go to TheJamesBondMovies.com and take a look at the StartPlayer.js on that site for a better understanding of how to make this technique work with multiple pages.

Well, that was my solution, I'm sure there are other ways to do this, but I don't think this way is overly complicated and I hope someone finds it helpful.

Download the Project files: PlaylistSample.zip (364.40 kb)

Download the Silverlight 2 version: ScrollingPlaylist2.zip (1.22 mb)


Posted by Williarob on Wednesday, November 21, 2007 12:13 PM
Permalink | Comments (31) | Post RSSRSS comment feed

Comments

DotNetKicks.com | Reply

Friday, November 30, 2007 12:15 PM

trackback

Trackback from DotNetKicks.com

Sample Scrolling Silverlight 1.0 Video Playlist

Test | Reply

Monday, December 03, 2007 11:50 AM

trackback

Trackback from Test

Sample Scrolling Silverlight 1.0 Video Playlist

181 United States | Reply

Tuesday, December 04, 2007 7:13 PM

181

i like how you use flash player to show how to use silverlight.

Ishan India | Reply

Friday, December 14, 2007 11:47 PM

Ishan

wow! Great,Thanks very much,i would like to learn more from you.

kevin lin People's Republic of China | Reply

Thursday, December 27, 2007 6:14 PM

kevin lin

thank you very much!good skill

RANA India | Reply

Sunday, December 30, 2007 7:13 AM

RANA

This is having good info about silverlight.

Jimmy Taiwan | Reply

Tuesday, January 08, 2008 2:35 PM

Jimmy

Thanks your sample!

When I try to fetch the playlist in the bottom, the JavaScript makes me  in a mess Frown

How can I show only the playlist?

And when the button is clicked, navigate to the video page?

Shankar krishna United States | Reply

Monday, January 28, 2008 8:07 AM

Shankar krishna

Thanks for the great article !! It'll definitely same me time in the future. I have a question, how can I get rid of the scroll bar when I maximize the screen (by double-clicking on it). The scroll bar can be at times frustrating.

Rui Marinho Portugal | Reply

Thursday, January 31, 2008 5:37 AM

Rui Marinho

Hi there, i try to email you but it fails to the adress that you ahve here, i have a problem with this sample of yours, and i really like it, it works fine in my website on my computer, but when i put it on the webserver, online, it only apeears white, the silverlight object is there but all in blank. think maybe be a problem with the urls?
Would be great if you can give me a hand solving this.
Please write me a email back.

Ahmad Masykur Indonesia | Reply

Friday, February 08, 2008 12:28 AM

Ahmad Masykur

Great sample. I wanna create it for my silverlight application.
Thanks a lot.

fred Albania | Reply

Friday, March 07, 2008 9:46 AM

fred

ne dikush thote harro filmi thote meso.

ALBERTO Germany | Reply

Saturday, March 15, 2008 2:05 PM

ALBERTO

One important point I miss is the capability to concatenate all videos, linking the end of one video with the lstart of the following one. That would be
a true playlist. The sample is oke, but it is just a way of displaying a list of videos, not a playlist. Thank you for the article.

Shaun O'Reilly Australia | Reply

Thursday, April 03, 2008 3:22 AM

Shaun O'Reilly

Flash player works at least. Even on my Linux distro!

Shaun O'Reilly Australia | Reply

Thursday, April 03, 2008 3:24 AM

Shaun O'Reilly

You have a done a great job, thanks for contributing, so that others can learn.

Paras Kaneriya India | Reply

Tuesday, April 22, 2008 5:51 AM

Paras Kaneriya

Thanks!
I was looking for such stuff only!
Smile

Williarob United States | Reply

Friday, May 02, 2008 5:45 PM

Williarob

Alberto wrote:

One important point I miss is the capability to concatenate all videos, linking the end of one video with the lstart of the following one. That would be
a true playlist. The sample is oke, but it is just a way of displaying a list of videos, not a playlist. Thank you for the article.
  

I just wanted to point out that in the startplayer.js, in the _onMediaEnded function, near the end, you might notice there is a line that has been commented out that reads:

//window.setTimeout( Function.createDelegate(this, this._playNextVideo), 1000);

Now if you uncomment this line, it will play all the movies in the list, like a real playlist.

I commented it out, simply because I didn’t want to waste bandwidth looping through all the movies in the collection. I’d rather people select which movies to watch and watch them, rather than just have them play one after the other, even after they have got up and walked away from their desks. Then I forgot to uncomment it when I pasted the code into the article.

Williarob United States | Reply

Friday, May 02, 2008 5:51 PM

Williarob

Shankar wrote "Thanks for the great article !! It'll definitely same me time in the future. I have a question, how can I get rid of the scroll bar when I maximize the screen (by double-clicking on it). The scroll bar can be at times frustrating."  

Good Question. I never tried that before, so I didn't know about that issue. However I will look into it when I have time. I suspect it is a simple matter of hooking into the event that fires when they maximize it, and setting the visible property of the scrollbar panel.

Williarob United States | Reply

Friday, May 02, 2008 5:55 PM

Williarob

Rui wrote: "i have a problem with this sample of yours, and i really like it, it works fine in my website on my computer, but when i put it on the webserver, online, it only apeears white, the silverlight object is there but all in blank. think maybe be a problem with the urls?"  

In my experience, this is an indication that the path to your javascript files and/or your xaml file(s) is not quite right. I put all my scripts in a single folder, right off the root.

Williarob United States | Reply

Sunday, May 04, 2008 6:05 PM

Williarob

OK. I have fixed the problem when maximizing - the scroll bars are now hidden. The event I was thinking of (onFullScreenChange) could have been used to set the visibility of the scroll bars, but I thought of a much simpler solution: I simply moved the code for the FullScreenArea Canvas to the bottom of the xaml file, effectively moving it on top of my scroll bars, so now when it is maximized it all works correctly. The sample above is hosted on Microsoft's silverlight.live.com server and that site appears to employ some sort of caching, because while their "Application Test Page" reflects the new code, the sample above does not (at least at the time of this post.) However, I have also modified the xaml in the sample files and on thejamesbondmovies.com so you can test it out there if you prefer.

Sapumal Moragoda | Reply

Wednesday, May 14, 2008 9:59 PM

Sapumal Moragoda

You have a done a great job, thanks for contributing, so that others can learn.
(There hasn’t mentioned in dropdown list. Please add)

chencheng People's Republic of China | Reply

Thursday, July 03, 2008 8:54 PM

chencheng

Thank you very much!I was looking!

venkat United States | Reply

Sunday, December 07, 2008 6:00 AM

venkat

Nice.

I wanted to create something similar, but not with the media.
I have 100 buttons to display, 25 per page with next and prev 25 buttons, in a canvas with little animation, similar way as in silverlight.net/showcase.
How can I do that. I tried to edit your code, could not complete it.
Can u give me some tips please

thanks

gianpiero Italy | Reply

Friday, December 12, 2008 1:49 AM

gianpiero

hi,

is avaliable a  silverlight 2 version? Too javascript code!

Williarob United States | Reply

Friday, December 12, 2008 6:33 PM

Williarob

mmsgg@libero.it wrote:

>>hi,
>>is avaliable a silverlight 2 version? Too javascript code!

Not yet, but when I find the time, I do plan to update it to Silverlight 2.

Williarob United States | Reply

Friday, December 12, 2008 6:35 PM

Williarob

vvemulapalli,

If you send me your project as it is right now, I’d be glad to take a look at it.

venkat United States | Reply

Tuesday, December 23, 2008 6:02 PM

venkat

Is there any way to disable the fullscreen mode or disable double click event ?
Please let me know...

thanks

Williarob United States | Reply

Saturday, December 27, 2008 6:31 PM

Williarob

Question: Is there any way to disable the fullscreen mode or disable double click event ?

Answer: Yes. Edit BasePlayer.js:

Change:

_onFullScreen:function(){var a=this.get_element().content;a.fullScreen=!a.fullScreen}

To:

_onFullScreen:function(){var a=this.get_element().content;a.fullScreen=false}

bisnis dari rumah United States | Reply

Saturday, January 03, 2009 2:59 PM

bisnis dari rumah

wow, a great info. have downloaded your sample. thanks for sharing.

Tadalafil Italy | Reply

Monday, February 23, 2009 8:20 AM

Tadalafil

I have an Error in this code part:

var element = plugIn.Content.findName('play' + i);

Can you give me any advices? What I'm doing wrong?

Thanks!

Sildenafil Italy | Reply

Tuesday, March 17, 2009 2:26 AM

Sildenafil

Thanks for this. Silverlight is ROCKs, and Scrolling in its video is fantastic!

Williarob United States | Reply

Tuesday, March 31, 2009 7:45 AM

Williarob

Tadalafil wrote:

I have an Error in this code part:

var element = plugIn.Content.findName('play' + i);

Can you give me any advices? What I'm doing wrong?

What is the error? Chances are it is a null reference. I would recommend you set a breakpoint on this line using either the debugger that is built into IE 8 or firebug in firefox. Mouse over each object until you find one of them that is either null or undefined. It could be that your plugIn object has a different name. If you created your Silverlight project in Expression Blend 2 (March Preview) instead of Visual Studio, try control.Content.findName('play' + i);

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading