Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

PropertyChanged sur les indexeurs

[new:16/09/2010]Voici un court sujet pour cette rentrée (et surtout pour me remettre du pavé de 92 pages sur MVVM et le toolkit MVVM Light !). En effet, ce bon Anders Hejlsberg, en repompant certaines bonnes idées de Delphi qu’il avait créé quelques années avant C#, a oublié certaines choses pourtant utiles comme les indexeurs nommés. En C# un seul indexeur par classe et pas de nom ! Choix curieux, étrange, peu judicieux, et au bout de tant de temps jamais modifié. Bref un seul indexeur, et sans nom. Mais lors d’un Binding WPF ou Silverlight (donc Xaml) comment diable prévenir les objets bindés que les valeurs de l’indexeur ont changé (quand ce cas se présente) ?

Beaucoup ne font pas, comme ça l’affaire est réglée, et du coup ils se privent de tout l’intérêt du binding qui est vivant grâce à INotifyPropertyChanged…

Avec MVVM où le binding tient un rôle central, il est encore plus capital d’apporter une solution à ce problème.

Et ce n’est pas une solution mais TROIS que je vais vous proposer. Des approches similaires mais dont les variations sont intéressantes.

Nommer l’indexeur !

Le vrai problème c’est que l’indexeur unique n’a pas de nom… Il suffit donc de lui en donner un ! Riche idée ! Comment pratiquer ? Grâce à l’attribut “IndexerName” avec lequel on peut décorer l’indexeur.

On ne rêve pas, cela ne permettra pas vraiment de donner un nom utilisable à l’indexeur, mais en revanche ce nom sera reconnu dans un PropertyChanged et les bindings liés seront bien mis à jour. C’est peu, mais c’est énorme.

   1: [IndexerName("Data")]
   2: public string this[string key]
   3: {
   4:     get { return xxxx[key]; }
   5:     set
   6:     {
   7:         if (value==xxxx[key]) return;
   8:         xxxx[key] = value;
   9:         RaisePropertyChanged("Data[]"); // Ruse !
  10:     }
  11: }

Nommer l’indexeur est donc un moyen simple pour disposer d’un nom de propriété utilisable dans un ProperyChanged. Attention, dans ce dernier il faut bien passer le nom de la propriété avec les accolades (“Data” devient “Data[]” dans l’exemple) !

Le Nom par défaut

En fait il semble que le compilateur (ou la plateforme plus certainement) donne le nom “Item[]” par défaut à l’indexeur. De fait on peut se passer de l’attribut et utiliser comme nom de propriété “Item[]”.

Je me méfie de cette solution qui oblige à utiliser “en dur” un nom surgit de nulle part et qui peut changer sans prévenir.

La constante de nom d’indexeur

Se passer dans l’attribut est une bonne idée, moins on écrit de code, moins il y en a à maintenir ! Mais franchement, le nom en dur je n’aime pas ça. Heureusement, il existe depuis la version 3.0 une constante, passée un peu inaperçue, qui retourne le nom de l’indexeur, ce qui évite d’utiliser “Item[]”. Cette constante c’est Binding.IndexerName définie dans le namespace System.Windows.Data (de PresentationFramework.dll).

Du coup il est possible d’écrire ceci :

   1: public string this[string key]
   2: {
   3:     get { return _items[key]; }
   4:     set
   5:     {
   6:         _items[key] = value;
   7:  
   8:         if (PropertyChanged != null)
   9:             PropertyChanged(this, new PropertyChangedEventArgs(Binding.IndexerName);
  10:     }
  11: }

 

Mais il y a un “Hic”. Cette constante ne semble pas être supportée par Silverlight pour qui les autres astuces restent donc d’actualité.

Conclusion

Notifier les changements de valeurs d’un indexeur peut rendre d’immenses services (pensez à un système de localisation dont on peut changer la langue à l’exécution, des données issues de capteurs qui varient dans le temps, etc).

Encore fallait-il connaître l’astuce. C’est chose faite !

Stay Tuned !

blog comments powered by Disqus