Silverlight 5 Preview Features

1 commentaire

    Silverlight 5 sera là pour le second semestre 2011, c'est en substance le message que Scott Guthrie a diffusé le 2 décembre lors de l'événement FireStarter. Vous pouvez retrouver la Keynote ici.

    En attendant la version finale, une première version sera disponible courant du premier trimestre 2011. Le salon du MIX se tiendra du 12 au 14 avril 2011 à Las Vegas, il est très probable que la version Bêta sera disponible dès cette date si n'est plus tôt. Scott a tout de même pris le temps de communiquer quelques infos à propos de cette version. Le passage de la version 4 à 5 pourrait être comparée à celui qu'il y a eu entre la version 1 et 2. L'un des indices qui nous mène à cette réflexion concerne la durée des développement qui vont au moins s'étaler sur un an complet (d'après Scott puis qu'elle est prévue sur le second semestre) alors que le passage de la 3 vers la 4 n'avait duré que 10 mois environ. Scott Guthrie rassure tous les acteurs et la communauté Silverlight avec une énorme liste de fonctionnalité. Quelques sujets sont encore vaguement présenté mais on peut facilement imaginer leur impact et leur implémentation. En voici quelques unes de taille :

    -Nouvelle API 3D et moteur 3D

    -Impression vectorielle avec API riche

    -Un système de liaison largement amélioré avec notamment :

    • les customs markup extensions
    • la possibilité de placer des breaks points dans le XAML
    • Ancestor RelativeSource

    -Grosses améliorations à prévoir en termes de mise en page et d'affichage du texte.

    -Accélération GPU bien mieux supportée et intégrée

    -Le décodage hardware des vidéos HD H264, cela signifie que le GPU pourra prendre en charge lui même la décompression vidéo et soulagé le CPU.

    -La possibilité de créer des liaisons de données dans les Setter des styles. Cette fonctionnalité là est réellement très pratique lorsque l'on utilise des propriétés attachées notamment.

    -Les modèles de données (DataTemplate) peuvent désormais être implicite ça s'est plutôt une bonne nouvelle et encore une fois on sent bien que Microsoft essaie au maximum de rapprocher le flux de production Silverlight et WPF.

    -Les tests unitaires pour les IHM seront dorénavant supportés et Microsoft fournira un outil à cet fin :)

    Il y a encore pas mal d'autres fonctionnalités et certaines d'entre elles apparaîtront peut-être au fil du temps et des annonces alors rester à l'écoute car Microsoft investit massivement dans Silverlight 5 et vient de nous le prouver.

    Bookmark and Share

    Design time Custom Attributes

    Aucun commentaire

      Il arrive souvent lorsque vous concevez des composants d'avoir besoin d'une représentation au design time, dans Blend, très spécifique. Sans aller jusqu'à développer des design time dlls, les attributs existants peuvent être d'une grande aide (vous pourrez trouver des explications concernant les designs dll par Justin Angel ici). Du coup, je liste ci-dessous une suite d'attributs que j'utilise couramment lors de mes projets, ceux-ci sont accessibles via les espaces de nom System.ComponentModel et System.Windows.Interactivity :

      1. Définir une description au survol d'une propriété ou d'un contrôle dans le panneau Properties
        [Description("ma description")]
      2. Définir une valeur par défaut au Design Time
        [DefaultValue()]
      3. Définir dans quel sous catégorie du panneau propriété la propriété se trouve
        [Category("Common Properties")]
      4. Définir la propriété dans les options avancées d'une sous catégorie ou ne pas l'afficher. C'est cette sous-partie que vous pouvez déplier.
        [EditorBrowsable(EditorBrowsableState.Advanced)]
      5. Définir si une propriété supporte la liaison de donnée et si oui dans quelles directions.
        [Bindable(BindableSupport.Yes,BindingDirection.TwoWay)]
      6. Définir un type de présentation spécifique. Très utile lorsque la propriété est affectée de certain type d'objets, par exemple cibler un état visuel.
        [CustomPropertyValueEditorAttribute(CustomPropertyValueEditor.StateName)]
      7. Lorsqu'une classe définit des objets customizables, faciliter l'accès aux templates via les menus d'accès aux styles additionnels.
        [StyleTypedProperty(Property = "ToolTipStyle", StyleTargetType = typeof(ToolTip))]
      8. Définir une partie de contrôle
        [TemplatePart(Name="maPartieLogique", Type=typeof(FrameworkElement))]
      9. Définir un état visuel
        [TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
      10. Définir une propriété comme propriété de type Content alternative, celui-ci est réellement très pratique, C'est ce qu'utilise en interne les controles de type TabItem ou les charts de Silverlight ToolKit.
        [AlternateContentProperty()]
      Bookmark and Share

      Fxg Import versus DrawingBrush in Expression Blend SP1

      Aucun commentaire

        Depuis le service pack1 de Blend, ce dernier propose le nouveau type d'importation FXG qui jusque là était purement réservé aux produits de la gamme Adobe. Clairement Microsoft n'a jamais réellement eu pour vocation de créer des logiciels de conception graphique pour le print ou le web tels qu'Expression Design. C'est un coup de génie que l'on voit venir depuis des mois, la notion de DrawingBrush n'a pas été portée sur Silverlight 4 et aucune annonce ne va dans ce sens pour la version 5. Ce n'est plus rédhibitoire aujourd'hui avec l'import direct de librairies FXG, de cette manière Microsoft clarifie son positionnement aussi bien d'un point de vue métier (dans le flux de production) que d'un point de vue outil. Cette fonctionnalité n'est pas forcément connue des développeurs car beaucoup de projets WPF se sont passés des DrawingBrush exportés sous forme de dictionnaires de ressources depuis Expression Design. Cela est certainement dommageable mais on peut contourner cette manière de gérer les différents acteurs d'une production avec plus ou moins de réussite.

        Concrètement grâce aux DrawingBrush sous WPF, nous avons la capacité d'avoir des remplissages de formes vectorielles pour n'importe quel contrôle. Les DrawingBrush sont en général stockés dans des dictionnaires de ressource en bon XAML. L'avantage est qu'un graphiste peut à tout moment ré-exporter ces dictionnaires depuis Expression Design et écraser le précédent. Le développeur n'a de son côté qu'à re-compiler l'application et celle-ci est mise à jour en prenant en compte le nouveau dictionnaire. Chacun est bien a sa place dans ce type de flux de production.

        L'import de fichiers FXG, sans être aussi élégante propose le même type de flux de production, voici l'écran d'importation d'un fichier FXG.

        Pour ma part, Flasher depuis pas mal de temps, j'ai conçu chaque élément et exporté le FXG via Flash. Une fois le fichier importé, le premier réflexe consiste à essayer de récupérer les éléments graphiques du FXG au sein de l'interface Blend. Ceux-ci ne sont pas traités comme ressources graphiques mais bien comme contrôles, du coup vous les trouverez dans le panneau assets comme montré ci-dessous.

        La mise à jour fonctionne exactement de la même manière que les DrawingBrush puisque qu'il suffit de modifier le fichier exporté pour que Blend le détecte et vous alerte d'une mise à jour. Attention toutefois si vous ajoutez ou supprimez des éléments, j'ai remarqué qu'il fallait souvent supprimer le FXG du projet puis le ré-importer dans ce cas. Cette importation est moins élégante à ce jour qu'un export sous forme de dictionnaires de ressource mais sait-on jamais peut-être pouvons nous imaginer une amélioration sous forme d'option d'importation dans l'avenir...

        Bookmark and Share

        DonutLoader [Custom Control]

        Aucun commentaire


          Pour les besoins d'une production, nous avons eu besoin d'un visuel d'attente (Torus ou Donut Loader) très proche de ce qui se fait déjà déjà sous Windows. Pour cela, j'ai étendu Control puis j'ai ajouté quelques Dependency properties :

          #region Dps
           
          /// 
          /// Set the speed of animation based on
          /// 
          [Category("Common Properties")]
          [Description("Allow to define animation's duration")]
          public TimeSpan Duration
          {
              get { return (TimeSpan)GetValue(DurationProperty); }
              set { SetValue(DurationProperty, value); }
          }
           
          // Using a DependencyProperty as the backing store for SpeedRatio.  This enables animation, styling, binding, etc...
          public static readonly DependencyProperty DurationProperty =
              DependencyProperty.Register("Duration", typeof(TimeSpan), typeof(DonutLoader), new PropertyMetadata(TimeSpan.FromSeconds(1), new PropertyChangedCallback(OnDurationChanged)));
           
          private static void OnDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              var donutLoader = d as DonutLoader;
           
              if (d != null)
              {
                  ClockState cs = donutLoader.boucle.GetCurrentState()
           
                  donutLoader.boucle.Stop();
           
                  donutLoader.daBoucle.Duration = (TimeSpan)e.NewValue;
           
                  if (cs == ClockState.Active)
                  {
                      donutLoader.boucle.Begin();
                  }
              }
          }
           
          /// 
          /// Percent of progression
          /// 
          [Category("Common Properties")]
          [Description("Current progression value")]
          public double Percent
          {
              get { return (double)GetValue(PercentProperty); }
              set { SetValue(PercentProperty, value); }
          }
           
          // Using a DependencyProperty as the backing store for Percent.  This enables animation, styling, binding, etc...
          public static readonly DependencyProperty PercentProperty =
              DependencyProperty.Register("Percent", typeof(double), typeof(DonutLoader), new PropertyMetadata(0d));
           
          /// 
          /// Allow to  hide percent textBlock
          /// 
          [Category("Common Properties")]
          [Description("Show/Hide progression value")]
          public Visibility PercentVisibility
          {
              get { return (Visibility)GetValue(PercentVisibilityProperty); }
              set { SetValue(PercentVisibilityProperty, value); }
          }
           
          // Using a DependencyProperty as the backing store for PercentVisibility.  This enables animation, styling, binding, etc...
          public static readonly DependencyProperty PercentVisibilityProperty =
              DependencyProperty.Register("PercentVisibility", typeof(Visibility), typeof(DonutLoader), new PropertyMetadata(Visibility.Visible));
           
          #endregion

          A ce stade, tout fonctionne comme prévu, mais un petit bug m'a pris un peu de temps à résoudre dans les lignes suivantes.

          private void SetOpacityMask()
          {
          BitmapImage bi = new BitmapImage() { UriSource = new Uri("/Tweened.Controls;component/bitmaps/ProgressBitmap.png", UriKind.RelativeOrAbsolute) };
           
               ImageBrush ib = new ImageBrush();
           
               ib.ImageSource = bi;
           
               _DonutFrameworkElement.RenderTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
           
               _DonutFrameworkElement.OpacityMask = ib;
          }
           
          private void SetStoryboard()
          {
               boucle.Children.Add(daBoucle);
           
               Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));
           
               daBoucle.From = 0;
           
               daBoucle.To = -360;
           
               daBoucle.RepeatBehavior = RepeatBehavior.Forever;
           
               daBoucle.Duration = Duration;
          }

          Lorsque l'on joue l'animation de rotation par C# ou XAML, l'objet qui subit l'animation disparaît soudainement au runtime. Après avoir passé un peu de temps sur ce bug, j'ai décidé non pas d'animer l'objet lui même mais son remplissage :

          private void SetOpacityMask()
          {
              BitmapImage bi = new BitmapImage() { UriSource = new Uri("/Tweened.Controls;component/bitmaps/ProgressBitmap.png", UriKind.RelativeOrAbsolute) };
           
              ImageBrush ib = new ImageBrush();
           
              ib.ImageSource = bi;
           
              ib.RelativeTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
           
              //ancienne version
              //_DonutFrameworkElement.RenderTransform = new CompositeTransform() { CenterX = .5, CenterY = .5 };
           
              _DonutFrameworkElement.OpacityMask = ib;
          }
           
          private void SetStoryboard()
          {
              boucle.Children.Add(daBoucle);
           
              Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.OpacityMask).(Brush.RelativeTransform).(CompositeTransform.Rotation)"));
           
              //ancienne version
              //Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));
           
              daBoucle.From = 0;
           
              daBoucle.To = -360;
           
              daBoucle.RepeatBehavior = RepeatBehavior.Forever;
           
              daBoucle.Duration = Duration;
          }

          j'ai donc remplacé la ligne ciblant la propriété à animer :

          Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)"));

          par :

          Storyboard.SetTargetProperty(daBoucle, new PropertyPath("(UIElement.OpacityMask).(Brush.RelativeTransform).(CompositeTransform.Rotation)"));

          Là, au miracle, plus aucun bug, tout fonctionne parfaitement. C'est la première fois que j'anime, au sein d'un custom contrôle, un objet avec une image en masque d'opacité et j'ai bien perdu 30 minutes là dessus. Si quelqu'un a une idée du pourquoi de ce bug je suis preneur.

          Bookmark and Share

          FireStarter Silverlight

          Aucun commentaire

            Illuminez vos compétences Silverlight avec la toute nouvelle session globale Firestarter Silverlight !
            Pour vous raffraichir un peu les idées le Silverlight Firestarter est un événement mondial d'une journée, diffusé en direct et présenté par Scott Guthrie. Vous pourrez à cette occasion directement communiquer avec l'équipe produit Silverlight, vous aurez également accès à des atelier d'auto-formation.

            Suivez le programme :)

            Pour vous inscrire c'est ici.

            Bookmark and Share

            Sortie officielle de Windows Phone 7

            Aucun commentaire

              Steve Ballmer et Ralph de la Vega, President & CEO de AT&T Mobility, ont annoncé pour le 21 Octobre la sortie mondiale à la vente de Windows Phone 7. Comme l'on pouvait s'y attendre au vu des précédentes annonces, 3 opérateurs historiques français ainsi que 5 constructueurs sont concernés, pas d'exclusivité en vue. A noter que windows Phone 7 contient une version de office mobile spécialement conçu pour l'OS. Le premier device mobile Windows Phone 7 sera disponible au prix de 29 euros. Pour avoir une vue globale de la plateforme WP7, vous pouvez vous référer à la conférence des teched Microsoft que vous trouverez ici. En attendant de l'avoir entre les mains, quelques devices sont sur les rangs, le Omnia 7 de Samsung, le Optimus 7 de LG et pour complètement innover le HTC HD7; c'est ce dernier qui présente le design le plus épuré avec son large écran de 4,7".

              Bookmark and Share

              TextBox Attached Property

              Aucun commentaire

                Lorsque l'on conçoit avec MVVM en Silverlight, on se retrouve rapidement bloqué en matière de binding car le framework Silverlight contrairement à WPF n'a pas pour but d'être exhaustif. De nombreux comportements notamment la mise à jour d'un Binding, peuvent être configurés de manière direct dans WPF au niveau du XAML. Dans Silverlight, la mise à jour d'une liaison de données est souvent réalisée sur l'événement LostFocus ce qui n'est pas l'idéal. En effet lorsque l'utilisateur arrive sur le dernier champ d'un formulaire, juste avant de cliquer sur le bouton, ce dernier ne sait pas forcément si les données qu'il a saisit sont correctes. Pour le savoir celui-ci devra faire en sorte de perdre le focus sur le champ de saisie en question. L'experience utilisateur n'est pas vraiment friendly dans ce cas. Dès lors, on pense à WPF et à la fameuse option UpdateSourceTrigger=PropertyChanged qui résoudrait notre problème en toute simplicité. Grâce aux propriétés attachées, il est possible de reproduire ce comportement au cas par cas. Pour les TextBox, l'idéal serait d'écrire AutoUpdateBinding="True". Voici donc ci-dessous une propriété attachée qui permet de reproduire ce comportement :

                public static class MesExtensions
                {
                    public static bool GetAutoUpdateBinding(DependencyObject obj)
                    {
                        return (bool)obj.GetValue(AutoUpdateBindingProperty);
                    }
                 
                    public static void SetAutoUpdateBinding(DependencyObject obj, bool value)
                    {
                        obj.SetValue(AutoUpdateBindingProperty, value);
                    }
                 
                    // Using a DependencyProperty as the backing store for AutoUpdateBinding.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty AutoUpdateBindingProperty =
                        DependencyProperty.RegisterAttached("AutoUpdateBinding", typeof(bool), typeof(TextBox),
                        new PropertyMetadata(false, new PropertyChangedCallback(OnAutoUpdateChanged)));
                 
                    private static void OnAutoUpdateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
                    {
                        TextBox tb = d as TextBox;
                 
                        if (tb == null)
                        {
                            return;
                        }
                        if (System.Convert.ToBoolean(e.NewValue))
                        {
                            tb.TextChanged += new TextChangedEventHandler(tb_TextChanged);
                        }
                        else
                        {
                            tb.TextChanged -= tb_TextChanged;
                        }
                    }
                 
                    static void tb_TextChanged(object sender, TextChangedEventArgs e)
                    {
                        TextBox tb = sender as TextBox;
                        if (tb == null)
                        {
                            return;
                        }
                 
                        BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
                 
                        if (be != null)
                        {
                            be.UpdateSource();
                        }
                    }
                }

                Ce n'est pas la seule problématique que pose MVVM en matière de binding, le fait de lier des données des collections en lecture seule en est une autre que nous verrons dans un autre post...

                Bookmark and Share

                RadioButton Extension [Custom Control] [API]

                Aucun commentaire


                  Pour les nécessités d'un projet, j'ai récemment eu besoin de conserver l'historique des sélections effectuées au sein d'un groupe de RadioButton. Cette classe existe sur des environnements comme Flex de manière native mais pas sur Silverlight, du coup, il est nécessaire de concevoir un mini framework pour avoir le même type de fonctionnalités. Généralement la classe RadioButtonGroup possède un événement Selected diffusé lorsqu'un des RadioButton est sélectionné au sein du groupe. Toutefois pour les besoins de la production, nous avions besoin d'historier les sélections successives car ceux-ci était utilisés comme menu. Du coup, j'ai développé une mini API permettant de faire cela et un peu plus.

                  Cette API est composée de différents éléments :
                  - La classe RadioButtonGroup
                  - Des propriétés attachées pour activer / désactiver / stocker un groupe de radio boutton associé.
                  - Une méthode d'extension
                  - Un comportement facilitant l'activation de RadioButtonGroup (à glisser dans Blend sur un RadioButton)

                  Il est intéressant de constater qu'utiliser un comportement peut s'avérer bien plus souple qu'utiliser une Attached Property. Bien qu'une Attached Property puisse être définit dans un style, celle-ci le constructeur d'une MainPage ne pourra pas accéder à une instance de RadioButtonGroup directement. Vous pourrez le faire uniquement sur le Loaded. Dans le cas du comportement cela n'est pas vrai car le RadioButtonGroup est instancié via la méthode OnAttached qui est appelée dès que le comportement est affecté à un RadioButton, c'est à dire bien avant son affichage effectif. Je mets un projet de test au téléchargement ici.

                  Pour chacun d'eux, je mets le code en clair dans les sections ci-dessous :

                  La classe RadioButtonGroup

                  Celle-ci possède plusieurs membres très utiles :

                  • SelectionChanged

                  Cet événement est diffusé lorsqu'un RadioButton du groupe est sélectionné.

                  • Children

                  CollectionObservable contenant tous les RadioButton du groupe

                  • Historic

                  CollectionObservable contenant l'historique de sélection

                  • Current

                  Propriété renvoyant le RadioButton actuellement sélectionné

                  • Last

                  Propriété renvoyant le RadioButton précédemment sélectionné

                  • Add

                  Méthode permettant d'ajouter un RadioButton au groupe

                  • Remove

                  Méthode permettant de supprimer un RadioButton du groupe

                  • CancelLastSelection

                  Annule la dernière sélection. Très pratique pour certaines fonctionnalités spécifiques aux menus

                  public class RadioButtonGroup : INotifyPropertyChanged
                  {
                   
                      ///
                      /// This event is fired when the selected RadioButton has been modified.
                      ///
                      public event EventHandler
                   SelectionChanged;
                   
                      private bool _IsCanceledByGroup;
                      public bool IsCanceledByGroup
                      {
                          get
                          {
                              return _IsCanceledByGroup;
                          }
                          set
                          {
                              _IsCanceledByGroup = value;
                          }
                      }
                   
                      ///
                      /// ObservableCollection wich contains an historic of selected RadioButton
                      ///
                      private ObservableCollection _Historic = new ObservableCollection();
                      public ObservableCollection Historic
                      {
                          get
                          {
                              return _Historic;
                          }
                          set
                          {
                              _Historic = value;
                          }
                      }
                   
                      ///
                      /// ObservableCollection of RadioButton contained by the RadioButtonGroup
                      ///
                      private ObservableCollection _Children = new ObservableCollection();
                      public ObservableCollection Children
                      {
                          get
                          {
                              return _Children;
                          }
                          set
                          {
                              _Children = value;
                          }
                      }
                   
                      ///
                      /// Returns Current Selected RadioButton in the group
                      ///
                      private RadioButton _Current;
                      public RadioButton Current
                      {
                          get
                          {
                              return _Current;
                          }
                          set
                          {
                              if (!Object.ReferenceEquals(_Current, value))
                              {
                                  _Current = value;
                   
                                  NotifyPropertyChanged("Current");
                   
                                  if (SelectionChanged != null)
                                  {
                                      SelectionChanged(this, new SelectedChangedEventArgs() { Last=_Last,Current=_Current, CurrentGroup=this });
                                  }
                   
                              }
                          }
                      }
                   
                      ///
                      /// Returns last selected RadioButton in the group
                      ///
                      private RadioButton _Last;
                      public RadioButton Last
                      {
                          get
                          {
                              return _Last;
                          }
                          set
                          {
                              if (!Object.ReferenceEquals(_Current, value))
                              {
                                  _Last = value;
                   
                                  NotifyPropertyChanged("Last");
                              }
                          }
                      }
                   
                      ///
                      /// Add a RadioButton to the RadioButtonGroup
                      ///
                      ///
                   
                      public void Add(RadioButton rb)
                      {
                          _Children.Add(rb);
                          rb.Checked += new RoutedEventHandler(rb_Checked);
                      }
                   
                      ///
                      /// Remove a RadioButton contained by the RadioButtonGroup
                      ///
                      ///
                   
                      public void Remove(RadioButton rb)
                      {
                          _Children.Remove(rb);
                          rb.Checked -= new RoutedEventHandler(rb_Checked);
                      }
                   
                      ///
                      /// Cancel last selection in the group and select the one before
                      ///
                      /// Return Current Selection after CancelLastSelection method has been called.
                      public void CancelLastSelection()
                      {
                   
                          if (Historic.Count > 1)
                          {
                              Historic.RemoveAt(Historic.Count - 1);
                          }
                          Historic[Historic.Count - 1].IsChecked = true;
                      }
                   
                      private void rb_Checked(object sender, RoutedEventArgs e)
                      {
                          Current = sender as RadioButton;
                   
                          Historic.Add(Current);
                   
                          Last = Historic[Historic.Count - 1];
                      }
                   
                      #region NPC members
                   
                      public event PropertyChangedEventHandler PropertyChanged;
                   
                      private void NotifyPropertyChanged(string prop)
                      {
                          if (PropertyChanged != null)
                          {
                              PropertyChangedEventArgs e = new PropertyChangedEventArgs(prop);
                              PropertyChanged(this, e);
                          }
                      }
                   
                      #endregion
                   
                  }

                  Des propriétés de dépendance attachées (aux RadioButton) facilitant l'activation et l'instanciation d'un objet RadioButtonGroup.

                  ActivateRadioButtonGroup permet d'activer la gestion via RadioButtonGroup.
                  CommitProperty permet de sélectionné un RadioButton lorsqu'une propriété de votre choix est modifiée.

                  namespace Tweened.Extensions.RadioButtonEx
                  {
                   
                      public static partial class RadioButtonExtension
                      {
                          #region static Dictionnary string / RadioButtonGroup named RadioButtonGroups
                          private static Dictionary _RadioButtonGroups = new Dictionary();
                          ///
                          /// Contains RadioButtonGroups in the application
                          ///
                          public static Dictionary RadioButtonGroups
                          {
                              get
                              {
                                  return _RadioButtonGroups;
                              }
                              set
                              {
                                  _RadioButtonGroups = value;
                              }
                          }
                          #endregion
                   
                          #region Commit DependencyProperty enables the radiobutton to autoselect if some property has changed
                          ///
                          /// Enable the radiobutton to autoselect if some property has changed
                          ///
                          public static object GetCommitProperty(DependencyObject obj)
                          {
                              return (object)obj.GetValue(CommitPropertyProperty);
                          }
                   
                          public static void SetCommitProperty(DependencyObject obj, object value)
                          {
                              obj.SetValue(CommitPropertyProperty, value);
                          }
                   
                          // Using a DependencyProperty as the backing store for CommitProperty.  This enables animation, styling, binding, etc...
                          public static readonly DependencyProperty CommitPropertyProperty =
                              DependencyProperty.RegisterAttached("CommitProperty", typeof(object), typeof(RadioButton), new PropertyMetadata(null,
                                                                                                                          new PropertyChangedCallback(OnCommitProperty)));
                   
                          private static void OnCommitProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
                          {
                              RadioButton rb = d as RadioButton;
                              if (rb == null)
                              {
                                  return;
                              }
                   
                              if (e.NewValue != e.OldValue)
                              {
                   
                                  rb.IsChecked = e.NewValue != null;
                              }
                          }
                          #endregion
                   
                          #region ActivateRadioButtonGroup DependencyProperty enables RadioButtonGroup management for a given RadioButton
                   
                          ///
                          /// Retrieves the RadioButtonGroup wich hold the RadioButton.
                          ///
                          ///
                   
                          ///
                          public static RadioButtonGroup GetRadioButtonGroup(DependencyObject obj)
                          {
                              return (RadioButtonGroup)obj.GetValue(RadioButtonGroupProperty);
                          }
                   
                          public static void SetRadioButtonGroup(DependencyObject obj, RadioButtonGroup value)
                          {
                              obj.SetValue(RadioButtonGroupProperty, value);
                          }
                   
                          // Using a DependencyProperty as the backing store for RadioButtonGroup.  This enables animation, styling, binding, etc...
                          private static readonly DependencyProperty RadioButtonGroupProperty =
                              DependencyProperty.RegisterAttached("RadioButtonGroup", typeof(RadioButtonGroup), typeof(RadioButton), null);
                   
                          ///
                          /// Activate RadioButton Group managment
                          ///
                          ///
                   
                          ///
                          public static bool GetActivateRadioButtonGroup(DependencyObject obj)
                          {
                              return (bool)obj.GetValue(ActivateRadioButtonGroupProperty);
                          }
                   
                          public static void SetActivateRadioButtonGroup(DependencyObject obj, bool value)
                          {
                              obj.SetValue(ActivateRadioButtonGroupProperty, value);
                          }
                   
                          // Using a DependencyProperty as the backing store for ActivateRadioButtonGroup.  This enables animation, styling, binding, etc...
                          public static readonly DependencyProperty ActivateRadioButtonGroupProperty =
                              DependencyProperty.RegisterAttached("ActivateRadioButtonGroup", typeof(bool), typeof(RadioButton), new PropertyMetadata(false, new PropertyChangedCallback(OnActivateRadioButtonGroupChanged)));
                   
                          private static void OnActivateRadioButtonGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
                          {
                              RadioButton rb = d as RadioButton;
                   
                              if (rb == null || String.IsNullOrEmpty(rb.GroupName))
                              {
                                  throw new Exception("Object is not a RadioButton or GroupName is not defined.");
                              }
                   
                              RadioButtonGroup rbg;
                   
                              if (System.Convert.ToBoolean(e.NewValue))
                              {
                                  if (!RadioButtonExtension.RadioButtonGroups.ContainsKey(rb.GroupName))
                                  {
                                      rbg = new RadioButtonGroup();
                                      RadioButtonExtension.RadioButtonGroups.Add(rb.GroupName, rbg);
                                      Application.Current.Resources.Add(rb.GroupName, rbg);
                                      rbg.Add(rb);
                                  }
                                  else
                                  {
                                      rbg = RadioButtonExtension.RadioButtonGroups[rb.GroupName];
                                      rbg.Add(rb);
                                  }
                                  RadioButtonExtension.SetRadioButtonGroup(rb, rbg);
                              }
                              else
                              {
                                  if (RadioButtonExtension.RadioButtonGroups.ContainsKey(rb.GroupName))
                                  {
                                      rbg = RadioButtonExtension.RadioButtonGroups[rb.GroupName];
                                      rbg.Remove(rb);
                                      RadioButtonExtension.SetRadioButtonGroup(rb, null);
                                  }
                              }
                   
                          }
                   
                          #endregion
                      }
                  }

                  Des comportements que vous pourrez glisser directement sur des instances de RadioButton ou un conteneur en contenant.

                  public class RadioButtonGroupBehavior : Behavior
                  {
                  	public RadioButtonGroupBehavior()
                  	{
                   
                  	}
                   
                  	protected override void OnAttached()
                  	{
                  		base.OnAttached();
                   
                          	if (AssociatedObject is Panel)
                          	{
                              		AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
                          	}
                         		else if (AssociatedObject is RadioButton)
                          	{
                             		RadioButtonExtension.SetActivateRadioButtonGroup(AssociatedObject as RadioButton, true);
                          	}
                  	}
                   
                  	void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
                  	{
                  		foreach (UIElement b in (sender as Panel).Children)
                  		{
                  			if (b as RadioButton != null)
                  				RadioButtonExtension.SetActivateRadioButtonGroup(b as RadioButton, true);
                  		}
                  	}
                   
                  	protected override void OnDetaching()
                  	{
                  		base.OnDetaching();
                   
                          	if (AssociatedObject is Panel)
                          	{
                              		AssociatedObject.Loaded -= AssociatedObject_Loaded; 
                   
                              		foreach (RadioButton b in (AssociatedObject as Panel).Children)
                              		{
                                  		RadioButtonExtension.SetActivateRadioButtonGroup(b, false);
                              		}
                          	}
                          	else if (AssociatedObject is RadioButton)
                          	{
                             		 RadioButtonExtension.SetActivateRadioButtonGroup(AssociatedObject as RadioButton, false);
                          	}
                  	}
                   
                  }

                  Une méthode d'extension pour les instances de RadioButton qui facilite la récupération du RadioButtonGroup associé.

                  public static partial class RadioButtonExtension
                  {
                      #region Extension Method GetRadioButtonGroup
                      ///
                      /// Allow to get the RadioButtonGroup associated to a RadioButton
                      ///
                      ///
                   
                      ///
                      public static RadioButtonGroup GetRadioButtonGroup(this RadioButton radioButton)
                      {
                          RadioButtonGroup rbg = null;
                   
                          if (RadioButtonExtension.RadioButtonGroups.ContainsKey(radioButton.GroupName))
                          {
                              rbg = RadioButtonExtension.RadioButtonGroups[radioButton.GroupName];
                          }
                   
                          return rbg;
                      }
                      #endregion
                  }
                  Bookmark and Share

                  UniformGrid for Silverlight [Custom Control]

                  Aucun commentaire

                    Je n'ai jamais compris pourquoi le conteneur d'agencement UniformGrid n'était pas dans le silverlight ToolKit. Contrairement à ce que l'on peut imaginer, il n'est pas toujours remplaçable par un WrapPanel. J'ai donc porté l'existant WPF vers Silverlight. Comme ce panneau est relativement utile, voici la classe UniformGrid en téléchargement.

                    Bookmark and Share

                    Silverlight Bing Map for Wordpress [Plugin]

                    1 commentaire

                      Get Microsoft Silverlight

                      Voici un nouveau plugin pour wordpress. L'idée est cette fois de faciliter l'intégration d'une carte interactive basée sur Silverlight Bingmap. Pour cela rien de plus simple, il suffit d'installer le plugin que vous trouverez en téléchargement ici.

                      Get Microsoft Silverlight
                      Bookmark and Share

                      « Previous PageNext Page »