web-dev-qa-db-ja.com

ラジオボタンを列挙型にバインドする方法は?

私はこのような列挙型を持っています:

public enum MyLovelyEnum
{
  FirstSelection,
  TheOtherSelection,
  YetAnotherOne
};

DataContextにプロパティがあります:

public MyLovelyEnum VeryLovelyEnum { get; set; }

そして、WPFクライアントに3つのRadioButtonがありました。

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

次に、適切な双方向バインディングのためにRadioButtonをプロパティにバインドするにはどうすればよいですか?

387
Sam

より汎用的なコンバーターを使用できます

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
}

そして、XAML-Partで次を使用します。

<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>
368
Lars

EnumToBooleanConverter回答の場合:DependencyProperty.UnsetValueを返す代わりに、ラジオボタンのIsChecked値がfalseになった場合にBinding.DoNothingを返すことを検討してください。前者は問題を示し(ユーザーに赤い長方形または同様の検証インジケーターを表示する場合があります)、後者は何も実行すべきではないことを示すだけです。

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

ListBoxでRadioButtonsを使用し、SelectedValueにバインドします。

これはこのトピックに関する古いスレッドですが、基本的な考え方は同じです: http://social.msdn.Microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f- 8d99-fecf45522395 /

5
Martin Moser

UWPの場合、それほど単純ではありません。フィールド値をパラメーターとして渡すには、余分なフープをジャンプする必要があります。

例1

WPFとUWPの両方に有効です。

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

例2

WPFとUWPの両方に有効です。

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

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

WPFにのみ有効!

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

UWPはx:Staticをサポートしていないため、例3は問題外です。 例1を選択すると、結果はより冗長なコードになります。 例2はわずかに優れていますが、理想的ではありません。

ソリューション

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

次に、サポートするタイプごとに、enumタイプをボックス化するコンバーターを定義します。

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

ボックス化する必要があるのは、ConvertBackメソッドで型を参照する方法がないように見えるためです。ボクシングがそれを処理します。最初の2つの例のいずれかを使用する場合、パラメーター型を参照するだけで、ボックス化されたクラスから継承する必要がなくなります。すべてを1行で、可能な限り冗長にしたい場合は、後者のソリューションが理想的です。

使用法は例2に似ていますが、実際には冗長ではありません。

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

欠点は、サポートするタイプごとにコンバーターを定義する必要があることです。

3
James M

RadioButtonとCheckBoxを列挙型にバインドするための新しいクラスを作成しました。フラグ付き列挙型(複数のチェックボックスが選択されている)および単一選択型チェックボックスまたはラジオボタン用のフラグなし列挙型で機能します。また、ValueConvertersはまったく必要ありません。

これは最初はもっと複雑に見えるかもしれませんが、このクラスをプロジェクトにコピーすれば完了です。汎用なので、あらゆる列挙型に簡単に再利用できます。

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

そして、それを使用する方法については、手動または自動でタスクを実行するための列挙型があり、曜日、およびいくつかのオプションのオプションをスケジュールできるとしましょう...

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
}

ここで、このクラスを使用するのがどれほど簡単かを示します。

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

そして、このクラスでチェックボックスとラジオボタンを簡単にバインドできます:

<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. UIが読み込まれると、[手動]ラジオボタンが選択され、選択を[手動]または[自動]に変更できますが、いずれかを常に選択する必要があります。
  2. 曜日はすべてチェック解除されますが、それらの数はチェックまたはチェック解除できます。
  3. 「オプションA」と「オプションB」は両方とも最初はチェックされていません。どちらか一方をチェックすることができ、一方をチェックすると他方のチェックが解除されます(RadioButtonと同様)が、両方をオフにすることもできます(WPFのRadioButtonでは実行できないため、ここでCheckBoxが使用されています)
1
Nick

これはチェックボックスでも機能します。

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

単一の列挙型を複数のチェックボックスにバインドします。

1
Ali Bayat

ScottのEnumToBooleanConverterに基づいています。 ConvertBackメソッドは、フラグコードを含むEnumでは機能しないことに気付きました。

私は次のコードを試しました:

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

私が働けない唯一のことは、intからtargetTypeにキャストすることです。そのため、使用する列挙型であるNavigationProjectDatesにハードコーディングしました。そして、targetType == NavigationProjectDates...


より一般的なFlags Enumコンバーター用に編集します。

 public class FlagsEnumToBooleanConverter:IValueConverter {
 private int _flags = 0; 
 public object Convert(object value、Type targetType、object parameter、string language){
 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(object value、Type targetType、objectパラメータ、文字列言語)
 {
 if(value?.Equals(true)?? false){
 _flags = _flags | (int)パラメーター; 
} 
 else {
 _flags = _flags&〜(int)parameter; 
} 
 return _flags; 
} 
} 
0
KenGey