有一个好的答案,但我想再添加另一种在WPF中构建seekbar的方式,因为我也正在进行类似的项目。
以下是用于该查找器的XAML代码:
<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>
我们 ViewModel 中的 CurrentTrackLenght
和 CurrentTrackPosition
是:
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));
}
}
这个想法非常简单;一旦我们开始播放:
首先,我们获取音频文件的长度(以秒为单位),并将其分配给
CurrentTrackLenght
属性,它将绑定到seekbar的
Maximum
属性。
然后,当我们播放音频文件时,我们不断更新
CurrentTrackPosition
属性,这反过来又驱动了我们的seekbar的
Value
属性。
因此,当我们按下“播放”按钮时,我们ViewModel中的以下命令将运行:
private void StartPlayback(object p)
{
if (_playbackState == PlaybackState.Stopped)
{
if (CurrentTrack != null)
{
_audioPlayer.LoadFile(CurrentTrack.Filepath, CurrentVolume);
CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds();
}
}
_audioPlayer.TogglePlayPause(CurrentVolume);
}
_audioPlayer
是我用来简化播放/暂停/停止的抽象,因此您可以用自己的代码替换它们。但是重要的部分是:
CurrentTrackLenght = _audioPlayer.GetLenghtInSeconds()
AudioPlayer
中 GetLenghtInSeconds()
的代码如下:
public double GetLenghtInSeconds()
{
if (_audioFileReader != null)
{
return _audioFileReader.TotalTime.TotalSeconds;
}
else
{
return 0;
}
}
因此,我们需要为每个开始播放的音频文件初始化seekbar的最大值。
现在,我们需要在音频播放时更新我们的seekbar。
首先,我们需要确定音频文件的当前位置(以秒为单位)。在这里我选择以秒为单位,因为我们的seekbar的Maximum
也是以秒为单位的,所以它们将正确匹配。
要做到这一点,我们需要在AudioPlayer
中使用以下方法:
public double GetPositionInSeconds()
{
if (_audioFileReader != null)
{
return _audioFileReader.CurrentTime.TotalSeconds;
}
else
{
return 0;
}
}
代码完成后,我们可以继续处理ViewModel。首先,我们需要在构造函数中设置一个计时器。
var timer = new System.Timers.Timer()
timer.Interval = 300
timer.Elapsed += Timer_Elapsed
timer.Start()
添加 Timer_Elapsed()
和 UpdateSeekBar()
方法:
private void UpdateSeekBar()
{
if (_playbackState == PlaybackState.Playing)
{
CurrentTrackPosition = _audioPlayer.GetPositionInSeconds();
}
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UpdateSeekBar();
}
完成这个步骤后,现在我们播放音频文件时,滑动条应该按预期移动。
接下来是实际的寻找部分,首先我们需要在
AudioPlayer
类中添加一个
SetPosition()
方法。
public void SetPosition(double value)
{
if (_audioFileReader != null)
{
_audioFileReader.CurrentTime = TimeSpan.FromSeconds(value);
}
}
这段代码将当前时间设置为我们传递的值,因此有效地寻找到新位置。
最后,我们需要4个方法来完成PreviewMouseDown
和PreviewMouseUp
事件的ViewModel命令。
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;
}
如果您想准确了解这些是如何实现的,您可以访问我的
GitHub页面并查看完整的实现。