Utilisation simple de IValueConverter : l’Horloge

Vous connaissez sans doute tous le DataBinding en XAML, celui ci nous permet de manière simple de lier des données à des contrôles de notre vue. Ces données peuvent aussi bien venir d’un autre élément de notre vue que d’une source externe (DataContext).
Imaginons par exemple que nous voulons afficher l’heure dans un TextBlock, pour cela dans notre ViewModel, nous créons une propriété de type String qui retournera l’heure courante et grâce à DispatcherTimer réglé sur 500 millisecondes, nous signalons à la vue le changement de valeur de l’heure deux fois par seconde:

using System;
using System.ComponentModel;
using System.Windows.Threading;

namespace HorlogeSilverlight.ViewModel
{
   public class HorlogeViewModel : INotifyPropertyChanged
   {
       DispatcherTimer timer;
       public string Heure
       {
          get
          { return
DateTime
.Now.TimeOfDay.ToString(); }

}

public
HorlogeViewModel()
{
timer = new DispatcherTimer();
timer.Interval =
newTimeSpan(500);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}

void
timer_Tick(object sender,
EventArgs
e)
{

if
(PropertyChanged != null)
NotifyPropertyChanged(« Heure »);
}

public
event
PropertyChangedEventHandler
PropertyChanged;

protected
void NotifyPropertyChanged(params string[]
propNames)
{

if
(PropertyChanged != null)
{

foreach
(string name
in
propNames)
{
PropertyChanged(this, new
PropertyChangedEventArgs
(name));
}
}
}
}
}

Puis notre TextBlock sera ainsi (en ayant au préalable défini la classe précédente
comme DataContext de notre vue):
<TextBlock Text= »{Binding Heure, Mode=OneWay} »/>
Vous pouvez d’ores et déjà voir que le TextBlock se met à jour toute les demi secondes dans le designer de Visual Studio et d’Expression Blend.
Notre horloge est déjà fonctionnelle mais disons qu’elle manque un peu de grâce.
Pourquoi ne pas faire une horloge avec des aiguilles qui tournent ?
Pour représenter les aiguilles, nous allons faire avec trois simples « Path » dont le bas est au centre de la fenêtre, nous déplaçons de même leur centre de rotation pour le positionner en bas :
<Path x:Name= »Secondes«  Data= »M167,28 L167,99.063354″ Fill= »#FFF4F4F5″HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″ Margin= »0,0,0,100″ RenderTransformOrigin= »0,1″>

<
Path.RenderTransform>

<
CompositeTransform Rotation= »0″/>

</
Path.RenderTransform>
</Path>
<Path x:Name= »Minutes » Data= »M167,28 L167,99.063354″ Fill= »#FFF4F4F5″HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″StrokeThickness= »2″ Margin= »0,0,0,100″ RenderTransformOrigin= »0,1″>

<
Path.RenderTransform>

<
CompositeTransform Rotation= »0″/>

</
Path.RenderTransform>
</Path>
<Path x:Name= »Heures«  Data= »M167,28 L167,99.063354″ Fill= »#FFF4F4F5″HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″StrokeThickness= »3″ Margin= »0,0,0,100″ RenderTransformOrigin= »0.5,1″>
<Path.RenderTransform>
<CompositeTransform Rotation= »0″/>
</Path.RenderTransform>
</Path>
La propriété qui nous intéresse de binder est la propriété « Rotation » de CompositeTransform de chaque Path, mais cette propriété est de type Double et notre propriété Heure est elle de type String, si nous effectuons le binding directement, cela ne fonctionnera pas !!
Heureusement, il est possible de convertir une valeur source juste avant de l’affecter à une propriété cible et cela grâce à des classes implémentant l’interface IValueConverter.
Cette interface contient deux méthodes, Convert et ConvertBack renvoyant toutes deux un Object et prenant quatres paramètres. Ces paramètres sont un object qui est la valeur source à convertir, un Type représentant le type de valeur cible, un object permettant de passer un paramètre au converter et enfin des informations sur la culture. Ici, ce sont les paramètres « value » et « parameter » qui vont nous intéresser.
L’idée ici est de récupérer un objet de type DateTime à partir de la valeur source, de le formater à partir d’une chaine de caractère passée en paramètre du converter afin de récupérer une information spécifique de l’Heure (soit le nombre de secondes, soit le nombre de minutes, soit le nombre d’heures) et afin par un simple calcul passer cette information en un double entre 0 et 360 (un tour complet d’aiguilles).
Le code du converter sera donc le suivant (la méthode ConvertBack n’est pas implémentée ici car elle ne sert que lors de binding en mode TwoWay ou OneWayToSource) :
public
class
HeureToRotationConverter :
IValueConverter
{

public
object Convert(object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo
culture)
{

try
{
string format = (string)parameter;

if
(string.IsNullOrEmpty(format))

return
0;

var
d = DateTime.Parse(value.ToString());

if
(format.Equals(« ss »))

return
double.Parse(d.ToString(format))
* 6;

else
if (format.Equals(« mm »))

return
double.Parse(d.ToString(format))
* 6 + (double.Parse(d.ToString(« ss »))
/ 10);

else
if (format.Equals(« HH »))

return
double.Parse(d.ToString(format))
* 30 + (double.Parse(d.ToString(« mm »))
/ 10);

else

return
d.ToString(format);
}

catch
(Exception ex)
{

throw
newException(« Paramètre de formatage invalide. » + ex.Message);
}
}

public
object ConvertBack(object
value, Type targetType,
object
parameter, System.Globalization.CultureInfo
culture)
{
thrownewNotImplementedException();
}
}
Maintenant, il ne nous reste plus qu’à revenir dans notre vue, déclarer notre converter dans les ressources de la page et faire notre binding en utilisant notre converter et en passant en paramètre le format à appliquer à la date (« ss », « mm » ou « HH ») :
<UserControl.Resources>
<conv:HeureToRotationConverter x:Key= »HeureToRotationConverter »/>
</UserControl.Resources>
<Path x:Name= »Secondes » Data= »M167,28
L167,99.063354″
Fill= »#FFF4F4F5″ HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″ Margin= »0,0,0,100″ RenderTransformOrigin= »0,1″>

<
Path.RenderTransform>

<
CompositeTransform Rotation= »{Binding Heure, ConverterParameter=ss, Converter={StaticResource HeureToRotationConverter}, Mode=OneWay} »/>

</
Path.RenderTransform>

</
Path>

<
Path x:Name= »Minutes » Data= »M167,28 L167,99.063354″ Fill= »#FFF4F4F5″ HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″ StrokeThickness= »2″ Margin= »0,0,0,100″ RenderTransformOrigin= »0,1″>

<
Path.RenderTransform>

<
CompositeTransform Rotation= »{Binding Heure, ConverterParameter=mm, Converter={StaticResource HeureToRotationConverter}, Mode=OneWay} »/>

</
Path.RenderTransform>

</
Path>

<
Path x:Name= »Heures » Data= »M167,28 L167,99.063354″ Fill= »#FFF4F4F5″ HorizontalAlignment= »Center » Height= »100″ Stretch= »Fill » Stroke= »Black » UseLayoutRounding= »False » VerticalAlignment= »Center » Width= »3″ StrokeThickness= »3″ Margin= »0,0,0,100″ RenderTransformOrigin= »0.5,1″>
<Path.RenderTransform>
<CompositeTransform Rotation= »{Binding Heure, ConverterParameter=HH, Converter={StaticResource HeureToRotationConverter},
Mode
=OneWay} »/>
</Path.RenderTransform>
</Path>
Cet exemple est simple, mais il permet de voir un cas typique d’utilisation des converters, une autre utilisation courante est le « BoolToVisibilityConverter », mais je vous laisse essayer de le réaliser par vous même🙂
A bientôt sur notre blog !!

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s