
Introduction
A few days ago, I posted an article about creating a Silverlight Carousel control, which you can read here. In this article, I'll create a new carousel control which can be used to play video or audio. I named it VideoCarousel. The VideoCarousel is very similar to the Carousel except it can play video or audio. If you have tried the Carousel control, maybe you already know the Carousel has a Panel to display the user selected picture (with title). In VideoCarousel, the Panel is replaced by a video player control. If you want to know how the Carousel works, you may need to read this article.
You may use the VideoCarousel control as a video player in your web page. It supports dynamic add/remove of media (video or audio) info to its collection. The media info can include a picture (you can capture a frame from the film), a URI where the media is located, a title, the name of the director, the name of the main actors, and the name of the producer.
When the user moves the mouse over a CarouselItem, the media info will appear. When the user selects a CarouselItem, the selected video picture will appear in the selected item panel and the media will be auto buffered. If the media is ready, the user can play it, or if you set the AutoPlay property to true, the media will auto play. The carousel menu auto-disappears when the media is playing.
Display media info when mouse over:

The carousel hides when a video is playing:

Background
I have created a Silverlight V2 control named Carousel which can display a collection of pictures in an interactive carousel. I liked the control, so I made some upgrades for it, so now it can be used as a video player.
Key Features
- Silverlight V2 control, all written in C#.
- Very easy to use.
- Auto display a loading animation when the media is downloading.
- Can play video in full screen mode.
- Has a very attractive carousel as the video menu.
Using the Control
Using the VideoCarousel control is very simple. In your Silverlight application, add a reference to the assembly (the name of the assembly is Cokkiy.Display.VideoCarousel). Then, in your page's XAML file, do the following:
In the beginning of the page, add an xmlns reference:
<UserControl x:Class="VideoCarouselTest.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:display="clr-namespace:Cokkiy.Display;assembly=Cokkiy.Display.VideoCarousel"
>
Then, place the VideoCarousel control inside your layout control, such as a Grid or StackPanel:
<Grid x:Name="LayoutRoot" Background="White">
<display:VideoCarousel x:Name="carousel"
TurnDirection="Counterclockwise" Padding="5,5,5,5" >
Also, you can set its background like this:
<display:VideoCarousel.Background>
<LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</display:VideoCarousel.Background>
If you want to set the media info static in your XAML file, you can do the following:
<display:VideoCarousel.ItemSources>
<display:ItemSource Title="Sample Video"
Director="Unknown" Actors="Unknown"
Producer="Microsoft"
ImageSource="Images/01.jpg"
MediaSource="Videos/SampleVideo.wmv"/>
<display:ItemSource Title="Sample" ImageSource="Images/02.jpg"
MediaSource="Videos/Sample.wmv"/>
<display:ItemSource Title="SL VC1 Video" ImageSource="Images/03.jpg"
MediaSource="Videos/sl.wmv"/>
<display:ItemSource Title="silverlight" ImageSource="Images/04.jpg"
MediaSource="Videos/silverlight.wmv"/>
<display:ItemSource Title="SampleVideo" ImageSource="Images/05.jpg"
MediaSource="Videos/SampleVideo.wmv"/>
<display:ItemSource Title="Sample" ImageSource="Images/06.jpg"
MediaSource="Videos/Sample.wmv"/>
<display:ItemSource Title="sl" ImageSource="Images/07.jpg"
MediaSource="Videos/sl.wmv"/>
<display:ItemSource Title="silverlight" ImageSource="Images/08.jpg"
MediaSource="Videos/silverlight.wmv"/>
<display:ItemSource Title="SampleVideo" ImageSource="Images/09.jpg"
MediaSource="Videos/SampleVideo.wmv"/>
<display:ItemSource Title="silverlight" ImageSource="Images/10.jpg"
MediaSource="silverlight.wmv"/>
<display:ItemSource Title="Error created" ImageSource="Images/11.jpg"/>
</display:VideoCarousel.ItemSources>
In the end, close the tag:
</display:VideoCarousel>
</Grid>
But in most situations, you want to dynamically add media info into the collection based on user selection or the video just watched. You can subscribe to the SelectedItemChanged or CurrentMediaStateChnaged event. The SelectedItemChanged event occurs when the user selects an item, and the CurrentMediaStateChnaged event occurs when the media is playing, paused, or stopped. The following code adds a film to the control:
carousel.ItemSources.Add(
new Uri("Images/25.jpg", UriKind.Relative), new Uri("Videos/sl.wmv",UriKind.Relative), "Dynamic added SL VC1 Video", "Jo Coco", "Tom Crausel", "the Disney Product");
You also want to remove the media the user just watched when the media ends.
void carousel_CurrentMediaStateChnaged(object sender, MediaStateChangedEventArgs e)
{
if (e.MediaState == MediaState.Ended)
{
carousel.ItemSources.Remove(e.Title);
}
}
Some times, you may want the Carousel part not to auto turn. You can simply set AutoTurn to false to get this effect. When the Carousel not auto turning, a button will appear on each side of the Carousel, and the user can use the buttons to turn left or right.

In the XAML file, you can do this:
<display:VideoCarousel x:Name="carousel" AutoTurn="False"
TurnDirection="Counterclockwise" Padding="5,5,5,5" >
Or you can set it in code.
How it Works
Here, I won't repeat how the Carousel works (you can read how it works here). The main point here is the video player. The video player is a UserControl. Here is the XAML (note that here I have omitted the control template code):
<UserControl x:Class="Cokkiy.Display.VideoPlayer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:mwc="clr-namespace:Microsoft.Windows.Controls;assembly=Cokkiy.Common.Controls"
xmlns:cc="clr-namespace:Cokkiy.Common.Controls;assembly=Cokkiy.Common.Controls"
xmlns:resources="clr-namespace:Cokkiy.Display.Resources"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
>
<Grid x:Name="LayoutRoot"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseLeave="LayoutRoot_MouseLeave"
MouseMove="LayoutRoot_MouseMove"
Opacity="1" Background="Transparent">
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="PositionStates">
<vsm:VisualState x:Name="SetPositionState">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:00.0010000"
Storyboard.TargetName="positionSlider"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:00.0010000"
Storyboard.TargetName="_base"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:00.0010000"
Storyboard.TargetName="position"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:00.0010000"
Storyboard.TargetName="bufferPosition"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="ShowPositionState"/>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="ControlStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="00:00:02"
To="ShowControlPanelState"/>
<vsm:VisualTransition From="ShowControlPanelState"
GeneratedDuration="00:00:02"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="ShowControlPanelState">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="controlBorder"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="HideControlPanelState"/>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Image x:Name="frameImage"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" />
<Grid x:Name="mediaBackground"
Background="Black" Visibility="Collapsed">
<StackPanel MouseLeftButtonUp="mediaPlace_MouseLeftButtonUp"
x:Name="mediaPlace"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<MediaElement AutoPlay="False" x:Name="mediaPlayer"/>
</StackPanel>
</Grid>
<Border CornerRadius="30,30,0,30"
VerticalAlignment="Bottom" Background="Black"
x:Name="controlBorder" Opacity="0">
<Grid x:Name="controlGrid" Opacity="0.7"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<cc:PlayPauseButton x:Name="playPauseButton"
IsEnabled="False" Width="60" Height="60"
Click="playPauseButton_Click"/>
<StackPanel Grid.Column="1">
<TextBlock x:Name="titleTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="16" Foreground="White"
Text="Video Tile" TextWrapping="Wrap"/>
<mwc:DockPanel LastChildFill="True">
<Button Margin="0,0,5,0"
mwc:DockPanel.Dock="Left" x:Name="stopButton"
Click="stopButton_Click"
Style="{StaticResource StopButtonStyle}"/>
<StackPanel Orientation="Horizontal"
mwc:DockPanel.Dock="Right">
<Slider Width="100" Maximum="1"
Value="0.5" LargeChange="0.05" SmallChange="0.01"
Style="{StaticResource EllipseSliderStyle}"
x:Name="volumeSlider"
ValueChanged="volumeSlider_ValueChanged"/>
<ToggleButton Margin="0,0,5,0" VerticalAlignment="Center"
Style="{StaticResource muteToggleButtonStyle}"
x:Name="muteButton"
Checked="muteButton_Checked"
Unchecked="muteButton_Unchecked"/>
</StackPanel>
-->
<StackPanel x:Name="positionPanel"
MouseEnter="positionPanel_MouseEnter"
MouseLeave="positionPanel_MouseLeave"
HorizontalAlignment="Stretch">
<Grid>
<Rectangle Fill="#FF7C7A7A"
Height="5" VerticalAlignment="Center"
x:Name="_base"
SizeChanged="_base_SizeChanged"
HorizontalAlignment="Stretch"/>
<Rectangle Fill="#FFD7F90E" x:Name="bufferPosition"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Height="5" Width="0"/>
<Rectangle Fill="#FFFAFAFB"
Height="5" VerticalAlignment="Center"
HorizontalAlignment="Left"
Width="0" x:Name="position"/>
<Slider Maximum="1"
LargeChange="0.05" SmallChange="0.01"
mwc:DockPanel.Dock="Right"
Style="{StaticResource EllipseSliderStyle}"
IsEnabled="False" x:Name="positionSlider"
ValueChanged="positionSlider_ValueChanged"
Opacity="0"/>
</Grid>
<mwc:DockPanel LastChildFill="False">
<TextBlock Foreground="White"
mwc:DockPanel.Dock="Right"
x:Name="mediaDurantionTextBlock"
Text="unknown"/>
<TextBlock Foreground="White"
mwc:DockPanel.Dock="Right"
Text="/"/>
<TextBlock Foreground="White"
mwc:DockPanel.Dock="Right"
x:Name="mediaPositionTextBlock"
Text="00:00:00"/>
</mwc:DockPanel>
</StackPanel>
</mwc:DockPanel>
</StackPanel>
</Grid>
</Border>
<Grid HorizontalAlignment="Center" x:Name="loadingInfo"
VerticalAlignment="Center"
Visibility="Collapsed">
<-- Omit the loading animation control -->
</Grid>
</Grid>
The main part of the video player is a control panel which controls the MediaElement to play, pause, or stop the media and indicate the position of the media. The visual state controls appear or disappear on the control panel. There are no more interesting things to say about this XAML file.
But in code, something needs to be said. The first is how we track the current position in the media. If you have been studying the MediaElement control, you will notice that there is no notification when the playing position changes. So, I created a Timer object to track the position at specified intervals.
Create a global Timer object:
private Timer setPositionTimer;
When the media plays, start the timer.
private void PlayMedia()
{
mediaPlayer.Play();
if (mediaPlayer.CanSeek)
{
if (setPositionTimer == null)
{
setPositionTimer = new Timer(this.UpdatePosition, null, 0, 500);
}
}
}
The TimerCallback function is called by the Timer object. It uses an anonymous delegate to update the position of the track bar.
private void UpdatePosition(object state)
{
positionSlider.Dispatcher.BeginInvoke(
delegate()
{
double value = mediaPlayer.Position.TotalSeconds
/ mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds;
position.Width = value * _base.ActualWidth;
mediaPositionTextBlock.Text =
mediaPlayer.Position.TotalMinutes.ToString("F2"); ;
});
}
The second interesting thing is when the user moves the mouse over the player, the control panel will appear and then disappear if the user holds the mouse still.
When the user moves the mouse, we use the VisualState to make the control panel appear. And, we start the down-count to hide the control panel.
private void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
{
VisualStateManager.GoToState(this, "ShowControlPanelState", true);
this.Cursor = null;
HideControlPanel();
}
The HideControlPanel uses a DispatcherTimer object. Here, we use the DispatcherTimer object because we want to directly update the Visual.
private DispatcherTimer hideControlPanelTimer;
private void HideControlPanel()
{
if (hideControlPanelTimer == null)
{
hideControlPanelTimer = new DispatcherTimer();
hideControlPanelTimer.Interval = TimeSpan.FromSeconds(10);
}
hideControlPanelTimer.Stop();
hideControlPanelTimer.Tick += delegate(object s, EventArgs args)
{
VisualStateManager.GoToState(this, "HideControlPanelState", true);
this.Cursor = Cursors.None;
hideControlPanelTimer.Stop();
};
hideControlPanelTimer.Start();
}
I have said so much about the VideoCarousel control. If you have any problems or advice, please contact me.
History
- 26 November, 2008: Initial post.
- 27 November, 2008: Updated the source code. Fixed a bug when the control size changes, the player panel can't resize itself. Added two events:
CurrentMediaStateChnaged and SelectedItemChanged.
- 27 November, 2008: Updated article.
- 27 November, 2008: Updated source code. Fixed a bug when setting
AutoTurn to false, the turning button did not appear.
The God created the world.
The programer made the world easy.
I am a programer like c# and c++, so I am writting application with it.
Creating beautiful application make life easy.
If you have a project and looking for a man, I'll be say "hi, I am just the man you are looking for."