在WPF中将进度条绑定到媒体元素

9

在c#/wpf中,我向我的窗口添加了一个进度条和媒体元素。想法是,进度条显示媒体元素已经播放的部分。

我尝试使用以下xaml:

<Window x:Class="TestApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="627" Width="889">
    <Grid>
    <MediaElement Margin="152,8,140,41" Name="mediaElement1" MediaEnded="mediaElement1_MediaEnded" Visibility="Hidden" />
    <ProgressBar Height="23" Margin="152,8,10,0" Name="mp3PlayingProgressBar" VerticalAlignment="Top" Foreground="DarkBlue" Maximum="{Binding Path=NaturalDuration.TimeSpan.TotalSeconds, Mode=OneWay, ElementName=mediaElement1}" Value="{Binding Path=Position.TotalSeconds, Mode=OneWay, ElementName=mediaElement1}" />
    </Grid>
</Window>

我尝试将最大值和值属性绑定到媒体元素。但是,当我将mp3文件加载到媒体元素中时,进度条没有任何变化。(音乐正在播放,因此mp3已正确加载和播放)。

我更喜欢用绑定方式实现。

我在这里做错了什么?

5个回答

12

这是因为媒体没有打开,因此进度条不知道最大值。尝试使用以下事件:MediaOpened用于媒体打开时,MouseLeftButtonUp用于进度条或滑块。我已经尝试过,可以正常工作。

public partial class AudioPage : Page
{
    TimeSpan _position;
    DispatcherTimer _timer = new DispatcherTimer();

    public AudioPage()
    {
        InitializeComponent();
        _timer.Interval = TimeSpan.FromMilliseconds(1000);
        _timer.Tick += new EventHandler(ticktock);
        _timer.Start();
    }

    void ticktock(object sender, EventArgs e)
    {
        sliderSeek.Value = media.Position.TotalSeconds;
    }

    // Executes when the user navigates to this page.
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
    }

    private void Play_Click(object sender, RoutedEventArgs e)
    {
        media.Play();
    }

    private void Pause_Click(object sender, RoutedEventArgs e)
    {
        media.Pause();
    }

    private void Stop_Click(object sender, RoutedEventArgs e)
    {
        media.Stop();
    }

    private void media_MediaOpened(object sender, RoutedEventArgs e)
    {
        _position = media.NaturalDuration.TimeSpan;
        sliderSeek.Minimum = 0;
        sliderSeek.Maximum = _position.TotalSeconds;
    }

    private void sliderSeek_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        int pos = Convert.ToInt32(sliderSeek.Value);
        media.Position = new TimeSpan(0, 0, 0, pos, 0);
    }
}

关联的XAML如下所示...

<Grid x:Name="LayoutRoot" Background="#FFFFE8E8">
    <Grid.RowDefinitions>
        <RowDefinition Height="220*" />
        <RowDefinition Height="75*" />
    </Grid.RowDefinitions>
    <MediaElement Height="189" HorizontalAlignment="Left" Margin="12,12,0,0" Name="media" VerticalAlignment="Top" Width="399" Source="anyfile.mp3" AutoPlay="True" MediaOpened="media_MediaOpened" />
    <StackPanel Orientation="Horizontal" Grid.Row="1" Height="30" Margin="12,8,163,37">
        <TextBlock Text="Volume" VerticalAlignment="Center"></TextBlock>
        <Slider Margin="2" Maximum="1" Minimum="0" Width="102" Value="{Binding Path=Volume, Mode=TwoWay, ElementName=media}"></Slider>
        <TextBlock Text="{Binding ElementName=media, Path=Volume, Mode=OneWay, StringFormat=0.00}" VerticalAlignment="Center" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Grid.Row="1" Height="30" Margin="26,47,163,-2">
        <TextBlock Text="Seek" VerticalAlignment="Center"></TextBlock>
        <Slider Margin="2" Width="104" Name="sliderSeek" MouseLeftButtonUp="sliderSeek_MouseLeftButtonUp"></Slider>
        <TextBlock Text="{Binding ElementName=sliderSeek, Path=Value, StringFormat=0}" VerticalAlignment="Center"></TextBlock>
    </StackPanel>
    <StackPanel Orientation="Horizontal" Grid.Row="1" Height="30" Margin="266,8,12,37">
        <Button Name="Play" Content="Play" Margin="2" Click="Play_Click" />
        <Button Name="Pause" Content="Pause" Margin="2" Click="Pause_Click" />
        <Button Name="Stop" Content="Stop" Margin="2" Click="Stop_Click" />
    </StackPanel>
</Grid>

你正在使用计时器更新值。我更喜欢使用绑定来完成。这不可能吗? - Peter Fortuin
1
我不能确定它是否可能。但是因为我无法通过绑定使其工作,所以我编写了这个解决方案。我意识到问题发生的原因是媒体没有被打开,所以我选择编写了这段代码。也许有人会纠正我并提供更好的解决方案。我会关注这个线程的。 - Rahul Soni

3

我尝试绑定这些值,但是没有成功:

<ProgressBar 
    x:Name="testProgressBar" Minimum="0" 
    Maximum="{Binding NaturalDuration.TimeSpan.TotalSeconds, ElementName=mediaPlayer}" 
    Value="{Binding Position.TotalSeconds, ElementName=mediaPlayer, Mode=OneWay}" />

太遗憾了,它无法工作。
恐怕你需要设置一个定时器。
类似于:

*.xaml

<ProgressBar x:Name="testProgressBar" />

*.cs

    public MainWindow()
    {
        InitializeComponent();
        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += timer_Tick;
        timer.Start();
    }
    private void timer_Tick(object sender, EventArgs e)
    {
        if (mediaPlayer.Source != null && mediaPlayer.NaturalDuration.HasTimeSpan)
        {
            testProgressBar.Minimum = 0;
            testProgressBar.Maximum = mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds;
            testProgressBar.Value = mediaPlayer.Position.TotalSeconds;
        }
    }

亲切的问候,
Thimo。

3
我尝试回答那个人想知道的问题,但不起作用,因此我提供了一个替代方案。这个替代方案可以实现提问者希望达到的效果,我的目的只是为了帮助请求帮助的人,对我来说这就是一个答案。好好读一下,直接将媒体播放器中已过去的时间值与进度条绑定是不可能的,因为微软做出了一些奇怪的决定。 - Thimo Braker
2
这是一个答案,而且是一个很好的答案!我正好遇到了OP的情况,这个答案解决了我的问题,并且完全符合我的解决方案。 :) - Milkncookiez

1
一份WPF版本的Rahul样例,增加了滑动条实时更改媒体位置的功能。
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        _timer.Interval = TimeSpan.FromMilliseconds(1000);
        _timer.Tick += new EventHandler(ticktock);
        _timer.Start();

        Open(@"filename.mp3");
    }
    public void Open(string fileName)
    {
        var uriPath = "file:///" + fileName.Replace("\\", "/");
        media.Source=new Uri(uriPath);
    }

    TimeSpan _position;
    DispatcherTimer _timer = new DispatcherTimer();

    void ticktock(object sender, EventArgs e)
    {
        if (!sliderSeek.IsMouseCaptureWithin)
            sliderSeek.Value = media.Position.TotalSeconds;
    }

    private void Play_Click(object sender, RoutedEventArgs e)
    {
        media.Play();
    }

    private void Pause_Click(object sender, RoutedEventArgs e)
    {
        media.Pause();
    }

    private void Stop_Click(object sender, RoutedEventArgs e)
    {
        media.Stop();
    }

    private void media_MediaOpened(object sender, RoutedEventArgs e)
    {
        _position = media.NaturalDuration.TimeSpan;
        sliderSeek.Minimum = 0;
        sliderSeek.Maximum = _position.TotalSeconds;
    }

    private void sliderSeek_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {           
        int pos = Convert.ToInt32(sliderSeek.Value);
        media.Position = new TimeSpan(0, 0, 0, pos, 0);
    }

    private void sliderSeek_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (sliderSeek.IsMouseCaptureWithin)
        {
            int pos = Convert.ToInt32(sliderSeek.Value);
            media.Position = new TimeSpan(0, 0, 0, pos, 0);
        }
    }
}

0

在GitHub上有一个称为Gu.Wpf.Media的包装库,它处理了所有MediaElement问题并提供更多功能。

它支持通过TwoWay绑定自动维护Position属性。无需使用计时器麻烦操作。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接