IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

DatePicker et validation : ça n'en fait qu'à sa tête


Sujet :

Windows Presentation Foundation

  1. #1
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut DatePicker et validation : ça n'en fait qu'à sa tête
    Bonjour à tous et à toutes...

    J'ai plusieurs DatePicker dans une fenêtre.
    Code xaml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
                                        <DatePicker Grid.Column="1"
                                                    Grid.Row="5"
                                                    x:Name="guiDateFin"
                                                    HorizontalAlignment="Left"
                                                    VerticalAlignment="Top"
                                                    ToolTip="Date de fin&#x0a;Ce champ est obligatoire"
                                                    IsEnabled="{Binding Mode=OneWay, TargetNullValue=false}"
                                                    Style="{StaticResource UserDatePickerCheckedTemplate}"
                                                    Text="{Binding DateFin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"/>
    J'en ai aussi un autre qui utilise SelectedDate = au lieu de Text =

    Voilà, le problème :
    Au démarrage la date est vide : le contrôle de contenu fonctionne et indique : La date de fin est obligatoire !
    Jusque là ça va, c'est ma validation du contenu qui est appelé.

    J'ouvre le picker, je choisi la date : 31/01/2013, je ne passe dans mon code de validation des données et le contrôle indique : Données non valides détectées lors du décodage.
    J'écris directement : 2013/01/30 donc en format anglais (je fais exprès de mettre 30 au lieu de 31 pour bien montrer que le DatePicker converti correctement la valeur) et là plus d'erreur (d'ailleur mon code de validation de données est appelé avec la bonne date) ! Lorsque je quitte le contrôle ceci-ci prend la bonne valeur et l'affiche : 30/01/2013 mais le code de validation (qui n'est pas appelé) m'indique à nouveau Données non valides détectées lors du décodage.

    Comme si l'affichage se faisait en français mais le décodage interne de je ne sais où était en anglais. Ça pue le bug...

    J'ai ajouté ce code là dans la MainWindow :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
            private class State
            {
                public CultureInfo Result { get; set; }
            }
     
            public CultureInfo GetCurrentCulture()
            {
                Thread.CurrentThread.CurrentCulture.ClearCachedData();
                var thread = new Thread(s => ((State)s).Result = Thread.CurrentThread.CurrentCulture);
                var state = new State();
                thread.Start(state);
                thread.Join();
                var culture = state.Result;
                return culture;
            }
            /// <summary>
            /// Fenêtre principale de l'application.
            /// 
            /// Contient tous les contrôle de l'application.
            /// </summary>
            public MainWindow()
            {
                Thread.CurrentThread.CurrentCulture = GetCurrentCulture();
                Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
    Puis dans le constructeur de ma vue :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
                this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name);
                Thread.CurrentThread.CurrentCulture = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
                Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern = "dd-MMM-yyyy";
    Mais tout celà n'a aucun effet !
    Seule chose : à un moment j'ai utilisé un convertiseur et la ligne this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name); permet d'avoir le System.Globalization.CultureInfo de la méthode Convert du convertisseur en français (sans cette ligne, c'est en anglais).

    Quelqu'un à une idée sur l'utilisation de ce contrôle qui m'a l'air bien buggé ?
    Merci.

  2. #2
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Quelqu'un utilise ce contrôle avec un binding de type 'DateTime ?'
    Parce que là çà fait des jours que je cherche avec des choses plus ou moins compliquées et rien nr fonctionne !

    Ou alors du code pour réécrire ce contrôle !
    Je vais péter un cable.

    Merci à tous.

  3. #3
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Bon, sujet résolu...

    Qu'est-ce qu'il s'est passé ?
    Architectures MVVM avec MVVMLight séparés en 3 couches : DTO / DAL / Application
    J'ai des classes DTO pour l'accès à la base de données. Ces classes sont utilisées tel quel dans les classes Vue Model / Vue.
    Or, normalement, pour bien faire, il faut que chaque classe de la couche DTO ai sont pendant dans le VueModel : obligé d'encapsuler les classes DTO et de tout réécrire deux fois !

    Afin de simplifier le travail, j'utilise des classes dynamiques (dynamic).
    Pour ce faire j'utilisais une classe DynamicModel de la librairie DynamicMVVM.
    C'est cette classe, qui lorsque l'on a un Nullable objet à setter me lance ces exceptions !
    J'ai supprimé la librairie, et supprimé la classe de base.
    J'ai tout recodé. Les classes DTO dérivent toutes de EntityBase
    Voici le code, si ça peut servir :
    Code EntityBase : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Reflection;
    using System.Text;
     
    namespace xxxDTO
    {
        public class EntityBase : IDataErrorInfo
        {
            /// <summary>
            /// Non utilisé.
            /// </summary>
            string IDataErrorInfo.Error
            {
                get { throw new NotImplementedException(); }
            }
     
            /// <summary>
            /// Test la colonne donné par son nom.
            /// 
            /// Vérifie en utilisant les annotations.
            /// </summary>
            /// <param name="_strColumnName">Nom de la colonne</param>
            /// <returns>Une erreur si le champ n'est pas valide, null sinon.</returns>
            string IDataErrorInfo.this[string _strColumnName]
            {
                get
                {
                    PropertyInfo prop = GetType().GetProperty(_strColumnName);
                    var validationMap = prop.GetCustomAttributes(typeof(ValidationAttribute), true).Cast<ValidationAttribute>();
                    foreach(var v in validationMap)
                    {
                        try
                        {
                            v.Validate(prop.GetValue(this, null), _strColumnName);
                        }
                        catch (Exception ex)
                        {
                            return ex.Message;
                        }
                    }
     
                    return null; // Pas d'erreur
                }
            }
        }
    }
    Code WrapperViewModel : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    using xxxxDTO;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Dynamic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
     
    namespace xxx.ViewModel.ViewModelWrapper
    {
        /// <summary>
        /// Classe de base permettant de wrapper les classes du DTO afin que la modification des champ de ceux-ci provoque
        /// une notification de type property change.
        /// 
        /// De plus, les vérifications de type métier sont appliquées par la classe dérivant de celle-ci.
        /// </summary>
        /// <typeparam name="T">Class de type DTO</typeparam>
        public class WrapperViewModel<T> : DynamicObject
                                         , INotifyPropertyChanged
                                         , IDataErrorInfo where T : EntityBase
        {
            /// <summary>
            /// Récupérer l'instance du modèle.
            /// </summary>
            public T Model                                      { get; set; }
     
            /// <summary>
            /// Constructeur par défaut.
            /// </summary>
            public WrapperViewModel()
                : base()
            {
                Model = null;
            }
     
            /// <summary>
            /// Constructeur à utiliser.
            /// 
            /// Permet de wrapper une classe d'EntityBase.
            /// </summary>
            /// <param name="_value">Instance de classe dérivant d'EntityBase</param>
            public WrapperViewModel(T _value)
            {
                Model = _value;
            }
     
            private bool TryGetMember(GetMemberBinder _binder, out object _objResult,object _objCible,bool _bLogError)
            {
                _objResult = null;
                bool bRet = false;
     
                string strPropertyName = _binder.Name;
                PropertyInfo propertyInfo = _objCible.GetType().GetProperty(strPropertyName,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
     
                if (propertyInfo!=null && propertyInfo.CanRead)
                {
                    _objResult = propertyInfo.GetValue(_objCible, null);
                    bRet = true;
                }
                else if (_bLogError)
                {
                    String strError = String.Format("WrapperViewModel<{0}>.TryGetMember : impossible de trouver la propriété '{1}' !",typeof(T).Name,strPropertyName);
                    WPFExtToolkit.Logger.Log.Error(strError);
                    Debug.Assert(false, strError);
                }
     
                return bRet;
            }
     
            /// <summary>
            /// Getter un membre.
            /// 
            /// Détecte au runtime  la présence ou pas de ce membre puis récupère sa valeur.
            /// </summary>
            /// <param name="_binder">Paramètres du membre à récupérer</param>
            /// <param name="_objResult">Résultat à mettre à jour</param>
            /// <returns>true si le membre existe, false sinon.</returns>
            public override bool TryGetMember(GetMemberBinder _binder, out object _objResult)
            {
                return    TryGetMember(_binder, out _objResult,this,false)
                       || TryGetMember(_binder, out _objResult,Model,true);
            }
     
            private bool TrySetMember(SetMemberBinder _binder, object _objValue,object _objCible,bool _bLogError)
            {
                bool bRet = false;
     
                string strPropertyName = _binder.Name;
                PropertyInfo property = _objCible.GetType().GetProperty(strPropertyName,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
     
                if (property!=null && property.CanWrite)
                {
                    try
                    {
                        property.SetValue(_objCible, _objValue, null);
                        NotifyPropertyChanged(strPropertyName);
                        bRet = true;
                    }
                    catch(Exception _exception)
                    {
                        String strError = String.Format("WrapperViewModel<{0}>.TrySetMember : Exception lors du set de la propriété '{1}' :\n{2} !",typeof(T).Name,strPropertyName,_exception.Message);
                        WPFExtToolkit.Logger.Log.Error(strError);
                        Debug.Assert(false, strError);
                    }
                }
                else if (_bLogError)
                {
                    String strError = String.Format("WrapperViewModel<{0}>.TrySetMember : impossible de trouver la propriété '{1}' !",typeof(T).Name,strPropertyName);
                    WPFExtToolkit.Logger.Log.Error(strError);
                    Debug.Assert(false, strError);
                }
     
                return bRet;
            }
     
            /// <summary>
            /// Setter un membre.
            /// 
            /// Détecte au runtime  la présence ou pas de ce membre puis set sa valeur.
            /// </summary>
            /// <param name="_binder">Paramètres du membre à setter</param>
            /// <param name="_objValue">Valeur à setter pour ce membre</param>
            /// <returns>true si le membre existe, false sinon.</returns>
            public override bool TrySetMember(SetMemberBinder _binder, object _objValue)
            {
                return    TrySetMember(_binder, _objValue,this,false)
                       || TrySetMember(_binder, _objValue,Model,true);
            }
     
            //------------------------------------------------------------------------------------------------------
            //------------------------------------------------------------------------------------------------------
            //
            //                                    Implémentyation INotifyPropertyChanged
            //
            //------------------------------------------------------------------------------------------------------
            //------------------------------------------------------------------------------------------------------
            #region INotifyPropertyChanged implementation
            /// <summary>
            /// Evénement property changed.
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;
     
            /// <summary>
            /// Lance l'évènement de changement de propriété.
            /// </summary>
            /// <param name="_strPropertyName">Nom de la propriété.</param>
            public void NotifyPropertyChanged(string _strPropertyName)
            {
                if (PropertyChanged != null)
                {
                    VerifyPropertyName(_strPropertyName);
                    PropertyChanged(this, new PropertyChangedEventArgs(_strPropertyName));
                }
            }
     
            /// <summary>
            /// Warns the developer if this object does not have
            /// a public property with the specified name. This 
            /// method does not exist in a Release build.
            /// </summary>
            [Conditional("DEBUG")]
            [DebuggerStepThrough]
            public void VerifyPropertyName(String _strPropertyName)
            {
                // Vérifier que la propriété existe bien en tant que membre privé ou publique
                PropertyInfo property = GetType().GetProperty(_strPropertyName,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                if (property == null)
                {
                    property = Model.GetType().GetProperty(_strPropertyName,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                }
     
                if (property == null)
                {
                    String strError = String.Format("WrapperViewModel<{0}>.NotifyPropertyChanged : Impossible de trouver la propriété '{1}'",typeof(T).Name,_strPropertyName);
                    WPFExtToolkit.Logger.Log.Error(strError);
                    Debug.Assert(false, strError);
                }
            }
            #endregion
     
            //------------------------------------------------------------------------------------------------------
            //------------------------------------------------------------------------------------------------------
            //
            //                                             Gestion des Ereurs
            //
            //------------------------------------------------------------------------------------------------------
            //------------------------------------------------------------------------------------------------------
            #region Gestion des erreurs
     
            /// <summary>
            /// Non utilisé.
            /// </summary>
            string IDataErrorInfo.Error
            {
                get { throw new NotImplementedException(); }
            }
     
            /// <summary>
            /// Test la colonne donné par son nom.
            /// 
            /// Vérifie en utilisant les annotations.
            /// </summary>
            /// <param name="_strColumnName">Nom de la colonne</param>
            /// <returns>Une erreur si le champ n'est pas valide, null sinon.</returns>
            public string this[string _strColumnName]
            {
                get
                {
                    try
                    {   // Test en fonction des annotation de la classe wrappée
                        if (Model is IDataErrorInfo)
                        {
                            return ((IDataErrorInfo) Model)[_strColumnName];
                        }
                        Validator.ValidateProperty(Model.GetType().GetProperty(_strColumnName).GetValue(Model, null), new ValidationContext(Model, null, null)
                        {
                            MemberName = _strColumnName
                        });
                    }
                    catch(Exception ex)
                    {
                        return ex.Message;
                    }
     
                    return null; // Pas d'erreur
                }
            }
            #endregion
        }
    }

    Ensuite, je n'ai plus qu'à dériver mes classes de cette classe de base et d'implémenter les propriétés supplémentaires et la gestion des erreurs (public new string this[string _strColumnName]) :
    Code exemple d'utilisation de WrapperViewModel : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        public class UtilisateurViewModel : WrapperViewModel<EntityUtilisateur>
                                          , IDataErrorInfo
        {
        }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [UI] Datepicker et validation au blur
    Par almoha dans le forum jQuery
    Réponses: 4
    Dernier message: 16/11/2012, 22h14
  2. Gridview.DataKeyNames n'en fait qu'à sa tête
    Par davidso dans le forum ASP.NET
    Réponses: 1
    Dernier message: 06/03/2007, 09h13
  3. le menu qui n'en fait qu'à sa tête
    Par Invité dans le forum Mise en page CSS
    Réponses: 5
    Dernier message: 09/11/2006, 11h32
  4. [struts-validator] la validation de ne se fait pas
    Par jeb001 dans le forum Struts 1
    Réponses: 84
    Dernier message: 21/07/2006, 15h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo