There is a good answer but I want to add another way of building a seekbar in WPF, since I was also working on a similar project.
Here is the XAML code for the seeker:
<Slider Grid.Column="0" Minimum="0" Maximum="{Binding CurrentTrackLenght, Mode=OneWay}" Value="{Binding CurrentTrackPosition, Mode=TwoWay}" x:Name="SeekbarControl" VerticalAlignment="Center">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseDown">
<i:InvokeCommandAction Command="{Binding TrackControlMouseDownCommand}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="PreviewMouseUp">
<i:InvokeCommandAction Command="{Binding TrackControlMouseUpCommand}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Slider>
CurrentTrackLenght
and CurrentTrackPosition
in our ViewModel are:
public double CurrentTrackLenght
{
get { return _currentTrackLenght; }
set
{
if (value.Equals(_currentTrackLenght)) return;
_currentTrackLenght = value;
OnPropertyChanged(nameof(CurrentTrackLenght));
}
}
public double CurrentTrackPosition
{
get { return _currentTrackPosition; }
set
{
if (value.Equals(_currentTrackPosition)) return;
_currentTrackPosition = value;
OnPropertyChanged(nameof(CurrentTrackPosition));
}
}
The idea is really simple; once we start playing:
First we get the lenght of the audio file in seconds and assign it to the CurrentTrackLenght
property and it will be bound to seekbar's Maximum
property.
Then as we are playing the audio file, we continuously update the CurrentTrackPosition
property that is in turn driving the Value
property of our seekbar.
So when we press the "Play" button, following command in our ViewModel will run:
private void StartPlayback(object p)
{
if (_playbackState == PlaybackState.Stopped)
{
if (CurrentTrack != null)
{
_audioPlayer.LoadFile(CurrentTrack.Filepath, CurrentVolume);
CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds();
}
}
_audioPlayer.TogglePlayPause(CurrentVolume);
}
_audioPlayer
is an abstraction I used to ease the Play/Pause/Stop, so you can replace those with your own code. But the important part is:
CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds();
And the code for the GetLenghtInSeconds()
in AudioPlayer
is:
public double GetLenghtInSeconds()
{
if (_audioFileReader != null)
{
return _audioFileReader.TotalTime.TotalSeconds;
}
else
{
return 0;
}
}
So with this we initialize our seekbar's Maximum
value for each audio file we start playing.
Now, we need to update our seekbar as audio plays.
First we need to determine the current position of our audio file in seconds. I choose seconds here because our seekbar's Maximum
is in seconds as well so they will match correctly.
To do this we need the following method in AudioPlayer
:
public double GetPositionInSeconds()
{
if (_audioFileReader != null)
{
return _audioFileReader.CurrentTime.TotalSeconds;
}
else
{
return 0;
}
}
With this code done, we can move on to our ViewModel. First we need to set up a timer in our constructor.
var timer = new System.Timers.Timer();
timer.Interval = 300;
timer.Elapsed += Timer_Elapsed;
timer.Start();
And add Timer_Elapsed()
and UpdateSeekBar()
methods:
private void UpdateSeekBar()
{
if (_playbackState == PlaybackState.Playing)
{
CurrentTrackPosition = _audioPlayer.GetPositionInSeconds();
}
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UpdateSeekBar();
}
With this done, now when we play an audio file our seekbar should move as expected.
Now for the actual seeking part, first we need a SetPosition()
method in our AudioPlayer
class.
public void SetPosition(double value)
{
if (_audioFileReader != null)
{
_audioFileReader.CurrentTime = TimeSpan.FromSeconds(value);
}
}
This code sets the current time to the value we pass therefore effectively seeking to the new position.
Finally we need 4 methods to finalize our ViewModel commands for PreviewMouseDown
and PreviewMouseUp
events.
private void TrackControlMouseDown(object p)
{
_audioPlayer.Pause();
}
private void TrackControlMouseUp(object p)
{
_audioPlayer.SetPosition(CurrentTrackPosition);
_audioPlayer.Play(NAudio.Wave.PlaybackState.Paused, CurrentVolume);
}
private bool CanTrackControlMouseDown(object p)
{
if (_playbackState == PlaybackState.Playing)
{
return true;
}
return false;
}
private bool CanTrackControlMouseUp(object p)
{
if (_playbackState == PlaybackState.Paused)
{
return true;
}
return false;
}
If you would like to see exactly how these are implemented, you can go to my github page and see the complete implementation.