web-dev-qa-db-fra.com

Comment lier Radio Buttons à une énumération?

J'ai un enum comme ça:

public enum MyLovelyEnum
{
  FirstSelection,
  TheOtherSelection,
  YetAnotherOne
};

J'ai une propriété dans mon DataContext:

public MyLovelyEnum VeryLovelyEnum { get; set; }

Et j'ai trois boutons radio dans mon client WPF.

<RadioButton Margin="3">First Selection</RadioButton>
<RadioButton Margin="3">The Other Selection</RadioButton>
<RadioButton Margin="3">Yet Another one</RadioButton>

Maintenant, comment lier les RadioButtons à la propriété pour une liaison correcte dans les deux sens?

395
Sam

Vous pouvez utiliser un convertisseur plus générique

public class EnumBooleanConverter : IValueConverter
{
  #region IValueConverter Members
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
      return DependencyProperty.UnsetValue;

    if (Enum.IsDefined(value.GetType(), value) == false)
      return DependencyProperty.UnsetValue;

    object parameterValue = Enum.Parse(value.GetType(), parameterString);

    return parameterValue.Equals(value);
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
        return DependencyProperty.UnsetValue;

    return Enum.Parse(targetType, parameterString);
  }
  #endregion
}

Et dans la partie XAML que vous utilisez:

<Grid>
    <Grid.Resources>
      <l:EnumBooleanConverter x:Key="enumBooleanConverter" />
    </Grid.Resources>
    <StackPanel >
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FirstSelection}">first selection</RadioButton>
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=TheOtherSelection}">the other selection</RadioButton>
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=YetAnotherOne}">yet another one</RadioButton>
    </StackPanel>
</Grid>
377
Lars

Pour la réponse EnumToBooleanConverter, procédez comme suit: Au lieu de renvoyer DependencyProperty.UnsetValue, envisagez de renvoyer Binding.DoNothing pour le cas où la valeur du bouton radio IsChecked devient false. Le premier indique un problème (et peut montrer à l'utilisateur un rectangle rouge ou des indicateurs de validation similaires) tandis que le dernier indique simplement que rien ne doit être fait, ce qui est recherché dans ce cas.

http://msdn.Microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback.aspxhttp://msdn.Microsoft.com/en- us/library/system.windows.data.binding.donothing.aspx

25
anon

Je voudrais utiliser les RadioButtons dans un ListBox, puis lier à SelectedValue.

Ceci est un fil plus ancien sur ce sujet, mais l'idée de base devrait être la même: http://social.msdn.Microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f- 8d99-fecf45522395 /

5
Martin Moser

Pour UWP, ce n’est pas si simple: vous devez passer par un cercle supplémentaire pour transmettre une valeur de champ en tant que paramètre.

Exemple 1

Valable pour WPF et UWP.

<MyControl>
    <MyControl.MyProperty>
        <Binding Converter="{StaticResource EnumToBooleanConverter}" Path="AnotherProperty">
            <Binding.ConverterParameter>
                <MyLibrary:MyEnum>Field</MyLibrary:MyEnum>
            </Binding.ConverterParameter>
        </MyControl>
    </MyControl.MyProperty>
</MyControl>

Exemple 2

Valable pour WPF et UWP.

...
<MyLibrary:MyEnum x:Key="MyEnumField">Field</MyLibrary:MyEnum>
...

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource MyEnumField}}"/>

Exemple

Valable uniquement pour WPF!

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static MyLibrary:MyEnum.Field}}"/>

UWP ne supporte pas x:Static donc Exemple 3 est hors de question; en supposant que vous y alliez Exemple 1 , le résultat est un code plus détaillé. Exemple 2 est légèrement meilleur, mais pas idéal.

Solution

public abstract class EnumToBooleanConverter<TEnum> : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;

        if (Parameter == null)
            return DependencyProperty.UnsetValue;

        if (Enum.IsDefined(typeof(TEnum), value) == false)
            return DependencyProperty.UnsetValue;

        return Enum.Parse(typeof(TEnum), Parameter).Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;
        return Parameter == null ? DependencyProperty.UnsetValue : Enum.Parse(typeof(TEnum), Parameter);
    }
}

Ensuite, pour chaque type que vous souhaitez prendre en charge, définissez un convertisseur qui encadre le type d’énumération.

public class MyEnumToBooleanConverter : EnumToBooleanConverter<MyEnum>
{
    //Nothing to do!
}

La raison pour laquelle il doit être encadré est qu’il n’ya apparemment aucun moyen de référencer le type dans la méthode ConvertBack; la boxe s'en occupe. Si vous choisissez l'un des deux premiers exemples, vous pouvez simplement référencer le type de paramètre, éliminant ainsi le besoin d'hériter d'une classe boxed; si vous souhaitez tout faire en une ligne et avec le moins de verbosité possible, cette dernière solution est idéale.

L'usage ressemble à Exemple 2 , mais est en fait moins détaillé.

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource MyEnumToBooleanConverter}, ConverterParameter=Field}"/>

L'inconvénient est que vous devez définir un convertisseur pour chaque type que vous souhaitez prendre en charge.

3
James M

J'ai créé une nouvelle classe pour gérer les liaisons RadioButtons et CheckBox en enums. Cela fonctionne pour les énumérations marquées (avec plusieurs sélections de cases à cocher) et les énumérations non marquées pour les cases à cocher ou les boutons radio à sélection unique. De plus, il ne nécessite aucun ValueConverters.

Cela peut paraître plus compliqué au début, cependant, une fois que vous avez copié cette classe dans votre projet, c’est fait. C'est générique, donc il peut facilement être réutilisé pour n'importe quelle énumération.

public class EnumSelection<T> : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible
{
  private T value; // stored value of the Enum
  private bool isFlagged; // Enum uses flags?
  private bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can)
  private T blankValue; // what is considered the "blank" value if it can be deselected?

  public EnumSelection(T value) : this(value, false, default(T)) { }
  public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default(T)) { }
  public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { }
  public EnumSelection(T value, bool canDeselect, T blankValue)
  {
    if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums...
    isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false);

    this.value = value;
    this.canDeselect = canDeselect;
    this.blankValue = blankValue;
  }

  public T Value
  {
    get { return value; }
    set 
    {
      if (this.value.Equals(value)) return;
      this.value = value;
      OnPropertyChanged();
      OnPropertyChanged("Item[]"); // Notify that the indexer property has changed
    }
  }

  [IndexerName("Item")]
  public bool this[T key]
  {
    get
    {
      int iKey = (int)(object)key;
      return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key);
    }
    set
    {
      if (isFlagged)
      {
        int iValue = (int)(object)this.value;
        int iKey = (int)(object)key;

        if (((iValue & iKey) == iKey) == value) return;

        if (value)
          Value = (T)(object)(iValue | iKey);
        else
          Value = (T)(object)(iValue & ~iKey);
      }
      else
      {
        if (this.value.Equals(key) == value) return;
        if (!value && !canDeselect) return;

        Value = value ? key : blankValue;
      }
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void OnPropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

Et pour savoir comment l'utiliser, supposons que vous ayez un enum pour exécuter une tâche manuellement ou automatiquement, et que vous puissiez le planifier pour n'importe quel jour de la semaine, ainsi que certaines options facultatives ...

public enum StartTask
{
  Manual,
  Automatic
}

[Flags()]
public enum DayOfWeek
{
  Sunday = 1 << 0,
  Monday = 1 << 1,
  Tuesday = 1 << 2,
  Wednesday = 1 << 3,
  Thursday = 1 << 4,
  Friday = 1 << 5,
  Saturday = 1 << 6
}

public enum AdditionalOptions
{
  None = 0,
  OptionA,
  OptionB
}

Maintenant, voici à quel point il est facile d’utiliser cette classe:

public class MyViewModel : ViewModelBase
{
  public MyViewModel()
  {
    StartUp = new EnumSelection<StartTask>(StartTask.Manual);
    Days = new EnumSelection<DayOfWeek>(default(DayOfWeek));
    Options = new EnumSelection<AdditionalOptions>(AdditionalOptions.None, true, AdditionalOptions.None);
  }

  public EnumSelection<StartTask> StartUp { get; private set; }
  public EnumSelection<DayOfWeek> Days { get; private set; }
  public EnumSelection<AdditionalOptions> Options { get; private set; }
}

Et voici comment il est facile de lier les cases à cocher et les boutons radio avec cette classe:

<StackPanel Orientation="Vertical">
  <StackPanel Orientation="Horizontal">
    <!-- Using RadioButtons for exactly 1 selection behavior -->
    <RadioButton IsChecked="{Binding StartUp[Manual]}">Manual</RadioButton>
    <RadioButton IsChecked="{Binding StartUp[Automatic]}">Automatic</RadioButton>
  </StackPanel>
  <StackPanel Orientation="Horizontal">
    <!-- Using CheckBoxes for 0 or Many selection behavior -->
    <CheckBox IsChecked="{Binding Days[Sunday]}">Sunday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Monday]}">Monday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Tuesday]}">Tuesday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Wednesday]}">Wednesday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Thursday]}">Thursday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Friday]}">Friday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Saturday]}">Saturday</CheckBox>
  </StackPanel>
  <StackPanel Orientation="Horizontal">
    <!-- Using CheckBoxes for 0 or 1 selection behavior -->
    <CheckBox IsChecked="{Binding Options[OptionA]}">Option A</CheckBox>
    <CheckBox IsChecked="{Binding Options[OptionB]}">Option B</CheckBox>
  </StackPanel>
</StackPanel>
  1. Lors du chargement de l'interface utilisateur, le bouton radio "Manuel" est sélectionné et vous pouvez modifier votre sélection entre "Manuel" ou "Automatique", mais l'un ou l'autre d'entre eux doit toujours être sélectionné.
  2. Tous les jours de la semaine ne sont pas contrôlés, mais vous pouvez les cocher ou les désélectionner.
  3. "Option A" et "Option B" seront initialement décochées. Vous pouvez cocher l'une ou l'autre option, en cochant une qui décochera l'autre (similaire à RadioButtons), mais maintenant vous pouvez également désélectionner les deux (ce que vous ne pouvez pas faire avec le bouton RadioButton de WPF, c'est pourquoi CheckBox est utilisé ici)
1
Nick

Ce travail pour Checkbox aussi.

public class EnumToBoolConverter:IValueConverter
{
    private int val;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int intParam = (int)parameter;
        val = (int)value;

        return ((intParam & val) != 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        val ^= (int)parameter;
        return Enum.Parse(targetType, val.ToString());
    }
}

Relier une seule énumération à plusieurs cases à cocher.

1
Ali Bayat

Basé sur le convertisseur EnumToBoolean de Scott. J'ai remarqué que la méthode ConvertBack ne fonctionne pas sur le code Enum avec indicateurs.

J'ai essayé le code suivant:

public class EnumHasFlagToBooleanConverter : IValueConverter
    {
        private object _obj;
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            _obj = value;
            return ((Enum)value).HasFlag((Enum)parameter);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value.Equals(true))
            {
                if (((Enum)_obj).HasFlag((Enum)parameter))
                {
                    // Do nothing
                    return Binding.DoNothing;
                }
                else
                {
                    int i = (int)_obj;
                    int ii = (int)parameter;
                    int newInt = i+ii;
                    return (NavigationProjectDates)newInt;
                }
            }
            else
            {
                if (((Enum)_obj).HasFlag((Enum)parameter))
                {
                    int i = (int)_obj;
                    int ii = (int)parameter;
                    int newInt = i-ii;
                    return (NavigationProjectDates)newInt;

                }
                else
                {
                    // do nothing
                    return Binding.DoNothing;
                }
            }
        }
    }

La seule chose que je ne peux pas me mettre au travail est de faire un casting de int à targetType afin de le coder en dur en NavigationProjectDates, l'énum que j'utilise. Et, targetType == NavigationProjectDates...


Modifier pour plus de convertisseur générique Flags Enum:

 public class FlagsEnumToBooleanConverter: IValueConverter {
 private int _flags = 0; 
 objet public Convert (valeur d'objet, Type targetType, paramètre d'objet, langage de chaîne) {
 if (valeur == null) renvoie false; 
 _flags = (int) valeur; 
 Tapez t = valeur.GetType (); 
 objet o = Enum.ToObject (t, paramètre ); 
 return ((Enum) value) .HasFlag ((Enum) o); 
} 
 
 objet public ConvertBack (valeur de l'objet, Type targetType, objet paramètre, langage de chaîne) 
 {
 if (valeur? .Equals (true) ?? false) {
 _flags = _flags | (int) paramètre; 
} 
 sinon {
 _flags = _flags & ~ (int) paramètre; 
} 
 return _flags; 
} 
} 
0
KenGey