• 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"/>

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

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

  • 相关阅读:
    《深入理解C#》泛型高级
    vs2019 插件下载慢的解决方法
    C# Tuple和 ValueTuple
    前端ajax用json方式请求 后端php 用 $GLOBALS['HTTP_RAW_POST_DATA'] 取值
    Vue之Axios跨域问题解决方案
    Jquery自定义方法获取URL后面参数
    C# List 某行数据置顶
    EF空字段使用contains查询的解决办法
    sql语句查询,多字段like模糊查询优化
    Asp.Net Core中间件
  • 原文地址:https://www.cnblogs.com/LCHL/p/3084700.html
Copyright © 2020-2023  润新知