it-swarm.com.de

Wie binde ich Optionsfelder an eine Aufzählung?

Ich habe eine Aufzählung wie folgt:

public enum MyLovelyEnum
{
  FirstSelection,
  TheOtherSelection,
  YetAnotherOne
};

Ich habe eine Eigenschaft in meinem DataContext:

public MyLovelyEnum VeryLovelyEnum { get; set; }

Und ich habe drei RadioButtons in meinem WPF-Client.

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

Wie binde ich nun die RadioButtons an die Eigenschaft, um eine ordnungsgemäße wechselseitige Bindung zu gewährleisten?

395
Sam

Sie könnten einen allgemeineren Konverter verwenden

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
}

Und im XAML-Part verwenden Sie:

<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

Für die Antwort von EnumToBooleanConverter: Anstatt DependencyProperty.UnsetValue zurückzugeben, sollten Sie Binding.DoNothing für den Fall zurückgeben, dass der Optionsfeldwert IsChecked zu false wird. Ersteres weist auf ein Problem hin (und zeigt dem Benutzer möglicherweise ein rotes Rechteck oder ähnliche Überprüfungsindikatoren an), während Letzteres lediglich darauf hinweist, dass nichts unternommen werden sollte, was in diesem Fall erwünscht ist.

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

Ich würde die RadioButtons in einer ListBox verwenden und dann an den SelectedValue binden.

Dies ist ein älterer Thread zu diesem Thema, aber die Grundidee sollte dieselbe sein: http://social.msdn.Microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f- 8d99-fecf45522395 /

5
Martin Moser

Bei UWP ist das nicht so einfach: Sie müssen durch einen zusätzlichen Rahmen springen, um einen Feldwert als Parameter zu übergeben.

Beispiel 1

Gültig für WPF und UWP.

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

Beispiel 2

Gültig für WPF und UWP.

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

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

Beispiel

Gilt nur für WPF!

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

UWP unterstützt x:Static Nicht, sodass Beispiel 3 nicht in Frage kommt. Angenommen, Sie gehen mit Beispiel 1 , ist das Ergebnis ausführlicherer Code. Beispiel 2 ist etwas besser, aber immer noch nicht ideal.

Lösung

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);
    }
}

Definieren Sie dann für jeden Typ, den Sie unterstützen möchten, einen Konverter, der den Aufzählungstyp enthält.

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

Der Grund dafür ist, dass es anscheinend keine Möglichkeit gibt, den Typ in der ConvertBack -Methode zu referenzieren. Dafür sorgt das Boxen. Wenn Sie sich für eines der ersten beiden Beispiele entscheiden, können Sie einfach auf den Parametertyp verweisen, sodass Sie nicht mehr von einer Box-Klasse erben müssen. Wenn Sie alles in einer Zeile und mit der geringstmöglichen Ausführlichkeit ausführen möchten, ist die letztere Lösung ideal.

Die Verwendung ähnelt Beispiel 2 , ist jedoch weniger ausführlich.

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

Der Nachteil ist, dass Sie für jeden Typ, den Sie unterstützen möchten, einen Konverter definieren müssen.

3
James M

Ich habe eine neue Klasse erstellt, um RadioButtons und CheckBoxes an Enums zu binden. Es funktioniert für markierte Aufzählungen (mit mehreren Kontrollkästchen) und nicht markierte Aufzählungen für Kontrollkästchen oder Optionsfelder mit einfacher Auswahl. Es werden auch überhaupt keine ValueConverter benötigt.

Dies sieht auf den ersten Blick möglicherweise komplizierter aus. Sobald Sie diese Klasse in Ihr Projekt kopiert haben, ist sie abgeschlossen. Es ist generisch und kann daher problemlos für jede Aufzählung wiederverwendet werden.

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));
  }
}

Nehmen wir an, Sie haben eine Auflistung für die manuelle oder automatische Ausführung einer Aufgabe, die für jeden Wochentag geplant werden kann, und einige optionale Optionen ...

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
}

So einfach ist die Verwendung dieser Klasse:

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; }
}

Und so einfach ist es, Kontrollkästchen und Optionsfelder mit dieser Klasse zu verknüpfen:

<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. Wenn die Benutzeroberfläche geladen wird, wird das Optionsfeld "Manuell" ausgewählt und Sie können zwischen "Manuell" und "Automatisch" wählen. Es muss jedoch immer eines davon ausgewählt sein.
  2. Jeder Wochentag wird deaktiviert, aber eine beliebige Anzahl von ihnen kann aktiviert oder deaktiviert werden.
  3. "Option A" und "Option B" sind zunächst deaktiviert. Sie können das eine oder das andere aktivieren, wenn Sie das andere aktivieren, wird das andere deaktiviert (ähnlich wie bei RadioButtons). Jetzt können Sie jedoch auch beide deaktivieren (was Sie mit dem RadioButton von WPF nicht tun können, weshalb hier CheckBox verwendet wird).
1
Nick

Dies funktioniert auch für Checkbox.

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());
    }
}

Binden einer einzelnen Aufzählung an mehrere Kontrollkästchen.

1
Ali Bayat

Basiert auf dem EnumToBooleanConverter von Scott. Ich habe festgestellt, dass die ConvertBack-Methode in Enum with flags-Code nicht funktioniert.

Ich habe den folgenden Code ausprobiert:

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;
                }
            }
        }
    }

Das Einzige, was ich nicht zur Arbeit bringen kann, ist ein Cast von int zu targetType, also habe ich es hartcodiert zu NavigationProjectDates, der von mir verwendeten Aufzählung. Und, targetType == NavigationProjectDates...


Bearbeiten Sie für allgemeinere Flags Enum Converter:

 public class FlagsEnumToBooleanConverter: IValueConverter {
 private int _flags = 0; 
 public object Convert (Objektwert, Typ targetType, Objektparameter, Zeichenfolgensprache) {
 if (value == null) return false; 
 _flags = (int) value; 
 Type t = value.GetType (); 
 object o = Enum.ToObject (t, parameter ); 
 return ((Enum) value) .HasFlag ((Enum) o); 
} 
 
 public object ConvertBack (Objektwert, Typ targetType, Objekt Parameter, String-Sprache) 
 {
 if (Wert? .Equals (true) ?? false) {
 _flags = _flags | (int) parameter; 
} 
 else {
 _flags = _flags & ~ (int) parameter; 
} 
 return _flags; 
} 
} 
0
KenGey