Partage
  • Partager sur Facebook
  • Partager sur Twitter

[c#] Ajouter un event à un dictionary

Est-ce possible ?

Sujet résolu
    25 juillet 2011 à 10:31:10

    Bonjour,
    J'ai deux classes, une qui contient un dictionnaire, et une qui contient une liste des clés du dictionnaire.
    J'aurais aimé qu'il y ait un listener sur mon dictionnaire pour que quand j'y ajoute un élément, une méthode se déclenche dans l'autre classe pour ajouter la clé de cette élément à la liste et ainsi la maintenir à jour automatiquement (de même pour quand on retire un élément.)
    Est-ce possible de réaliser ça ? Si oui, comment ? (et si non, comment feriez-vous vous pour avoir toujours la liste des clés à jour ?)
    • Partager sur Facebook
    • Partager sur Twitter
      25 juillet 2011 à 11:10:01

      Citation : Antoine

      J'ai deux classes, une qui contient un dictionnaire, et une qui contient une liste des clés du dictionnaire.


      Et un Dictionary<> n'aurait-il pas suffit?

      Citation : Antoine

      J'aurais aimé qu'il y ait un listener sur mon dictionnaire pour que quand j'y ajoute un élément, une méthode se déclenche dans l'autre classe pour ajouter la clé de cette élément à la liste et ainsi la maintenir à jour automatiquement (de même pour quand on retire un élément.)


      Une classe contenant les paramètres des évènements :
      public class DictionaryKeysActionEventArgs : EventArgs
      {
          public Object ModifiedKey
          {
              get;
              private set;
          }
      
          public bool IsAdded
          {
              get;
              private set;
          }
          
          public bool IsRemoved
          {
              get { return !this.IsAdded; }
          }
      
          public DictionaryKeysActionEventArgs(Object key, bool isAdded)
          {
              this.ModifiedKey = key;
              this.IsAdded = isAdded;
          }
      }
      


      Les ajouts sur ta classe de clé de dictionnaires :
      public class DictionaryKeys
      {
          public event EventHandler<DictionaryKeysActionEventArgs> KeysModified;//C'est comme ça qu'on fait un évènement en partant d'un délégué en C#
      
          //[Ton Code]
      
          private void RaiseKeysModified(Object key, bool isAdded)
          {
              if(this.KeysModified != null)
              {
                  this.KeysModified(this, new DictionaryKeysActionEventArgs(key, isAdded));
              }
          }
      
          //Pense à appeler RaiseKeysModified là où c'est nécessaire.
      }
      


      Les ajouts sur ta classe de dictionnaire (j’admets que _keys est ton objet DictionaryKeys instancié dans ta classe de dictionnaire):
      public class Dictionary
      {
          public Dictionary()
          {
              _keys.KeysModified += this.DictionaryKeysModified;
          }
      
          //[Ton Code]
      
          private void DictionaryKeysModified(Object sender, DictionaryKeysActionEventArgs)
          {
              //Faire le code de traitement à l'ajout/suppression d'une clé.
          }
      }
      


      Edit : J'me suis gouré sur le sens...J'ai fais "Quand on modifie les clés, ça change les données" et pas "Quand on change les données, ça change les clés". Le principe restant le même dans un sens comme dans l'autre, je ne modifierais pas ce code.
      • Partager sur Facebook
      • Partager sur Twitter
        25 juillet 2011 à 11:33:58

        Citation : Nisnor

        Citation : Antoine

        J'ai deux classes, une qui contient un dictionnaire, et une qui contient une liste des clés du dictionnaire.


        Et un Dictionary<> n'aurait-il pas suffit?


        En fait, l'idée c'est d'afficher les clés de mon dictionnaire dans une combobox WPF. J'essaye d'être en MVVM (je ne sais pas si tu connais) donc mon dico (dans le model) n'est pas sensé être accessible depuis ma vue. Donc j'ai fait dans mon ViewModel (la classe qui est le DataContext de ma View) une liste qui contient les clés que je veux afficher. Et je veux bien sûr que cette liste soit à jour par rapport au dictionnaire.

        Citation : Nisnor

        Edit : J'me suis gouré sur le sens...J'ai fais "Quand on modifie les clés, ça change les données" et pas "Quand on change les données, ça change les clés". Le principe restant le même dans un sens comme dans l'autre, je ne modifierais pas ce code.



        Non je pense que c'est bon dans ce sens là, en fait il ne s'agit pas de modifier, juste lorsqu'on ajoute (ou supprime) un couple clé/donnée dans le dictionnaire, je veux ajouter (ou supprimer) la clé correspondante dans la liste.
        • Partager sur Facebook
        • Partager sur Twitter
          25 juillet 2011 à 12:26:53

          As-tu vraiment besoin d'un dictionnaire dans ton ViewModel ? Une collection non indexée (comme une liste) ne suffirait-elle pas ?

          Si tu n'as pas besoin d'une collection indexée, tu peux envisager d'exposer une ObservableCollection<> qui contient un ensemble d'objets métier. Tu bindes ensuite ta combobox à cette collection en utilisant sa propriété DisplayMemberPath pour désigner la propriété qui servira à l'affichage ^^ Une ObservableCollection étant observable par nature, tout changement sur son contenu sera répercuté automatiquement sur la combobox.

          Tu peux aussi envisager d'utiliser une collection PagedCollectionView, qui va un poil plus loin en gérant notamment la sélection d'un objet au sein de la collection. Elle expose un événement SelectionChanged auquel tu peux donc t'abonner dans ton ViewModel pour connaitre l'objet sélectionné dans l'interface :)
          • Partager sur Facebook
          • Partager sur Twitter
            25 juillet 2011 à 12:28:03

            Citation : Antoine

            En fait, l'idée c'est d'afficher les clés de mon dictionnaire dans une combobox WPF. J'essaye d'être en MVVM (je ne sais pas si tu connais) donc mon dico (dans le model) n'est pas sensé être accessible depuis ma vue. Donc j'ai fait dans mon ViewModel (la classe qui est le DataContext de ma View) une liste qui contient les clés que je veux afficher. Et je veux bien sûr que cette liste soit à jour par rapport au dictionnaire.


            A moins que ton dictionnaire contienne des milliers d'entrées, peut-être faire ceci serait-il plus simple?

            public Dictionary<String, Object> MesObjets
            {
                get;
                set;
            }
            


            (Ici, j'admets que le DataContext est hérité d'un contrôle parent et que le DataContext du contrôle parent est bindé sur une instance de l'objet contenant la propriété MesObjets)
            <ComboBox x:Name="Test" ItemSource="{Binding MesObjets.Keys, Mode=OneWay}"/>
            


            et à chaque modification du dictionnaire, tu spécifie que la propriété MesObjets à changé (pour du MVVM, soit tu utilises MVVMlight ou un framework équivalent, auquel cas il y a moyen de faire cette notification...Soit ta classe hérite de DependencyObject, auquel cas la notification est effectuée à l'appel de SetValue...Soit ta classe implémente INotifyPropertyChanged, auquel cas il te faut lever l'évènement PropertyChanged quand c'est nécessaire).
            Ceci ne pourrait marcher que si ton dictionnaire ne contient pas plus de quelques centaines d'objets => Notifier du changement d'une propriété de type liste/collection va forcer le proxy de binding sur les liste à se rafraichir, c'est a dire énumérer à nouveau la collection et régénérer les éléments de la vue correspondante.
            • Partager sur Facebook
            • Partager sur Twitter
              25 juillet 2011 à 13:55:17

              Citation : Orwell

              As-tu vraiment besoin d'un dictionnaire dans ton ViewModel ? Une collection non indexée (comme une liste) ne suffirait-elle pas ?

              Si tu n'as pas besoin d'une collection indexée, tu peux envisager d'exposer une ObservableCollection<> qui contient un ensemble d'objets métier. Tu bindes ensuite ta combobox à cette collection en utilisant sa propriété DisplayMemberPath pour désigner la propriété qui servira à l'affichage ^^ Une ObservableCollection étant observable par nature, tout changement sur son contenu sera répercuté automatiquement sur la combobox.

              Tu peux aussi envisager d'utiliser une collection PagedCollectionView, qui va un poil plus loin en gérant notamment la sélection d'un objet au sein de la collection. Elle expose un événement SelectionChanged auquel tu peux donc t'abonner dans ton ViewModel pour connaitre l'objet sélectionné dans l'interface :)



              Justement ^^
              En fait, mon dictionnaire n'est pas dans mon ViewModel mais dans mon Model. Dans mon ViewModel, j'ai donc une ObservableCollection<String> qui contient mes clés... Et je l'ai bindé à ma combobox, là-dessus aucun souci. Mon problème est que le contenu de l'observableCollection doit toujours être le même que dico.Keys.

              Citation

              Ceci ne pourrait marcher que si ton dictionnaire ne contient pas plus de quelques centaines d'objets => Notifier du changement d'une propriété de type liste/collection va forcer le proxy de binding sur les liste à se rafraichir, c'est a dire énumérer à nouveau la collection et régénérer les éléments de la vue correspondante.


              Pas de problème pour ça, le dictionnaire ne dépassera sûrement pas la vingtaine d'éléments.

              Citation

              et à chaque modification du dictionnaire, tu spécifie que la propriété MesObjets à changé (pour du MVVM, soit tu utilises MVVMlight ou un framework équivalent, auquel cas il y a moyen de faire cette notification...Soit ta classe hérite de DependencyObject, auquel cas la notification est effectuée à l'appel de SetValue...Soit ta classe implémente INotifyPropertyChanged, auquel cas il te faut lever l'évènement PropertyChanged quand c'est nécessaire).


              En fait, je débarque en .NET, et je voulais retrouver mon MVC dont j'avais l'habitude, j'ai compris que l'équivalent était MVVM donc j'essaye de suivre ça, mais aucune idée de ce qu'est light ou autre.
              Pour DependencyObject et INotifiyPropertyChanged, je les ai croisé, j'en ai plein mon programme, mais je ne dirais pas pour autant que je sais vraiment les utiliser... Il s'agirait de faire hériter le dictionnaire lui même ou bien la classe qui le contient ?
              • Partager sur Facebook
              • Partager sur Twitter
                25 juillet 2011 à 14:17:29

                Ah oui je comprends mieux :o

                Apparemment tu fais face à un problème classique: .Net ne propose pas par défaut d'implémentation pour un "dictionnaire observable". Cependant plusieurs implémentations "artisanales" sont disponibles sur le net. Voici par exemple un article qui parle (entre autres) de ce sujet. ;)
                • Partager sur Facebook
                • Partager sur Twitter
                  25 juillet 2011 à 15:01:39

                  Citation : Antoine

                  En fait, je débarque en .NET, et je voulais retrouver mon MVC dont j'avais l'habitude, j'ai compris que l'équivalent était MVVM donc j'essaye de suivre ça, mais aucune idée de ce qu'est light ou autre.


                  MVVMLight, Prism et autre (p'tet bientôt le mien? ^^ ) sont des frameworks qui te "simplifient" la création d'application en utilisant le modèle MVVM.

                  Citation : Antoine

                  Pour DependencyObject et INotifiyPropertyChanged, je les ai croisé, j'en ai plein mon programme, mais je ne dirais pas pour autant que je sais vraiment les utiliser... Il s'agirait de faire hériter le dictionnaire lui même ou bien la classe qui le contient ?


                  DependencyObject, c'est pour le cas où tu feras des petites classes à mis-chemin entre le métier et la vue, dans le cadre de la réalisation d'un ensemble de contrôles. Si tu n'es pas dans ce cas là, tu n'en auras normalement pas besoin.
                  INotifyPropertyChanged, INotifyCollectionChanged, sans deux interfaces clés dans la réalisation d'une application MVVM sans Framework (pour fonctionner, les frameworks implémentent l'une ou l'autre d'ailleurs). INotifyPropertyChanged permet, lorsqu'elle est implémentée, de notifier le changement d'une propriété sur un objet donné au moteur de binding. INotifyCollectionChanged permet quand à elle de notifier du changement de la structure d'un objet énumérable. L'un comme l'autre sont à implémenter sur la classe qui doit effectuer ces notifications.

                  Si l'implémentation de l'une ou l'autre des interfaces est classique dans la couche ViewModel, il est n'est pas rare de voir des POCOs de la couche Model les implémenter aussi.

                  public class MonPoco : INotifyPropertyChanged
                  {
                      private String _pA, _pB;
                      private List<String> _pC;
                      public event PropertyChanged;
                      public String PropA
                      {
                          get { return _pA; }
                          set 
                          {
                              _pA= value;
                              this.Notify("PropA");
                          }
                      }
                      public String PropB
                      {
                          get { return _pB; }
                          set 
                          {
                              _pB= value;
                              this.Notify("PropB");
                          }
                      }
                      public List<String> PropC
                      {
                          get { return _pC; }
                          set 
                          {
                              _pC= value;
                              this.Notify("PropC");
                          }
                      }
                      private void Notify(String pName)
                      {
                          if(PropertyChanged != null)
                          {
                              this.PropertyChanged(this, new PropertyChangedEventArgs(pName));
                          }
                      }
                  }
                  
                  public class TestViewModel : INotifyPropertyChanged
                  {
                      private MonPoco _monPoco;
                      public event PropertyChanged;
                      public MonPoco MaPropriete
                      {
                          get { return _monPoco; }
                          set 
                          {
                              _monPoco = value;
                              this.Notify("MaPropriete");
                          }
                      }
                  
                      private void Notify(String pName)
                      {
                          if(PropertyChanged != null)
                          {
                              this.PropertyChanged(this, new PropertyChangedEventArgs(pName));
                          }
                      }
                  }
                  


                  Le mieux, c'est que tu implémente l'une ou l'autre des interfaces partout, là où tu en as besoin; sachant bien sur que si ton POCO contient un truc énumérable et que tu notifie du changement de ce truc, il sera entièrement énuméré à nouveau.

                  Ou sinon, teste le lien que t'as proposé Orwell, peut-être ce dictionnaire observable pourra-t-il te suffire?
                  • Partager sur Facebook
                  • Partager sur Twitter
                    25 juillet 2011 à 15:16:36

                    D'accord, j'ai compris comment cela s'utilise. Quelques questions sur ton code quand même:
                    (déjà, qu'est ce que tu appelles un poco ? :euh: )
                    Ensuite :
                    if(PropertyChanged != null)
                            {
                                this.PropertyChanged(this, new PropertyChangedEventArgs(pName));
                            }
                    

                    A quel moment PropertyChanged pourrait être null ? Juste quand on a pas encore eu d'événement en fait? Quand on fait un PropertyChanged(this, new ...) on en construit une nouvelle non ? Elle est lancée à ce moment là. On connait donc le nom de la propriété qui change, mais on ne connait pas la valeur ?

                    Pour revenir à mon problème, si j'ajoute un élément à mon dictionnaire, je vais savoir que le dictionnaire a changé, mais je ne connaîtrais pas l'élément qui lui a été ajouté, donc obligé de remettre toute la liste à jour...

                    (donc au final, la solution la plus simple, c'est de créer une méthode publique dans mon ViewModel, et d'écrire dans mon modèle une méthode AddElemDico qui ajoute l'élément et appelle la méthode du ViewModel en lui donnant la clé à rajouter, de faire de même pour la suppression et de toujours passer par ces méthodes là pour modifier mon dictionnaire.)
                    • Partager sur Facebook
                    • Partager sur Twitter
                      25 juillet 2011 à 15:40:10

                      Citation : Nisnor

                      public String PropA
                          {
                              get { return _pA; }
                              set 
                              {
                                  _pA= value;
                                  this.Notify("PropA");
                              }
                          }
                      



                      Perso je préfère écrire
                      public String PropA
                          {
                              get { return _pA; }
                              set 
                              {
                                  if(_pA != value)
                                  {
                                      _pA = value;
                                      this.Notify("PropA");
                                  }
                              }
                          }
                      

                      C'est plus long, mais au moins l'événement n'est lancé qu'en cas de réel changement (ce qui est utile pour traquer les changements sur un objet en cours d'édition par exemple).

                      Je me suis créé un snippet "propn" que j'utilise énormément:

                      <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
                        <CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
                          <Header>
                            <Title>Change Notifying Property Snippet</Title>
                            <Author>Orwell</Author>
                            <Shortcut>propn</Shortcut>
                            <SnippetTypes>
                              <SnippetType>Expansion</SnippetType>
                            </SnippetTypes>
                          </Header>
                          <Snippet>
                            <Declarations>
                              <Object>
                                <ID>FieldType</ID>
                                <ToolTip>The type of the property.</ToolTip>
                                <Default>string</Default>
                              </Object>
                              <Object>
                                <ID>PropertyName</ID>
                                <ToolTip>The name of the property.</ToolTip>
                                <Default>MyProperty</Default>
                              </Object>
                            </Declarations>
                            <Code Language="CSharp"><![CDATA[private $FieldType$ _$PropertyName$;
                              public $FieldType$ $PropertyName$
                              {
                                  get { return _$PropertyName$; }
                                  set { if(_$PropertyName$ != value) { _$PropertyName$ = value; RaisePropertyChanged("$PropertyName$"); } }
                              }
                              ]]>
                            </Code>
                          </Snippet>
                        </CodeSnippet>
                      </CodeSnippets>
                      

                      (le snippet utilise une méthode "RaisePropertyChanged" qui correspond au "Notify" de Nisnor).

                      Citation

                      (déjà, qu'est ce que tu appelles un poco ? :euh: )


                      POCO = "Plain Old CLR Object", en gros "Bon vieux objet CLR", càd une simple classe avec des propriétés qui n'a pas besoin d'hériter d'une classe compliquée pour faire ce qu'on veut qu'elle fasse: représenter des données.

                      Citation

                      A quel moment PropertyChanged pourrait être null ? Juste quand on a pas encore eu d'événement en fait?

                      PropertyChanged est null si aucun gestionnaire d'événement n'a été associé à cet événement. Note que l'abonnement à l'événement PropertyChanged est fait automatiquement par l'interface lorsque tu bindes un de ses contrôles à un objet qui implémente INotifyPropertyChanged. L'événement ne communique effectivement que le nom de la propriété modifiée; la valeur est à consulter sur l'objet lui-même.

                      La classe DependencyObject et les Dependency Properties ne sont utiles que si tu veux pouvoir désigner ta propriété comme cible d'un Binding (ou d'une animation). En pratique ça sert essentiellement pour la définition de nouveaux contrôles, car les ViewModels servent essentiellement de sources pour les Bindings (et non de cibles).
                      • Partager sur Facebook
                      • Partager sur Twitter
                        25 juillet 2011 à 15:53:05

                        Citation : Antoine

                        (donc au final, la solution la plus simple, c'est de créer une méthode publique dans mon ViewModel, et d'écrire dans mon modèle une méthode AddElemDico qui ajoute l'élément et appelle la méthode du ViewModel en lui donnant la clé à rajouter, de faire de même pour la suppression et de toujours passer par ces méthodes là pour modifier mon dictionnaire.)


                        En gros, oui ^^ .
                        Après, es-tu sûr qu'il n'est pas plus pratique de mettre cette méthode AddElemDico sur le Model plus que sur le ViewModel, sachant que les deux (suivant mon exemple un peu plus haut) ont la possibilité de notifier d'un changement de propriété?
                        • Partager sur Facebook
                        • Partager sur Twitter
                          25 juillet 2011 à 15:59:21

                          Citation : Nisnor

                          Après, es-tu sûr qu'il n'est pas plus pratique de mettre cette méthode AddElemDico sur le Model plus que sur le ViewModel ?


                          Oui, c'est ce que j'ai fait. Celle dans ViewModel sert à ajouter l'élément à la liste, mais si la liste est publique, la méthode n'est pas nécessaire.

                          @Orwell : Je ne connais pas les snippets...
                          • Partager sur Facebook
                          • Partager sur Twitter

                          [c#] Ajouter un event à un dictionary

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown