none
Decimal.ToString() : Comportement étrange RRS feed

  • Question

  • Bonjour,

    J'ai un comportement étrange  : decimal.ToString() ne renvoie pas toujours la même chaine de caractères, je m'explique :

    J'ai une grille avec 3 colonnes "bind" avec une classe simple : 

    <Grid x:Name="LayoutRoot" Background="White">
     <sdk:DataGrid AutoGenerateColumns="False" Height="300" HorizontalAlignment="Left" Name="dataGrid1" VerticalAlignment="Top" Width="400" SelectionChanged="dataGrid1_SelectionChanged" BeginningEdit="dataGrid1_BeginningEdit" RowEditEnded="dataGrid1_RowEditEnded">
      <sdk:DataGrid.Columns>
       <sdk:DataGridTextColumn Header="Code" Binding="{Binding Code}" />
       <sdk:DataGridTextColumn Header="Label" Binding="{Binding Label}" />
       <sdk:DataGridTextColumn Header="Value" Binding="{Binding Value, StringFormat=F3}" />
      </sdk:DataGrid.Columns>
     </sdk:DataGrid>
    </Grid>

    Voici la classe :

    public class MyData
    {
     public string Code { get; set; }
     public string Label { get; set; }
     public decimal Value { get; set; }
    }

    Je m'abonne à 2 évènements : BeginningEdit and RowEditEnded and j'ai seulement ces 2 lignes de codes dans chacun des évènements :

    MyData data = e.Row.DataContext as MyData;
    Debug.WriteLine("Value={0}", data.Value);
    Dans l'évènement BeginningEdit le résultat est "Value=12"

    Dans l'évènement RowEditEndedle résultat est "Value=12,000"

    On dirait que le résultat de decimal.ToString() dépend du format de la colonne  Indifferent

    Si j'enlève StringFormat dans le XAML, le résultat est le même dans les 2 évènements.

    Pareil, si je remplace, decimal par double, le résultat est identique dans les 2 évènements.

     

    Quelqu'un peut-il m'expliquer ce comportement ?

    PS : J'ai un projet VS2010 si quelqu'un a un doute Wink

    mercredi 23 juin 2010 06:41

Réponses

  • Bonjour,

    Dans ce cas, soit vous implémentez votre propre Sérialiseur (héritant de XmlDataSerializer). Soit vous ajouter une propriété de type string contenant le nombre au format désiré, et c'est celle-ci que vous sérialisez :

    [DataMember(Name = "Value")]
    public string ValueFormaté
    {
      get { return this.Value.ToString("F3"); }
      set { this.Value = Decimal.Parse(value); }
    }
    

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    vendredi 9 juillet 2010 15:07
    Modérateur

Toutes les réponses

  • Bonjour,

    Lorsque vous écrivez :

    Debug.WriteLine("Value={0}", data.Value);
    

    C'est normal que vous obtenez 12. Il faut écrire :

    Debug.WriteLine("Value={0:F3}", data.Value);
    

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    mercredi 23 juin 2010 08:10
    Modérateur
  • Oui je confirme que c'est normal que la ligne suivante affiche 12 :

     

    Debug.WriteLine("Value={0}", data.Value);

    Ce que je ne comprends pas c'est que ça ne renvoie pas toujours 12 !

    Dans le BeginningEdit(), ca renvoie 12 : normal !

    Dans le RowEditEndedEdit(), ça renvoie 12,000 : Pas normal, car il n'y a pas de format !

     

     

    mercredi 23 juin 2010 14:54
  • Bonjour,

    Pouvez-vous nous montrer le code contenu dans l'événement RowEditEndedEdit() ?

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    jeudi 24 juin 2010 20:36
    Modérateur
  •     private void dataGrid1_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
        {
          Debug.WriteLine("*** dataGrid1_BeginningEdit");
    
          MyData data = e.Row.DataContext as MyData;
          Debug.WriteLine("Value={0}", data.Value);
        }
    
        private void dataGrid1_RowEditEnded(object sender, DataGridRowEditEndedEventArgs e)
        {
          Debug.WriteLine("*** dataGrid1_RowEditEnded");
          MyData data = e.Row.DataContext as MyData;
          Debug.WriteLine("Value={0}", data.Value);
        }
    

    Et le résultat:

    *** dataGrid1_BeginningEdit
    Value=12
    *** dataGrid1_RowEditEnded
    Value=12,000
    

    vendredi 25 juin 2010 09:28
  • Bonjour,

    Pouvez-vous m'envoyer un projet exemple qui reproduit votre problème sur gilles.tourreau@pos.fr ? Merci de mettre l'URL de la conversation dans l'e-mail du message.

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    samedi 26 juin 2010 19:49
    Modérateur
  • Le mail est parti, merci de votre aide :-)
    lundi 28 juin 2010 08:51
  • Bonjour,

     

    Si vous trouvez une solution, je vous prie de nous tenir au courant. Bonne chance avec la continuation de votre projet !

     

    Cordialement,

    Alex

    ________________

    Publiez un article sur une de ces technologies : Visual Basic, C#, C++, .NET, ASP.NET, SQL Server, Silverlight, SharePoint 2010, SharePoint 2007

     

    Astuces pour Visual Studio 2010

    Didacticiels et astuces : VB.NET, C#, ASP.NET, .NET Framework, Workflow Foundation, WPF

     

     

    Café des usages

     

    Microsoft propose ce service gratuitement, dans le but d'aider les utilisateurs et d'élargir les connaissances générales liées aux produits et technologies Microsoft. Ce contenu est fourni "tel quel" et il n'implique aucune responsabilité de la part de Microsoft.

     

     

     

    mardi 29 juin 2010 08:24
    Modérateur
  • Bonsoir,

    Le comportement que vous évoquez est normal (selon la documentation de Microsoft). La méthode Decimal.ToString(), utilise le format "G" (http://msdn.microsoft.com/fr-fr/library/fzeeb5cd.aspx "...la valeur de retour de cette instance est mise en forme avec le spécificateur de format numérique général (G)."

    Dans la documentation concernant le format "G" (http://msdn.microsoft.com/fr-fr/library/dwhawy9k.aspx#GFormatString "Toutefois, si le nombre est un Decimal et que le spécificateur de précision est omis, la notation à virgule fixe est toujours utilisée et les zéros de fin sont conservés.").

    Vous devez donc spécifier un format personnalisé afin de ne pas faire figurer le zéros non significatifs.

    Voici un code simplifié hors Silverlight pour reproduire le formatage des Decimal.

    decimal d;
    
    d = 12.0000M;
    
    Console.WriteLine(d.ToString());

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    mardi 29 juin 2010 21:04
    Modérateur
  • Bonjour,

    Effectivement, je comprends mieux le comportement:

     

          decimal d1, d2;
          d1 = 12.0000M;
          d2 = 12.00M;
          Debug.WriteLine(d1.ToString());
          Debug.WriteLine(d2.ToString());
    

     

    donne le résultat suivant :

     

    12,0000
    12,00
    

     

    Donc  l'affectation via le binding doit expliquer ce comportement.

    Mais, j'en viens à  l'origine de mon problème : Je sérialise en XML l'objet qui contient un décimal et le résultat du XML est différent selon l'évènement (puisque Decimal.ToString() est utilisé). Je n'ai pas la possibilité de mettre un format puisque je n'ai pas la main dans la sérialisation, je pourrais dans l'absolu mais vu la quantité d'objet que j'ai a sérialiser ça me parait compliquer.

    La meilleure solution est-elle d'utiliser un double plutôt d'un decimal ?

    Cordialement,

    Jérôme

    mercredi 30 juin 2010 07:07
  • Bonjour,

    Comment sérialisez vous votre document XML ? Avec le double, vous n'aurez pas ce problème, parcontre vous perdrez en précision...

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    mercredi 30 juin 2010 07:27
    Modérateur
  •       using (MemoryStream stream = new MemoryStream())
          {
            var serializer = new DataContractJsonSerializer(type, knownTypes);
            serializer.WriteObject(stream, data);
            stream.Flush();
            valueToHash = new byte[stream.Length];
            stream.Position = 0;
            stream.Read(valueToHash, 0, valueToHash.Length);
            stream.Close();
          }
          byte[] bytes = sha1.ComputeHash(valueToHash, 0, valueToHash.Length);
    
    L'idée est de calculer une valeur de hash sur la sérialisation XML. 
    mercredi 30 juin 2010 08:17
  • Bonjour,

    Dans ce cas implémenter votre propre substitut de serializer afin de formater comme vous le souhaitez les nombres de type decimal : http://msdn.microsoft.com/fr-fr/library/ms733064.aspx

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    mercredi 30 juin 2010 09:21
    Modérateur
  • Effectivement, je vais devoir en arriver là, j'aurais aimé plus simple. Merci de votre aide :)
    mercredi 30 juin 2010 16:28
  • Bonjour,

    Il y a un problème à votre solution : le Framework Silverlight n'implémente pas IDataContractSurrogate. Je ne peux donc pas l'appliquer !

    Retour à la case départ :-(

    vendredi 9 juillet 2010 06:44
  • Bonjour,

    Dans ce cas, soit vous implémentez votre propre Sérialiseur (héritant de XmlDataSerializer). Soit vous ajouter une propriété de type string contenant le nombre au format désiré, et c'est celle-ci que vous sérialisez :

    [DataMember(Name = "Value")]
    public string ValueFormaté
    {
      get { return this.Value.ToString("F3"); }
      set { this.Value = Decimal.Parse(value); }
    }
    

    Cordialement


    Gilles TOURREAU - MVP C# - MCTS Windows Forms - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr
    vendredi 9 juillet 2010 15:07
    Modérateur
  • Effectivement ca fonctionne comme ca !

    Merci.

    vendredi 9 juillet 2010 16:39