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:

  1. How to set a theme on your Media player Framework and play with the different controls
  2. How to Add your own custom controls, title, message box, buttons, etc
  3. 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:

  1. Windows OS ;)
  2. Visual Studio 2015 (here) installed, or 2013
  3. Media Player Framework vsix installed.

Setting a theme on the Media Player Framework

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 a TextBlock 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 a GridView , or a Button 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 default TransportBarButtonStyle 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