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 AudioPlayerclass.
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.