(2/2) Using Live Visual Tree and Live Property Explorer to customize the Media Player (XAML, C#)
In this series of tutorials on how to customize your MediaPlayer
, we have seen how to customize a button in part I. Now we will see how we can use Live Visual Tree and Live Property to make our lives easier when we are trying to design our MediaPlayer
element.
Before we had no way to edit elements on the fly, we had to imagine what would happen when we changed a property like Margin
, Height
, Color
, BrushThickness
etc. Web developers have tons of tools like Firebug for Firefox, Chrome Tools for Chrome and Developer Tools for Edge and IE that allow them to do this, so why not us? The developing gods (Microsoft in our case) have answered our call and have given us the Live Visual Tree and Live Property Explorer tools which address exactly these issues. I will talk about these two elements and how they can help us develop better and faster.
In this tutorial we will implement and design a custom control that will be shown when the video has finished playing. The user will be able to rate the video using this control and we will also move the PlayPauseButton
to the bottom left using Live Visual Tree and Live Property Explorer.
This tutorial will cover:
- What the Live Visual Tree and Live Property Explorer components are, how to use them, and why they are great tools
- How to use these tools to speed up your development
- How to add a control to the
MediaPlayer
that users can interact with - How to move an element in the
MediaPlayer
control
The final version of our player will have:
- A custom element in which users can rate the video. This element will have three different buttons (like, neutral, dislike), which will be shown at the end of the video.
- These three buttons will have different actions (rate the video).
- A
PlayPauseButton
that have been moved and re-sized.
or in other words:
Let’s get started, I will assume that you have:
- Windows OS ;)
- Visual Studio 2015 (here) installed
- Media Player Framework vsix installed.
- Setting (downloading and referencing) a theme to the MediaPlayer, in this example I will be using Entertainment.xaml template theme.
Live Visual Tree and Live Property Explorer
This new tool will speed up your development as you no longer not need to reload the application every time you change something, thanks to Live Property Explorer. You can find Live Visual Tree under: DEBUG -> WINDOWS -> Live Visual Tree and Live property Explorer under the same path.
Like you can see here:
Live Visual Tree on the right and the Live Property Explorer on the left:
You can see that Live Visual Tree provides information about the number of XAML elements inside each container (here we can see that we have a Grid
, a StackPanel
and more). Live Visual Tree will show only visible elements, and when an element changes from one state to another you can see that Live Visual Tree is changing at runtime which is very helpful (and AWESOME).
Live Property Explorer shows default values for properties, values which were inherited from other controls and the local values of control properties. You can modify local values, here for example we could choose to change the StackPanel
HorizontalAlignment=center
to Left
and we could change VerticalAlignment="Top"
to Bottom
and the effects would be taken into account without having to reload the app.
As you can see here:
Live Visual Tree and Live Property Explorer are two great tools that will help you speed up your development time since you will no longer need to recompile your application every time you wish to try a new design or version of a design.
(In this part I will not go over how to copy the theme template into the player, for more information on this please read part I).
First we will create a Windows 10 application with the MvvmLight Framework
:
If you don’t have MvvmLight, I can only highly recommend it to you download here.
Now that we have created a Windows 10 application, we will need to create the custom control, for this example I will create a folder called Contrls
and add a UserControl
called RateMyVideoControl
. (We deliberately do not name the folder Controls
, to avoid namespace clashes).
Now we create a Grid
element with 3 columns in which we will have 3 images: up vote, neutral vote and down vote. These 3 buttons will be linked to a Command
property which will show a different message to the user depending on which button is clicked.
Here is the code for the UserControl
:
<StackPanel> <TextBlock FontSize="32" Margin="10" Text="Did you like this video?"/> <Grid Height="70"> <Grid.ColumnDefinitions> <ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" /> <ColumnDefinition Width="0.3*" /> </Grid.ColumnDefinitions> <Button Grid.Column="0" Command="{Binding UpVoteCommand}" VerticalAlignment="Center" HorizontalAlignment="Center" > <Image Source="/Assets/up.png"></Image> </Button> <Button Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding NeutralVoteCommand}" > <Image Source="/Assets/Neutral.png"></Image> </Button> <Button Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding DownVoteCommand}" > <Image Source="/Assets/down.png"></Image> </Button> </Grid> </StackPanel>
Next we add these RelayCommands
to our MainViewModel
file so that the binding can happen:
public RelayCommand UpVoteCommand { get { return new RelayCommand(async () => { var dialog = ServiceLocator.Current.GetInstance<IDialogService>(); await dialog.ShowMessage("Custom Player", "Up Voted."); //TODO: add more logic here }); } } public RelayCommand NeutralVoteCommand { get { return new RelayCommand(async () => { var dialog = ServiceLocator.Current.GetInstance<IDialogService>(); await dialog.ShowMessage("Custom Player", "Neutral Voted."); //TODO: add more logic here }); } } public RelayCommand DownVoteCommand { get { return new RelayCommand(async () => { var dialog = ServiceLocator.Current.GetInstance<IDialogService>(); await dialog.ShowMessage("Custom Player", "Down Voted."); //TODO: add more logic here }); } }
In your IDE you should have a control that looks like this:
Now using Live Visual Tree we will look into the MediaPlayer
to see where we can place this component so that the user can see it once the video has ended.
We can see that in the InteractivityContainer
element we have a Border
element that holds all of the controls for the player. This looks like a good place to set our own control. We will then insert our control and will also add a binding to the Visibility
property so that we can show and hide our control using a boolean.
So looking with Live Visual Tree we now will have:
Our XAML Code in the MediaPlayer
:
<contrls:RateMyVideoControl Visibility="{Binding IsRateMyVideoVisible , Converter={StaticResource BTVConverter}, FallbackValue=Collapsed}" />
C# code for the IsRateMyVideoVisible
property:
private bool _isRateMyVideoVisible = false; public bool IsRateMyVideoVisible { get { return _isRateMyVideoVisible; } set { Set(ref _isRateMyVideoVisible, value); } }
And lastly in our MainPage
we use the MediaEnded
event on the MediaPlayer
. This will tell us when to show the control to the user so that we only show it when the user has finished watching the video.
public MainViewModel Vm => (MainViewModel)DataContext; public MainPage() { InitializeComponent(); Loaded += (s, e) => { //When player is ending show the Control player.MediaEnded += Player_MediaEnded; }; } private void Player_MediaEnded(object sender, Microsoft.PlayerFramework.MediaPlayerActionEventArgs e) { Vm.IsRateMyVideoVisible = true; }
When the video has finished playing we now have this:
which is not great… On we go to fix this issue!
Moving elements using Live Visual Tree and Live Property Explorer
As we saw previously we have a custom control that is shown when the video has ended, however, our play/pause button is hiding it!
Again using Live Visual Tree we have:
From what we see here we are going to need to search in our player theme for the element named PlayPauseButton
. We'll take a copy of it and then comment it out (just in case we want to go back again), and then paste the copy into the element called TimelineContainer
.
So in our PlayerTheme.xaml
resource file, around the Grid
named TimelineContainer
we will now have:
<Grid x:Name="TimelineContainer" Grid.Row="1" Background="{StaticResource TransportBackgroundBrush}"> <AppBarButton x:Name="PlayPauseButton" Margin="25,0,25,0" Width="140" Height="140" Style="{TemplateBinding PrimaryButtonStyle}" Visibility="{Binding IsPlayPauseButtonVisible, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource VisibleIfConverter}}"> <local:MediaControls.Behavior> <local:PlayPauseButtonBehavior ViewModel="{Binding ViewModel, RelativeSource={RelativeSource TemplatedParent}}"/> </local:MediaControls.Behavior> </AppBarButton> <Grid Margin="30,4,30,7"> The rest of the code.....
Your player should look like this ugly thing now, it’s normal:
Again using Live Visual Tree and looking for the PlayPauseButton
, we can see that it has a Height
and Width
of 140. We are going to change its Height
and Width
properties using the Live Property Explorer. You can change the properties Height
and Width
to 40px and all the sudden it looks a bit better, and if we add more left margin 90px to the Grid
which had a left margin of only 30px to start with, we are even better =).
Once we have modified these properties, our player should look as follows:
And there you have it, you have customized your MediaPlayer
again and seen how Live Visual Tree and Live Property Explorer can allow you to gain a lot of time when you are trying to position different elements in your app!
All of the code can be found on Github here