
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 :
Cet événement est diffusé lorsqu'un RadioButton du groupe est sélectionné.
CollectionObservable contenant tous les RadioButton du groupe
CollectionObservable contenant l'historique de sélection
Propriété renvoyant le RadioButton actuellement sélectionné
Propriété renvoyant le RadioButton précédemment sélectionné
Méthode permettant d'ajouter un RadioButton au groupe
Méthode permettant de supprimer un RadioButton du groupe
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
}