WPF MediElement 视频播放

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;

        }
View Code

 

下面再来看看使用依赖属性的做法:

添加一个类,继承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));

            }

        }

    }
View Code
  <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" 
View Code
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();

        }

    }
View Code

xaml中的转换器:

<local:TimeSpanConverter x:Key="TimeSpanConverter"/>

 

 

其实两种方法实现的都差不多,都需要计时器定时的去获取当前视频的进度,但第二种方面显然要优雅一些。

文章就写到这儿,如有疏漏错误,欢迎指正。

 

 

 

你可能感兴趣的:(element)