12/02/2012

[XAML] Les Converters

Bon aujourd'hui un petit topo sur les converters en WPF, c'est simple, rapide et ça peut rapporter gros !!
Après une expérience de conversions tout à fait improbable (sur 4 couches applicatives!!!) sur un projet en Debug, il va être bon de rappeller les fondemmentaux :

a - Conversion monétaire


Allez on assume que les prix sont stockés dans notre base en Euros sous un format decimal !
Alors on va créer notre converter :
Using ...

namespace MyConverterApp
{
   [System.Windows.Data.ValueConversion(typeof(decimal), typeof(string))]
   class ConvertMoneyInEuros : System.Windows.Data.IValueConverter
   {
       public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
       {
           decimal myPrice = decimal.Parse(value.ToString());
           return myPrice.ToString("C");
       }

       public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
       {
           string aMyPrice = value.ToString();
           decimal result;
           if(decimal.TryParse(aMyPrice, System.GLobalization.NumberStyles.Any, null, out result)
           {
              return result;
           }
           else
              return 0;
       }
   }
}

Ensuite on instancie dans le XAML :
<Window x:Class="MyConverterApp"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 xmlns:local="clr-namespace:MyConverterApp"

        Title="MainWindow" Height="350" Width="525">
<Window.Resources>
   <local:ConvertMoneyInEuros x:Key="DisplayInEuros"/>
</Window.Resources>
 ...
<Window>

Puis il ne nous reste plus qu'à l'utiliser dans notre Elément d'interface (en supposant un DataContext dans la Grid) :
<Grid Name="Grid1">
   <StackPanel>
      <label Content={Binding Path=UnitPrice, Converter={StaticResource DisplayInEuros}}
   </StackPanel>

Et cela nous affiche un joli 12,50 € !

Supposons maintenant que nous souhaitions l'afficher en dollars US mais pas comme font certains, qu'un produit à $6.00 ne devienne pas un produit vendu à 6,00 €... Mais bien que notre "12,50 €" devienne un "$9.52" Alors on créé simplement un nouveau converter :
Using ...

namespace MyConverterApp
{
   [System.Windows.Data.ValueConversion(typeof(decimal), typeof(string))]
   class ConvertMoneyInUSDollars : System.Windows.Data.IValueConverter
   {
      readonly decimal _rate = decimal.Parse("1,3131");
      readonly System.Globalization.CultureInfo _usCulture = new System.Globalization.CultureInfo("en-US");
      readonly System.Globalization.CultureInfo _currentcult = System.Globalization.CultureInfo.CurrentCulture;

      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
      {
         decimal myPrice = decimal.Parse(value.ToString());
         myPrice = myPrice /_rate
         return myPrice.ToString("C", _usCulture);
         
      }

      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
      {
         string aMyPrice = value.ToString();
         decimal result;
         if(decimal.TryParse(aMyPrice, System.GLobalization.NumberStyles.Any, null, out result)
         {
            result = result * _rate;
            return result;
         }
         else
            return 0;
      }
   }
}

Supposons maintenant souhaite basculer dynamiquement dans le code d'un affichage en Euros à un affichage en Dollars US avec un bouton radio par exemple :
        private void RadioButtonEURClick(object sender, RoutedEventArgs e)
        {
            var bd = new Binding();
            bd.Converter = new ConvertMoneyInEuros();
            bd.Path = new PropertyPath("UnitPrice");
            bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            bd.Mode = BindingMode.TwoWay;

            MyDynamicTextBox.SetBinding(TextBox.TextProperty, bd);
        }

        private void RadioButtonUSDClick(object sender, RoutedEventArgs e)
        {
            var bd = new Binding();
            bd.Converter = new ConvertMoneyInUsDollars();
            bd.Path = new PropertyPath("UnitPrice");
            bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            bd.Mode = BindingMode.TwoWay;

            MyDynamicTextBox.SetBinding(TextBox.TextProperty, bd);
        }

b - Conversion d'image


Les exemples présentés ici sont uniquement pour illustrer à nouveau les principes des converters à vous de les réadapter.
Voilà un converter pour remplacer le chemin d'accès à une image en image :
Using ...

namespace MyConverterApp
{
   [System.Windows.Data.ValueConversion(typeof(string), typeof(BitmapImage))]
   class ConvertStringToBitmap : System.Windows.Data.IValueConverter
   {

      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
      {
         try
         {
            string myPath = (string)value;
            Uri myUri = new Uri(myPath);
            BitmapImage myImage = new BitmapImage(myUri);
            return myImage;
         }
         catch
         {
            return new BitmapImage(new Uri("C:\\ImageNotAvailable.jpg"));
         }
      }
      
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo cultureInfo)
      {
         throw new NotImplementedException();
      }
   }
}

Pour résumer, les converters sont un moyens efficaces d'effectuer diverses convertions au niveau de l'IHM.