Récemment à l’ecole, on nous a demandé deveopper un Windows Media Player 2.0 qui doit gérer une bibliothèque de musique de vidéos et d’image.
Mon groupe et moi même, avons donc choisi de faire une architecture en MVVM et d’utiliser un mediaElement pour la lecture de ses différents fichiers.
Sauf que très vite, je me suis rendu compte d’un problème. La propriété Position du MediaElement n’est pas une DependencyProperty. De ce fait, impossible de faire un binding sur la position.
Apres quelques recherches sur mon moteur de recherche préféré, j’ai trouvé comment résoudre ce problème, mais seulement avec du code behind … En MVVM … Youpi …
Bref, un début de contournement aurait été :
<MediaElement Grid.Column="1" Name="MediaElem" Source="path/vers/ma/musique.mp3" />
<Slider Grid.Column="1" x:Name="SoundSlider" Minimum="0" Maximum="{Binding ElementName=MediaElem,Path=NaturalDuration.TimeSpan.TotalSeconds}"
Value="{Binding Path=Position, ElementName=MediaElem, Converter={x:Static converters:PositionToValueConverter.Instance}, Mode=TwoWay}" />
Sauf que cela pose deux problèmes. Premièrement, à la création de ma page, le média n’est pas chargé donc le maximum du Slider est égal à 0. De plus, le Slider ne se met pas à jour automatiquement.
Pas évident à traiter pour un apprenti du .NET. Donc du coup, pour vous éviter ce genre de souci, je vous propose une solution parmi tant d’autre : l’utilisation d’un behavior.
Je vois passe les formalités sur ce qu’est un behavior, son utilité et son utilisation. Mais l’idée est de faire un behavior héritant du MediaElement afin de pouvoir utiliser les contrôles associés et de pouvoir ajouter des Dependancy Property.
Ci dessous le code C# : MediaElementBehavior
public class SliderBehavior : Behavior<MediaElement>
{
//une DP pour la position
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position", typeof (TimeSpan), typeof (SliderBehavior),
new PropertyMetadata((d, e) =>
((SliderBehavior) d).PropertyChanged((TimeSpan) e.OldValue, (TimeSpan) e.NewValue)));
// une autre pour le maximum
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof (double), typeof (SliderBehavior), new PropertyMetadata(default(double)));
// les proprietes qui vont avec
public double Maximum
{
get { return (double) GetValue(MaximumProperty); }
set {SetValue(MaximumProperty, value);}
}
public TimeSpan Position
{
get { return (TimeSpan) GetValue(PositionProperty); }
set
{
SetValue(PositionProperty, value);
}
}
private readonly Timer _timer = new Timer(1000);
public void PropertyChanged(TimeSpan oldValue, TimeSpan newValue)
{
AssociatedObject.Position = newValue;
}
//Quand le behavior est utilise, on initialise tout.
protected override void OnAttached()
{
base.OnAttached();
_timer.AutoReset = true;
_timer.Elapsed += _timer_Elapsed;
Position = AssociatedObject.Position;
AssociatedObject.MediaOpened += MediaOpened;
_timer.Start();
}
private void MediaOpened(object sender, RoutedEventArgs e)
{
Maximum = AssociatedObject.NaturalDuration.TimeSpan.TotalSeconds;
}
private void DispatcherTimerResync()
{
Position = AssociatedObject.Position;
}
//on utilise un timer pour changer la position toute les secondes.
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
Dispatcher.Invoke(new Action(DispatcherTimerResync));
}
protected override void OnDetaching()
{
_timer.Stop();
_timer.Elapsed -= _timer_Elapsed;
base.OnDetaching();
}
}
Rien de techniquement compliqué là dedans, mis à part la declaration de la DP (sans doute ce qui m’as pris le plus de temps). On déclare un behavior qui herite de MediaElement, et deux Dependency Property qui nous interressent.
Enfin, le Code XAML :
<MediaElement Grid.Column="1" Name="MediaElem" Source="D:\Musique\Musique\Eluveitie\2012 - Helvetios\14 - Alesia.mp3">
<i:Interaction.Behaviors>
<behaviors:SliderBehavior x:Name="sliderDP"/>
</i:Interaction.Behaviors>
</MediaElement>
<Slider Grid.Row="1" x:Name="SoundSlider" Minimum="0"
Maximum="{Binding ElementName=sliderDP, Path=Maximum}"
VerticalAlignment="Center"
Value="{Binding Path=Position, ElementName=sliderDP, Converter={x:Static converters:PositionToValueConverter.Instance}, Mode=TwoWay}" />
Vous remarquerez que cette fois, je n’effectue plus mon binding sur le MediaElement mais sur le Behavior.
Désormais, le MediaElement en MVVM ne devrai plus être un problème :)

