Reputation: 171
I am writing an audio app on Windows Phone 8. I've created a MediaElement and a seek-bar(slider):
<MediaElement x:Name="player" CurrentStateChanged="GetTrackDuration" />
<Slider x:Name="playerSeekBar" Value="{Binding ElementName=player, Path=Position,
Mode=TwoWay, Converter={StaticResource PositionConverter}}" SmallChange="1" LargeChange="1"/>
And this is my converter code:
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)
{
return TimeSpan.FromSeconds((double)value);
}
}
And here is the CurrentStateChanged event code:
private void GetTrackDuration(object sender, RoutedEventArgs e)
{
var player = (MediaElement)sender;
if (player.CurrentState == System.Windows.Media.MediaElementState.Playing)
playerSeekBar.Maximum = player.NaturalDuration.TimeSpan.TotalSeconds;
}
It all seems to work OK, however there is one problem with the binding to slider - it doesn't update until I click somewhere inside the app - I mean i may click on a button that isn't connected with slider or media element or on a empty space. After I click the slider is being updated and everything works nice. BTW, the music plays normally - even at the beginning when the slider is not being updated. I tried to look on the Internet, however I am not sure what to ask, that's why I am asking You for help. If someone just knows where I could search for the solution, I would be very grateful!:)
Thank You for Your help in advance!
Upvotes: 2
Views: 1615
Reputation: 29792
It looks like a problem when Slider gets Focus, I've tried to find a solution with redirecting Focus, but so far - I haven't found it. Instead I've a diffrent proposal and few remarks to your code:
Get track duration when Media is Opened not when PlayState changes:
private void player_MediaOpened(object sender, RoutedEventArgs e)
{
playerSeekBar.Maximum = (sender as MediaElement).NaturalDuration.TimeSpan.TotalSeconds;
}
Your Slider will probably be updated every little change of MediaElement's position. I think it isn't needed - it can be updated for example every second. So my proposal is - bind your Slider to a property, and notify PropertyChanged every second (use DispatcherTimer
):
// In this case we need INotifyPropertyChanged -
public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
// implementing interface
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string property = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
// Property for Binding
public double SlideValue
{
get { return player.Position.TotalSeconds; }
set { player.Position = TimeSpan.FromSeconds(value); }
}
DispatcherTimer timer = new DispatcherTimer(); // timer
// Get the duration when Media File is opened
private void player_MediaOpened(object sender, RoutedEventArgs e)
{
playerSeekBar.Maximum = (sender as MediaElement).NaturalDuration.TimeSpan.TotalSeconds;
}
public MainPage()
{
InitializeComponent();
this.DataContext = this; // Set the DataContext
Play.Click += Play_Click; // My play method
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s, e) => { RaiseProperty("SlideValue"); };
}
private void Play_Click(object sender, RoutedEventArgs e)
{
player.AutoPlay = true;
player.Source = new Uri("music.mp3", UriKind.RelativeOrAbsolute);
timer.Start(); // DON'T forget to start the timer.
}
In this case you no longer need Converters, and your XAML code can look like this:
<MediaElement x:Name="player" MediaOpened="player_MediaOpened"/>
<Slider x:Name="playerSeekBar" Value="{Binding SlideValue, Mode=TwoWay}" SmallChange="1" LargeChange="1"/>
Above code probably still needs some improvements, but works quite fine.
EDIT - method without DataBinding and INotifyPropertyChanged
You can also accomplish your task simpler way without Binding, just using Timer
and LostMouseCapture
:
public partial class MainPage : PhoneApplicationPage
{
private double totalSeconds = 1;
DispatcherTimer timer = new DispatcherTimer();
private void player_MediaOpened(object sender, RoutedEventArgs e)
{
totalSeconds = (sender as MediaElement).NaturalDuration.TimeSpan.TotalSeconds;
}
public MainPage()
{
InitializeComponent();
Play.Click += Play_Click;
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s, e) => { playerSeekBar.Value += (double)(1 / totalSeconds); };
playerSeekBar.LostMouseCapture += (s, e) =>
{ player.Position = TimeSpan.FromSeconds(playerSeekBar.Value * totalSeconds); };
}
private void Play_Click(object sender, RoutedEventArgs e)
{
player.AutoPlay = true;
player.Source = new Uri("music.mp3", UriKind.RelativeOrAbsolute);
timer.Start(); // DON'T forget to start the timer.
}
}
In XAML:
<MediaElement x:Name="player" MediaOpened="player_MediaOpened"/>
<Slider x:Name="playerSeekBar" Value="0" SmallChange="0.01" Maximum="1.0"/>
Hope this helps.
Upvotes: 1