WPF中可以使用MediaElement控件來進行音視頻播放,然後需要做個進度條啥的,但是MediaElement.Position(進度)和MediaElement.NaturalDuration居然都不是依賴屬性,簡直不能忍!
好吧,首先說說比較傳統的做法(winform?)
slider用來顯示進度以及調整進度,tb1顯示當前進度的時間值,tb2顯示視頻的時長。
player_Loaded 事件中使用DispatcherTimer來定時獲取當前視頻的播放進度,
player_MediaOpened 事件中獲取當前視頻的時長(只有在視頻加載完成後才可以獲取到)
slider_ValueChanged 事件中執行對視頻進度的調整
xaml:
<MediaElement Name="player" Source="e:\MVVMLight (1).wmv" Loaded="player_Loaded"
MediaOpened="player_MediaOpened" />
<Slider Grid.Row="1" Margin="10" Name="slider" ValueChanged="slider_ValueChanged" />
<WrapPanel Grid.Row="1" Margin="0,40,0,0">
<TextBlock Name="tb1" Width="120" />
<TextBlock Name="tb2" Width="120" />
</WrapPanel>
後台代碼:
private void player_Loaded(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += (ss, ee) =>
{
//顯示當前視頻進度
var ts = player.Position;
tb1.Text = string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);slider.Value = ts.TotalMilliseconds;
};
timer.Start();
}
private void player_MediaOpened(object sender, RoutedEventArgs e)
{
//顯示視頻的時長
var ts = player.NaturalDuration.TimeSpan;
tb2.Text = string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
slider.Maximum = ts.TotalMilliseconds;
}
private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//調整視頻進度
var ts = TimeSpan.FromMilliseconds(e.NewValue);
player.Position = ts;
}
本欄目
下面再來看看使用依賴屬性的做法:
添加一個類,繼承MediaElement
public class MediaElementExt : MediaElement, INotifyPropertyChanged
{
private DispatcherTimer timer;
private const string CurrentTimeProperty = "CurrentTime";
/// <summary>
/// 當前播放進度
/// </summary>
public double CurrentTime
{
get
{
return this.Position.TotalMilliseconds;
}
set
{
//進度條拖動太頻繁太久,性能跟不上,所以設置一個時間閥,跳過某些時段
if ((DateTime.Now - _lastChangedTime).TotalMilliseconds > 50)
{
this.Position = TimeSpan.FromMilliseconds(value);
_lastChangedTime = DateTime.Now;
}
}
}
/// <summary>
/// 記錄最後修改進度的時間,
/// </summary>
private DateTime _lastChangedTime = DateTime.Now;
private const string DurationTimeProperty = "DurationTime";
/// <summary>
/// 當前視頻時長
/// </summary>
public double DurationTime
{
get
{
if (this.NaturalDuration.HasTimeSpan)
return this.NaturalDuration.TimeSpan.TotalMilliseconds;
return double.NaN;
}
}
public MediaElementExt()
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += timer_Tick;
this.MediaOpened += (ss, ee) =>
{
//觸發PropertyChanged DurationTime
this.RaisePropertyChanged(DurationTimeProperty);
timer.Start();
};
//發生錯誤和視頻播放完畢 停止計時器
this.MediaEnded += (ss, ee) => { timer.Stop(); };
this.MediaFailed += (ss, ee) => { timer.Stop(); };
}
void timer_Tick(object sender, EventArgs e)
{
//定時觸發PropertyChanged CurrentTime
this.RaisePropertyChanged(CurrentTimeProperty);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Grid DataContext="{Binding ElementName=player}">
<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<!--<MediaElement />-->
<local:MediaElementExt x:Name="player" Source="e:\MVVMLight (1).wmv" />
<Slider Name="slider" Grid.Row="1" Value="{Binding CurrentTime,Mode=TwoWay}" Maximum="{Binding DurationTime,Mode=OneWay}" Margin="10" />
<WrapPanel Grid.Row="1" Margin="0,40,0,0">
<TextBlock Text="{Binding Value, Converter={StaticResource TimeSpanConverter}, Mode=OneWay,ElementName=slider}" Width="120" />
<TextBlock Text="{Binding DurationTime, Mode=OneWay,Converter={StaticResource TimeSpanConverter}}" Width="120" />
</WrapPanel>
</Grid>
--- xmlns:local="clr-namespace:WPF_Player"
public class TimeSpanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var ts = TimeSpan.FromMilliseconds((double)value);
return string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
xaml中的轉換器:
<local:TimeSpanConverter x:Key="TimeSpanConverter" />
其實兩種方法實現的都差不多,都需要計時器定時的去獲取當前視頻的進度,但第二種方面顯然要優雅一些。