Binding a progressbar to a mediaelement in wpf
Asked Answered
C

5

9

In c#/wpf I added a progressbar and mediaelement to my window. The idea was that progressbar is displaying how much is been played in the mediaelement.

I tried it with the following 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>

I tried to bind the Maximum and Value property to the mediaelement. But when I load a mp3 for example into the mediaelement, nothing happends with the progressbar. (The music is playing so the mp3 is loaded and playing correctly).

I prefer to do this with a binding.

What am I doing here wrong?

Cockoftherock answered 30/10, 2010 at 10:6 Comment(0)
P
12

That is because Media is not opened and hence the progress bar is not aware of the maximum value. Try this...

Use the following events for MediaOpened, and MouseLeftButtonUp for your progress bar or slider. I tried it, and it works just fine.

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);
    }
}

The associate XAMl is as follows...

<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>
Permission answered 30/10, 2010 at 10:26 Comment(2)
You are updating the value with a timer. I prefered to do it with a binding. Isn't that possible?Cockoftherock
I can't really say if it is possible or not. But I wrote this solution since I was not able to get it work with Binding. I realized that the issue was happening because media was not getting opened and I chose to wrote this code. Someone may correct me, and provide a better solution. I will stay tuned to this thread.Permission
D
3

I tried to bind the values and came to this, but without success:

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

Too bad it doesn't work.
I am afraid that you will need a timer set up.
Something like:

*.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;
        }
    }

Kind regards,
Thimo.

Discordant answered 8/4, 2014 at 23:37 Comment(2)
Not? I tried what the person wished to know, which does not work, hence I gave an alternative to the question. one that does work and has the effect that the one questioning wishes to achieve, not? I only try to assist the one requesting for help, and in my eyes it is an answer. Read it well. It simply isn't possible to bind the value of the time that has been passed in the media player directly to the progress bar, due to some odd decisions Microsoft made.Discordant
It is an answer, and actually a good one! I had exactly the case of the OP and this answer solved worked exactly how I wanted it for my solution. :)Roobbie
T
1

A WPF version of Rahul's sample with added real-time change of media position while sliding the bar.

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);
        }
    }
}
Thorite answered 9/2, 2016 at 9:17 Comment(0)
U
0

just have look at this example - instead of progressbar he used slider...

Attaching Preview behavior to a WPF Slider control

http://jobijoy.blogspot.com/2009/07/attaching-preview-behavior-to-wpf.html

Underact answered 30/10, 2010 at 11:36 Comment(0)
C
0

There is a wrapper library called Gu.Wpf.Media in GitHub that handles all the problems of the MediaElement and brings more.

It supports binding to Position property via TwoWay binding out of the box. No need to hassle with timers.

Cilla answered 17/1, 2017 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.