(1/2) Customizing your Media Player Framework on Windows Phone (XAML, C#)
As you know here at Dailymotion we strive to deliver the best video player experience no matter what platform you are on (Windows, iOS, Android etc). Thus, I have spent a lot of time customizing the Media Player for our Windows apps and needless to say that it has been a long journey for me because of the lack of any good tutorials and examples. Many times I used trial and error to try and figure out what worked and what did not.
That is why I have created this tutorial, we will cover how to customize the XAML template for the Media Player Framework (version 3.0.0.1). I will only focus on Windows Phone 8.1 but these examples also work for Windows 8.1 and UAP and the version 2.0.0.0 of the Media Player.
This tutorial will cover 3 different examples:
- How to set a theme on your Media player Framework and play with the different controls
- How to Add your own custom controls, title, message box, buttons, etc
- How to customize your own AppBarButton using its default style
The final version of our player will have:
- A title in the Media Player that will be shown only when we click/tap on the player
- A custom button inside the player that users can click on
- A CommandBar button that can hide and show elements in the player
- A customized play pause button so that it doesn’t have its ellipse element around it
All of the code can be found on Github here
Let’s get started, I will assume that you have:
On Codeplex you will find 4 different themes :
- Classic
- Entertainment
- Generic
- Phone
To use any of these default themes you need to download and reference it into your project, here is an example with the Entertainment.xaml template downloaded:
<Page.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Theme/Entertainment.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Page.Resources> <Grid x:Name="LayoutRoot"> <playerFramework:MediaPlayer x:Name="player" Source="http://smf.blob.core.windows.net/samples/videos/wildlife.mp4" /> </Grid>
Using the different elements of the Media Player
If you want to show or hide elements on the MediaPlayer
element you need to look for the properties that contain the word 'Visible' in them. For example: when the user clicks on a button he can hide or show the rewind
, fastForward
and playPause
buttons:
private void HideBtn_OnClick(object sender, RoutedEventArgs e) { player.IsRewindVisible = false; player.IsFastForwardVisible = false; player.IsPlayPauseVisible = false; } private void ShowBtn_OnClick(object sender, RoutedEventArgs e) { player.IsFastForwardVisible = true; player.IsRewindVisible = true; player.IsPlayPauseVisible = true; }
Code can be found: in the MediaPlayerFramework_Theme project
Adding custom controls
First you will need to copy one of the default templates, here you can find the phone theme. In this theme we have a ControlTemplate
which contains a Grid
with a VerticalAlignment="Bottom"
property. Remove the VerticalAlignment="Bottom"
property and add it to your Border
element named 'border' that contains all of the controls for the player. Here is the XAML:
<Style TargetType="local:MediaPlayer"> <Setter Property="InteractiveDeactivationMode" Value="All" /> <Setter Property="AutoHideBehavior" Value="All" /> <Setter Property="ControlPanelTemplate"> <Setter.Value> <ControlTemplate TargetType="local:ControlPanel"> <Grid VerticalAlignment="Bottom"> <-- remove VerticalAlignment="Bottom" <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="MediaStates"> ... <Border x:Name="Border" <- add it here Background="{TemplateBinding Background}" DataContext="{TemplateBinding ViewModel}"> <Border.Resources> <local:BoolToVisibilityConverter x:Key="VisibleIfConverter" /> </Border.Resources>
Now that we have moved this property we can start customizing our player. For example:
- We can add another
border
element that we will position at the top with aTextBlock
in it that will hold the title of our video:
<Border x:Name="TopBorder" Height="30" VerticalAlignment="Top" Background="DarkRed"> <TextBlock Text="{Binding TitleOfTheVideo}" /> </Border>
- We could also add another
Border
element that contains aGridView
, or aButton
for example:
<Border x:Name="CustomElement" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding Path=IsCustomControlVisible, Mode=TwoWay, Converter={StaticResource BTVConverter}}"> <Button Command="{Binding CustomElementCommand}" Content="CLick Me" /> </Border>
We can even play with the visibility of this Border
element using its visibility property and only show this element when the video is finished or when the user does an action.
Here is an example on how to show the Border
with a button when the video is finished, you need to use the MediaEnded
event which is fired when the video has finished playing:
protected override void OnNavigatedTo(NavigationEventArgs e) { player.MediaEnded += Player_MediaEnded; } private void Player_MediaEnded(object sender, Microsoft.PlayerFramework.MediaPlayerActionEventArgs e) { ViewModel.IsCustomControlVisible = true; }
Customizing an AppBarButton
What if you wanted to customize one of the AppBarButtons
in the player, here is what the default PlayPauseButton
looks like:
<AppBarButton x:Name="PlayPauseButton" Width="70" Height="70" Margin="50,0,50,0" Style="{TemplateBinding TransportBarButtonStyle}" Visibility="{Binding IsPlayPauseButtonVisible, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource VisibleIfConverter}}"> <local:MediaControls.Behavior> <local:PlayPauseButtonBehavior ViewModel="{Binding ViewModel, RelativeSource={RelativeSource TemplatedParent}}" /> </local:MediaControls.Behavior> </AppBarButton>
The
AppBarButton
is styled using the defaultTransportBarButtonStyle
style.
In this next example we will be removing the Ellipse
elements that are around the PlayPausebutton
and we will name this style CircleLessTransportBarButtonStyle
.
First, we need to copy the style TransportBarButtonStyle
, and rename it so that we can customize it. You can find the defaut TransportBarButtonStyle
style here on codeplex, copy the TransportBarButtonStyle
style in your ResourceDictionary
file.
Next, we need to comment both Ellipse
elements that are located in the ViewBox
. Also we will need to comment all of the ObjectAnimationUsingKeyFrames
located in the VisualState
that are targeting the Ellipse
elements.
Here is the XAML modified with these changes:
<Style x:Key="CircleLessTransportBarButtonStyle" TargetType="AppBarButton"> <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}" /> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="Padding" Value="0" /> <Setter Property="IsCompact" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="AppBarButton"> <Grid x:Name="RootGrid" Background="Transparent"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ApplicationViewStates"> <VisualState x:Name="FullSize" /> <VisualState x:Name="Compact" /> </VisualStateGroup> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundEllipse" Storyboard.TargetProperty="Fill"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemPointerOverBackgroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames>--> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemPointerOverForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineEllipse" Storyboard.TargetProperty="Stroke"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundEllipse" Storyboard.TargetProperty="Fill"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames>--> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemPressedForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineEllipse" Storyboard.TargetProperty="Stroke"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemDisabledForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames>--> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarItemDisabledForegroundThemeBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <DoubleAnimation Duration="0" Storyboard.TargetName="FocusVisualWhite" Storyboard.TargetProperty="Opacity" To="1" /> <DoubleAnimation Duration="0" Storyboard.TargetName="FocusVisualBlack" Storyboard.TargetProperty="Opacity" To="1" /> </Storyboard> </VisualState> <VisualState x:Name="Unfocused" /> <VisualState x:Name="PointerFocused" /> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Viewbox Margin="{TemplateBinding Padding}"> <Grid Width="40" Height="40"> <!--<Ellipse x:Name="BackgroundEllipse" Fill="{ThemeResource AppBarItemBackgroundThemeBrush}" UseLayoutRounding="False"/> <Ellipse x:Name="OutlineEllipse" Stroke="{ThemeResource AppBarItemForegroundThemeBrush}" StrokeThickness="2" UseLayoutRounding="False"/>--> <ContentPresenter x:Name="Content" HorizontalAlignment="Center" VerticalAlignment="Center" AutomationProperties.AccessibilityView="Raw" Content="{TemplateBinding Icon}" Foreground="{TemplateBinding Foreground}" /> </Grid> </Viewbox> <Rectangle x:Name="FocusVisualWhite" IsHitTestVisible="False" Opacity="0" Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}" StrokeDashArray="1,1" StrokeDashOffset="1.5" StrokeEndLineCap="Square" /> <Rectangle x:Name="FocusVisualBlack" IsHitTestVisible="False" Opacity="0" Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}" StrokeDashArray="1,1" StrokeDashOffset="0.5" StrokeEndLineCap="Square" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
Now we need to change the referenced style on the AppBarButton
, on our example we wanted to change the play pause button so we need to reference our new style called CircleLessTransportBarButtonStyle
in the AppBarButton
named 'PlayPauseButton'.
Here is the final XAML for this AppBarButton
:
<AppBarButton x:Name="PlayPauseButton" Width="70" Height="70" Margin="50,0,50,0" Style="{StaticResource CircleLessTransportBarButtonStyle}" Visibility="{Binding IsPlayPauseButtonVisible, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource VisibleIfConverter}}"> <local:MediaControls.Behavior> <local:PlayPauseButtonBehavior ViewModel="{Binding ViewModel, RelativeSource={RelativeSource TemplatedParent}}" /> </local:MediaControls.Behavior> </AppBarButton>
Code can be found in the MediaPlayerFramework_Custom project
Once you run your code you will see that the play pause button doesn’t have the ellipse around it. This concludes this tutorial, if you have arrived here then you have read everything! Thank you for reading and good luck in your customization of your Media Player.
All of the code can be found on Github here