Wie binde ich RadioButtons an eine Aufzählung?

406

Ich habe eine Aufzählung wie diese:

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 für eine ordnungsgemäße bidirektionale Bindung an die Eigenschaft?

Sam
quelle
3
Wenn Sie dies tun möchten, ohne einzelne RadioButtons in Ihrer XAML anzugeben, würde ich eine ListBox empfehlen, die an die Aufzählungswerte wie diesen oder diesen gebunden ist und bei der die Elementvorlage überschrieben wurde, um RadioButtons wie diesen zu verwenden .
Rachel

Antworten:

389

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-Teil 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>
Lars
quelle
51
Arbeitete wie ein Zauber für mich. Als Ergänzung habe ich ConvertBack so geändert, dass UnsetValue auch auf "false" zurückgegeben wird, da silverlight (und vermutlich WPF-korrekt) den Konverter zweimal aufruft - einmal beim Deaktivieren des alten Optionsfeldwerts und erneut zum Festlegen des neuen. Ich habe andere Dinge an den Immobilienmakler gehängt, also wollte ich, dass es nur einmal aufgerufen wird. - if (parameterString == null || value.Equals (false)) return DependencyProperty.UnsetValue;
MarcE
8
Soweit ich das beurteilen kann, muss dies erfolgen , es sei denn, die Optionsfelder befinden sich in verschiedenen Gruppen (und AFAIK-Schaltflächen ohne festgelegten Gruppennamen mit demselben übergeordneten Element befinden sich standardmäßig in derselben Gruppe). Andernfalls führen die Aufrufe zum Festlegen der Eigenschaft "Bounce" zu einem merkwürdigen Verhalten.
Nlawalker
2
Ja, aber wenn Sie Unset im Konverter aufrufen, wenn Sie auf false setzen, handelt es sich nicht um einen echten EnumToBooleanConverter, sondern eher um einen EnumToRadioButtonConverter. Also überprüfe ich stattdessen, ob der Wert in meinem Eigenschaftssetter unterschiedlich ist: if (_myEnumBackingField == value) return;
Stéphane
8
Die Bindung an diese Lösung funktioniert nur in eine Richtung korrekt. Ich konnte den Radiobutton nicht programmgesteuert umschalten, indem ich die gebundene Eigenschaft einem anderen Wert zuwies. Wenn Sie eine ordnungsgemäß funktionierende UND bessere Lösung wünschen, verwenden Sie den Ansatz von Scott.
l46kok
2
@Marc, ist es nicht richtig, in diesem Fall 'Binding.DoNothing' und nicht 'DependencyProperty.UnsetValue' zurückzugeben?
Mark A. Donohoe
559

Sie können die akzeptierte Antwort weiter vereinfachen. Anstatt die Aufzählungen in xaml als Zeichenfolgen einzugeben und mehr Arbeit in Ihrem Konverter als nötig zu erledigen, können Sie den Aufzählungswert anstelle einer Zeichenfolgendarstellung explizit übergeben. Wie CrimsonX kommentierte, werden Fehler eher zur Kompilierungszeit als zur Laufzeit ausgegeben:

ConverterParameter = {x: Statisch lokal: YourEnumType.Enum1}

<StackPanel>
    <StackPanel.Resources>          
        <local:ComparisonConverter x:Key="ComparisonConverter" />          
    </StackPanel.Resources>
    <RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum1}}" />
    <RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum2}}" />
</StackPanel>

Dann vereinfachen Sie den Konverter:

public class ComparisonConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(true) == true ? parameter : Binding.DoNothing;
    }
}

Bearbeiten (16. Dezember 10):

Vielen Dank an anon für den Vorschlag, Binding.DoNothing anstelle von DependencyProperty.UnsetValue zurückzugeben.


Hinweis - Mehrere Gruppen von RadioButtons in demselben Container (17. Februar 11):

Wenn in xaml Optionsfelder denselben übergeordneten Container verwenden, werden durch Auswahl eines Optionsfelds alle anderen in diesem Container deaktiviert (auch wenn sie an eine andere Eigenschaft gebunden sind). Versuchen Sie also, Ihre RadioButton-Dateien, die an eine gemeinsame Eigenschaft gebunden sind, wie ein Stapelfenster in einem eigenen Container zusammenzufassen. In Fällen, in denen Ihre zugehörigen RadioButtons keinen einzelnen übergeordneten Container gemeinsam nutzen können, setzen Sie die GroupName-Eigenschaft jedes RadioButton auf einen gemeinsamen Wert, um sie logisch zu gruppieren.


Bearbeiten (5. April 11):

Vereinfachtes ConvertBack-If-else zur Verwendung eines ternären Operators.


Hinweis - Aufzählungstyp in einer Klasse verschachtelt (28. April 11):

Wenn Ihr Aufzählungstyp in einer Klasse verschachtelt ist (und nicht direkt im Namespace), können Sie möglicherweise die Syntax '+' verwenden, um auf die Aufzählung in XAML zuzugreifen, wie in einer (nicht markierten) Antwort auf die Frage Nicht gefunden angegeben Aufzählungstyp für statische Referenz in WPF :

ConverterParameter = {x: Statisch lokal: YourClass + YourNestedEnumType.Enum1}

Aufgrund dieses Microsoft Connect- Problems lädt der Designer in VS2010 jedoch keine Angabe mehr "Type 'local:YourClass+YourNestedEnumType' was not found.", sondern das Projekt wird erfolgreich kompiliert und ausgeführt. Natürlich können Sie dieses Problem vermeiden, wenn Sie Ihren Aufzählungstyp direkt in den Namespace verschieben können.


Bearbeiten (27. Januar 12):

Wenn Enum-Flags verwendet werden, lautet der Konverter wie folgt:

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

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value.Equals(true) ? parameter : Binding.DoNothing;
    }
}

Bearbeiten (7. Mai 15):

Denken Sie im Fall einer nullfähigen Aufzählung (die in der Frage nicht gestellt wird, aber in einigen Fällen erforderlich sein kann, z. B. ORM, das null aus der Datenbank zurückgibt, oder wann immer es sinnvoll ist, dass der Wert in der Programmlogik nicht angegeben wird), daran, ihn hinzuzufügen eine anfängliche Nullprüfung in der Konvertierungsmethode und Rückgabe des entsprechenden Bool-Werts, der normalerweise falsch ist (wenn Sie kein Optionsfeld auswählen möchten), wie unten:

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) {
            return false; // or return parameter.Equals(YourEnumType.SomeDefaultValue);
        }
        return value.Equals(parameter);
    }

Hinweis - NullReferenceException (10. Oktober 18):

Das Beispiel wurde aktualisiert, um die Möglichkeit des Auslösens einer NullReferenceException zu entfernen. IsCheckedist ein nullbarer Typ, daher Nullable<Boolean>scheint die Rückgabe eine vernünftige Lösung zu sein.

Scott
quelle
26
Ich stimme zu, ich glaube, das ist eine bessere Lösung. Wenn Sie diese Konvertierung verwenden, wird das Projekt zur Kompilierungszeit und nicht zur Laufzeit unterbrochen, wenn die Aufzählungswerte geändert werden. Dies ist ein großer Vorteil.
CrimsonX
4
Dies ist sicherlich eine viel bessere Lösung als die akzeptierte. +1
OrPaz
7
Schöne Lösung. Ich würde hinzufügen, dass dies wirklich nur ein Vergleichskonverter ist, der 2 Werte vergleicht. Es könnte einen allgemeineren Namen haben als EnumToBooleanConverter wie ComparisonConverter
MikeKulls
5
@ Scott, sehr nett. Dieser Konverter ist in jedem Fall mit oder ohne Flags-Attribut gut. In den meisten Fällen wäre es jedoch albern, diesen Filter direkt als Konverter mit enum als Flags zu verwenden. Der Grund ist, dass Sie mit dem vorherigen Wert eine boolesche Berechnung (| = oder ^ =) erreichen sollten, um das richtige Ergebnis zu erhalten, der Konverter jedoch keinen Zugriff auf den vorherigen Wert hat. Sie sollten dann für jeden Aufzählungswert einen Bool hinzufügen und die richtige boolesche Berechnung selbst in Ihrem MVVM-Modell durchführen. Aber danke für jede Info, sehr nützlich.
Eric Ouellet
2
In Windows Phone 8 (möglicherweise im Fall von Win Store Apps) haben wir kein x: static, daher können wir die Lösung hier nicht direkt verwenden. Die IDE / Complier ist jedoch intelligent genug und sucht den String anhand aller String-Literale (meine Vermutung jedenfalls). zB funktioniert dies <RadioButton IsChecked = "{Binding TrackingMode, ConverterParameter = Driving, Converter = {StaticResource EnumToBooleanConverter}, Mode = TwoWay}" /> Tippfehler in Driving werden eher während der Entwurfs- / Kompilierungszeit als während der Laufzeit abgefangen.
Adarsha
26

Für die Antwort von EnumToBooleanConverter: Anstatt DependencyProperty.UnsetValue zurückzugeben, sollten Sie Binding.DoNothing für den Fall zurückgeben, dass der Wert des Optionsfelds IsChecked falsch wird. Ersteres weist auf ein Problem hin (und zeigt dem Benutzer möglicherweise ein rotes Rechteck oder ähnliche Validierungsindikatoren an), während letzteres lediglich darauf hinweist, dass nichts getan werden sollte, was in diesem Fall gewünscht wird.

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

anon
quelle
Es gibt keine Bindung. Nichts in Silverlight. Es ist nur WPF. Verwenden Sie stattdessen null.
Alexander Vasilyev
1
Bindung.Nichts ist auch von UWP weg.
BlackICE
5

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/

Martin Moser
quelle
Ich erhalte eine bidirektionale Bindung, die eine ähnliche Methode mit einer ListBox und DataTemplate ausführt.
Bryan Anderson
Dieser Fehler: geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspx hat einen Tag für mich ruiniert.
Slampen
3
Dies ist bei weitem die beste Lösung, alles andere verursacht redundanten Code. ( Ein weiteres Beispiel für die Verwendung einer ListBox)
HB
3

Für UWP ist es 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 3

Gültig nur für WPF!

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

UWP unterstützt dies nicht, x:Staticdaher kommt Beispiel 3 nicht in Frage. 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, warum es eingerahmt werden muss, ist, dass es anscheinend keine Möglichkeit gibt, auf den Typ in der ConvertBackMethode zu verweisen . Dafür sorgt das Boxen. Wenn Sie sich für eines der beiden ersten 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 möglichst geringer Ausführlichkeit erledigen 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.

James M.
quelle
1

Ich habe eine neue Klasse erstellt, um RadioButtons und CheckBoxes an Aufzählungen 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 mag zunächst komplizierter aussehen. Sobald Sie diese Klasse in Ihr Projekt kopiert haben, ist dies erledigt. Es ist generisch, so dass es leicht für jede Aufzählung wiederverwendet werden kann.

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 Aufzählung zum manuellen oder automatischen Ausführen einer Aufgabe, die für jeden Wochentag geplant werden kann, sowie 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 es, diese Klasse zu verwenden:

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 Ihre Auswahl zwischen "Manuell" oder "Automatisch" ändern. Eine davon muss jedoch immer 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" werden zunächst deaktiviert. Sie können das eine oder das andere überprüfen, indem Sie das eine aktivieren, wird das andere deaktiviert (ähnlich wie bei RadioButtons), aber jetzt können Sie auch beide deaktivieren (was Sie mit dem RadioButton von WPF nicht tun können, weshalb hier CheckBox verwendet wird).
Nick
quelle
Angenommen, Sie haben 3 Elemente in der StartTask-Enumeration, z. B. {Undefiniert, Manuell, Automatisch}. Sie möchten standardmäßig Undefiniert verwenden, da diese undefiniert sind, bis ein Benutzer einen Wert festlegt. Außerdem: Wie wird mit dem SelectedItem umgegangen? Ihr ViewModel hat keine SelectedStartTask.
user1040323
In meinem ViewModel ist die StartUp-Eigenschaft ein EnumSelection<StartTask>Objekt. Wenn Sie sich die Definition von EnumSelection<T>ansehen, können Sie sehen, dass sie eine Value-Eigenschaft hat. Das Ansichtsmodell muss also keine "SelectedStartTask" haben. Sie würden verwenden StartUp.Value. Wenn Sie den Standardwert "Undefiniert" haben, lesen Sie die dritte Aufzählung "AdditionalOptions". Sie enthält "None" anstelle von "Undefined". Sie können den Namen jedoch nach Belieben ändern.
Nick
1

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.

Ali Bayat
quelle
1
Ich sage Ihnen ein großes "DANKE" für den Gefallen, den Sie für mich getan haben. Es hat wie ein Zauber für mich funktioniert.
Elham Azadfar
0

Basierend auf dem EnumToBooleanConverter von Scott. Ich habe festgestellt, dass die ConvertBack-Methode in der Enum mit 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, eine Besetzung von intbis zu machen, targetTypealso habe ich es fest codiert NavigationProjectDates, die Aufzählung, die ich benutze. Und targetType == NavigationProjectDates...


Für allgemeinere Flags Enum-Konverter bearbeiten:

    öffentliche Klasse FlagsEnumToBooleanConverter: IValueConverter {
        private int _flags = 0;
        public object Convert (Objektwert, Typ targetType, Objektparameter, Zeichenfolgensprache) {
            if (value == null) gibt false zurück;
            _flags = (int) value;
            Typ t = value.GetType ();
            Objekt o = Enum.ToObject (t, Parameter);
            return ((Enum) value) .HasFlag ((Enum) o);
        }}

        öffentliches Objekt ConvertBack (Objektwert, Typ targetType, Objektparameter, Zeichenfolgensprache)
        {
            if (Wert? .Equals (true) ?? false) {
                _flags = _flags | (int) Parameter;
            }}
            sonst {
                _flags = _flags & ~ (int) parameter;
            }}
            return _flags;
        }}
    }}
KenGey
quelle
Jemand hatte meine Antwort bearbeitet, um sie mit dem Flags-Code hinzuzufügen. Ehrlich gesagt habe ich sie nie selbst ausprobiert / verwendet und überlegt, sie zu entfernen, da ich denke, dass sie als eigene Antwort sinnvoller ist. Wenn ich später etwas Zeit finde, kann ich versuchen, etwas zusammenzustellen, um diesen Code sowie das, was Sie haben, zu testen und vielleicht zu einer besseren Lösung für Ihr Problem beizutragen.
Scott
0

Sie können die Optionsfelder dynamisch erstellen ListBoxund dabei ganz einfach ohne Konverter helfen.

Die folgenden Schritte zum Erstellen sind: Erstellen Sie eine ListBox, legen Sie die ItemsSource für das Listbox als Aufzählung fest MyLovelyEnumund binden Sie das SelectedItem der ListBox an die VeryLovelyEnumEigenschaft. Anschließend werden die Optionsfelder für jedes ListBoxItem erstellt.

  • Schritt 1 : Fügen Sie die Aufzählung zu statischen Ressourcen für Ihr Fenster, UserControl oder Grid usw. hinzu.
    <Window.Resources>
        <ObjectDataProvider MethodName="GetValues"
                            ObjectType="{x:Type system:Enum}"
                            x:Key="MyLovelyEnum">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:MyLovelyEnum" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
  • Schritt 2 : Verwenden Sie das Listenfeld und Control Templatefüllen Sie jedes Element als Optionsfeld aus
    <ListBox ItemsSource="{Binding Source={StaticResource MyLovelyEnum}}" SelectedItem="{Binding VeryLovelyEnum, Mode=TwoWay}" >
        <ListBox.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <RadioButton
                                Content="{TemplateBinding ContentPresenter.Content}"
                                IsChecked="{Binding Path=IsSelected,
                                RelativeSource={RelativeSource TemplatedParent},
                                Mode=TwoWay}" />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.Resources>
    </ListBox>

Der Vorteil ist unten: Wenn sich Ihre Enum-Klasse eines Tages ändert, müssen Sie die GUI (XAML-Datei) nicht aktualisieren.

Referenzen: https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

Bravo Yeung
quelle