首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF MediaElement 播放器進度條實作:傳統 Timer 與依賴屬性封裝兩種做法

     編輯:關於.NET

MediaElement.Position 與 NaturalDuration 不是依賴屬性,無法直接繫結。本文示範兩種實作播放進度條的方式:傳統 DispatcherTimer 輪詢,與封裝依賴屬性的優雅做法。

方法一:傳統 DispatcherTimer

<MediaElement Name="player" Source="demo.wmv" Loaded="player_Loaded" MediaOpened="player_MediaOpened"/>
<Slider Name="slider" ValueChanged="slider_ValueChanged"/>
<TextBlock Name="tb1"/><TextBlock Name="tb2"/>

private void player_Loaded(object sender, RoutedEventArgs e)
{
    var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
    timer.Tick += (s, e) => {
        var ts = player.Position;
        tb1.Text = $"{ts:hh\\:mm\\:ss}";
        slider.Value = ts.TotalMilliseconds;
    };
    timer.Start();
}

private void player_MediaOpened(object sender, RoutedEventArgs e)
{
    var ts = player.NaturalDuration.TimeSpan;
    tb2.Text = $"{ts:hh\\:mm\\:ss}";
    slider.Maximum = ts.TotalMilliseconds;
}

private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    player.Position = TimeSpan.FromMilliseconds(e.NewValue);
}

方法二:繼承 MediaElement 封裝依賴屬性

public class MediaElementExt : MediaElement, INotifyPropertyChanged
{
    private DispatcherTimer timer;
    private DateTime _lastChanged = DateTime.Now;

    public double CurrentTime
    {
        get => Position.TotalMilliseconds;
        set
        {
            if ((DateTime.Now - _lastChanged).TotalMilliseconds > 50) // 節流
            {
                Position = TimeSpan.FromMilliseconds(value);
                _lastChanged = DateTime.Now;
            }
        }
    }

    public double DurationTime => NaturalDuration.HasTimeSpan ? NaturalDuration.TimeSpan.TotalMilliseconds : double.NaN;

    public MediaElementExt()
    {
        timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
        timer.Tick += (s, e) => RaisePropertyChanged(nameof(CurrentTime));
        MediaOpened += (s, e) => { RaisePropertyChanged(nameof(DurationTime)); timer.Start(); };
        MediaEnded += (s, e) => timer.Stop();
        MediaFailed += (s, e) => timer.Stop();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void RaisePropertyChanged(string n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
}

XAML 繫結

<local:MediaElementExt x:Name="player" Source="demo.wmv"/>
<Slider Value="{Binding CurrentTime, ElementName=player, Mode=TwoWay}"
        Maximum="{Binding DurationTime, ElementName=player, Mode=OneWay}"/>
<TextBlock Text="{Binding Value, ElementName=slider, Converter={StaticResource TimeSpanConverter}}"/>
<TextBlock Text="{Binding DurationTime, ElementName=player, Converter={StaticResource TimeSpanConverter}}"/>

TimeSpanConverter

public class TimeSpanConverter : IValueConverter
{
    public object Convert(object v, Type t, object p, CultureInfo c)
        => TimeSpan.FromMilliseconds((double)v).ToString(@"hh\:mm\:ss");
    public object ConvertBack(...) => throw new NotImplementedException();
}

比較總結

面向Timer 輪詢依賴屬性封裝
程式碼量較少較多(需建立類別)
MVVM 友善否是,支援雙向繫結
效能固定間隔輪詢同樣需 Timer,但可加入節流
維護性分散在 Code-behind封裝於控制項,可重用

建議專案採用方法二,長期維護與擴充性較佳。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved