Control the playback position with MediaElement
So today, we are going to implement the playhead control, to allow the user to move to a specific frame in the track.
Binding our components
If you remember, in the chapter on timing display, we introduced 3 components:
ElapsedTime
, a Label for displaying elapsed playback time,TotalTime
, also a Label but to display the total duration of the audio track,and finally the Slider
TimeTracker
, to control the track’s playback head.
Goodbye dummy data! We are now going to bring these components to life with the help of Data Binding. And for this, we will use an initialization method, InitTimeTracker():
Filename:MusicPlayerView.cs
|
|
Yes, I know it’s a bit dense, but cheer up! It’s all explained below.
To get started, we have modified the behaviour of the TimeTracker
to associate two properties with it:
the current position of the cursor on the Slider,
and the maximum value of the cursor (when itβs all to the right).
Let’s take a closer look at the MediaElement represented by our MusicPlayer
component. It has exactly what we need:
the real time position of the playback head (
MusicPlayer.Position
), to be associated with the cursor position on the Slider (TimeTracker.Value
),and the total duration of the song (
MusicPlayer.Duration
), to be associated with the maximum value of the Slider (TimeTracker.Maximum
).
And now, if you remember, the Slider can only consider values of double type. This is why we need to convert the values obtained from MusicPlayer.Position
and MusicPlayer.Duration
, from the TimeSpan type to the double type, with the property named TotalSeconds
.
For the ElapsedTime
component, we want it to display how long the song has been playing, also based on the property MusicPlayer.Position
. But only in a special way! We want to display the duration in minutes and seconds. To do this, we need to apply the desired text format using the code: {0:mm\\:ss}
.
For the TotalTime
component, we want to display the total listening time of the song, based on the property MusicPlayer.Duration
. In the same way, we apply the identical text format to display a duration in minutes and seconds.
By the way, don’t forget to replace the β=>β sign with β=β when declaring each of these components! Otherwise it won’t work, as the same object will be returned each time you call these variables. You can use this opportunity to remove the dummy values we had defined by default for some properties:
Text
property for the two Labels,Maximum
andValue
properties for the Slider.
Here is the revised declaration for these 3 components:
Filename:MusicPlayerView.cs
|
|
Finally, the initialization method for these components must be called from the page constructor:
Filename:MusicPlayerView.cs
|
|
Go on, relaunch the project! I’m sure you really want to π
Normally, you should see the texts are correctly initialized with real values. Also, the cursor should move on its own as the song progresses.
We can play around with coloring the Labels background to get a better idea of the situation:
As the song progresses, the text is updated every second and a new value replaces the old one. But it looks like some values require more space than others! π
If you remember the scaffolding chapter, we defined there a Grid component named BottomLayout
, which we divided into 7 columns of different sizes:
Filename:MusicPlayerView.cs
|
|
Letβs rework the structure of our Grid by merging the first two columns, and the last twoβ¦
Filename:MusicPlayerView.cs
|
|
… then rearrange the positioning of the BottomLayout
elements:
Filename:MusicPlayerView.cs
|
|
As we removed two columns from our initial Grid, we had to readjust the positioning of our components, by modifying their assigned column and their horizontal alignment. To do this, we used the Start() and End() methods. These respectively align a component horizontally at the beginning or end of the available space.
And here is the outcome:
Move the playhead control
To control the playback head, the user must simply drag the Slider cursor from point A to point B. If we break down his gesture, we could say that:
The user first puts his finger on the cursor at point A of the Slider,
Then he moves the cursor by keeping his finger pressed down,
Finally, he releases it at point B of the Slider by lifting his finger from the screen.
Now you have read this, did it ring a bell?
Detect when user starts moving the cursor,
And detect when he releases it.
Let’s attach these two events to our TimeTracker
using the method InitTimeTracker():
Filename:MusicPlayerView.cs
|
|
Now define these new events in their dedicated region with the following code:
Filename:MusicPlayerView.cs
|
|
The first event TimeTracker_DragStarted() corresponds to the moment when the user starts moving the cursor on the Slider. When this happens, playback is paused to prevent the music from continuing to play. Or else, the slider would continue to move on its own!
And for a better user experience, the song must resume playback as soon as user has finished his gesture. To achieve this, we must refer to the variable mustResumePlayback
to assess if soundtrack must be resumed.
Filename:MusicPlayerView.cs
|
|
For the second event TimeTracker_DragCompleted(), this is the moment when the user has finished moving the cursor on the Slider. When this happens, we need to readjust the playback head of the MusicPlayer
accordingly to the new cursor position. To do this, we use the SeekTo() method that is exposed by the MediaElement class, with a time reference as parameter (in TimeSpan format).
MusicPlayer.Position
.Relaunch the project and make sure you can move forward or backward in the piece!
You have just finished implementing a great feature for the user, well done!
Next time, we will see how to implement the volume management. See you soon!
More articles in the series: