Using an IValueConverter to create a player with a seek-bar on Windows Phone


ValueConverterData binding is a powerful feature in Silverlight but often you need to bind properties that are not directly the same type. Let’s say we want to have a media player and a slider that works as a seek bar. You can’t directly bind the MediaElement’s Position-property to the sliders value, because they are not the same type. To make the binding work, you would use a converter.

I’ll assume you know the basics of Windows Phone development and are familiar with DataBinding, if not you should check those articles out.

We’ll start off by adding a .mp3-file to our project. Right-click on your project and select Add->Existing Item. For the sake of this demo I’m using the Kalimba.mp3 from Music-library->Sample Music.

image

Adding the elements

Next we’ll add a MediaElement and a slider to our MainPage’s ContentPanel, so we can play the music and control the position with our slider.

MainPage.xaml:

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" 
                   Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" 
                   Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <MediaElement />
        <Slider />
    </Grid>
</Grid>

To play the .mp3-file you just need to define the Source-property for the MediaElement, we’ll also name the elements and set the LargeChange of the slider to 30. This means that when we click on the seekbar the MediaElement’s position will move 30 seconds. Now if you run your application (F5) you’ll heard the awesome Kalimba-song!

MainPage.xaml

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <MediaElement x:Name="myMusic" Source="Kalimba.mp3"  />
    <Slider x:Name="slider" LargeChange="30" />
</Grid>

Next we’ll need to set the maximum value of our slider to the MediaElements duration. We’ll have to set this once the MediaElement is loaded and ready so hook-up to the CurrentStateChanged-event. Remember that you can let VisualStudio create the event handler automatically if you use the TAB-key when defining it in XAML.

MainPage.xaml

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <MediaElement x:Name="myMusic" Source="Kalimba.mp3" 
                  CurrentStateChanged="myMusic_CurrentStateChanged" />
    <Slider x:Name="slider" LargeChange="30" />
</Grid>

MainPage.xaml.cs

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
    }

    private void myMusic_CurrentStateChanged(object sender, RoutedEventArgs e)
    {
        if (myMusic.CurrentState == MediaElementState.Playing)
        {
            slider.Maximum = myMusic.NaturalDuration.TimeSpan.TotalSeconds;                
        }
    }
}

Creating a converter

To create a converter you need to add a new C# Class in your project, right-click on the project and select Add->New Item->C# Class and name the class PositionConverter.cs.

In your PositionConverter implement the IValueConverter-interface. You’ll notice that Visual Studio doesn’t recognize the interface, so right-click on it and select Resolve->Add “using System.Windows.Data;” Now a new using-statement is added and the interface is found. Next you need to implement the interface, so right-click on IValueConverter and select Implement Interface->Implement Interface. Now you have 2 methods, Convert and ConvertBack.

In our Convert-method we’ll take the MediaElements Position-property which is a TimeSpan and take it’s seconds to send it to our slider. The value comes as a object so we’ll have to parse it into a TimeSpan before we can get the seconds. This way the slider will show the MediaElements Position with a second accuracy.

PositionConverter.cs

public class PositionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double position = 0;
        TimeSpan timespan = TimeSpan.Parse(value.ToString());
        position = timespan.TotalSeconds;
        
        return position;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Next we’ll implement the ConverBack-method so when we move the slider the MediaElements Position will update. So take the sliders Value (double) and convert it to the MediaElements Position (TimeSpan).

PositionConverter.cs

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    return TimeSpan.FromSeconds((double)value);
}

Use the Converter

The last thing to do is to bind the Slider and the MediaElement and use our Converter in between. First we’ll need to add a new namespace to MainPage.xaml so we can use classes from our project in the XAML.

MainPage.xaml

<phone:PhoneApplicationPage 
    x:Class="ValueConverterSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:app="clr-namespace:ValueConverterSample"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

Then we’ll add the converter as a resource to our LayoutRoot using the namespace app that we defined. We’ll give it a key of “PositionConverter”, you’ll use this key to access the PositionConverter from XAML.

MainPage.xaml

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.Resources>
        <app:PositionConverter x:Key="PositionConverter" />
    </Grid.Resources>
    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" 
                Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" 
                Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <MediaElement x:Name="myMusic" Source="Kalimba.mp3" 
                        CurrentStateChanged="myMusic_CurrentStateChanged"  />
        <Slider x:Name="slider" LargeChange="30" />
    </Grid>
</Grid>

Now finish off by creating the binding. We’ll tell the slider that its Value-Property will be bound to the MediaElements position with a TwoWay binding, so they can update each other. We’ll also define a Converter that we want to be used with this binding. We can access the PositionConverter with the StaticResource-syntax because we defined it as a resource for LayoutRoot.

MainPage.xaml

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <MediaElement x:Name="myMusic" Source="Kalimba.mp3" 
                    CurrentStateChanged="myMusic_CurrentStateChanged"  />
    <Slider x:Name="slider" LargeChange="30" 
            Value="{Binding ElementName=myMusic, 
                            Path=Position, 
                            Mode=TwoWay, 
                            Converter={StaticResource PositionConverter}}" />
</Grid>

Now run the application (F5) and enjoy the music and the massive control you have over it! ;)

DownloadSummary

The goal of this post was to show you how to bind elements and objects together even if they don’t have the same type. IValueConverters are extremely powerful and be of great help for you when creating your apps. As always you can click on the icon on the left and download the finished project.

About these ads

3 Comments on “Using an IValueConverter to create a player with a seek-bar on Windows Phone”

  1. [...] Hopefully you know understand how the basics of binding works. There are some limitations here though. You might want to bind two properties that aren’t the same type, for that you’ll need to create a ValueConverter, you can read about there here, Using an IValueConverter to create a player with a seek-bar on Windows Phone [...]

  2. jaytbennett says:

    Great and very informative post but I’m curious as to whether this method could be used if you don’t have a MediaElement in the XAML to bind to.

    Specifically, I’m using a Background AudioPlayerAgent and I’m trying to bind it’s Progress to a progressBar as a visual representation of the background agent’s elapsed progress. Could this converter be used for such a one way binding in C# without XAML?

    • jaytbennett says:

      Answering my own question it’s possible in C# except that the BackgroundAudioPlayer’s Track Position doesn’t seem to implement INotifyProperty changed so you can’t live refresh the property, unless I’m missing something here?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.