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" />
其實兩種方法實現的都差不多,都需要計時器定時的去獲取當前視頻的進度,但第二種方面顯然要優雅一些。