จะผูก RadioButtons กับ enum ได้อย่างไร


406

ฉันมี Enum เช่นนี้:

public enum MyLovelyEnum
{
    FirstSelection,
    TheOtherSelection,
    YetAnotherOne
};

ฉันได้รับทรัพย์สินใน DataContext ของฉัน:

public MyLovelyEnum VeryLovelyEnum { get; set; }

และฉันได้ RadioButtons สามตัวในไคลเอนต์ WPF ของฉัน

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

ตอนนี้ฉันจะผูก RadioButtons กับคุณสมบัติเพื่อผูกสองทางที่เหมาะสมได้อย่างไร


3
หากคุณกำลังมองหาที่จะทำเช่นนี้โดยไม่ได้ระบุ radiobuttons ในแต่ละ XAML ของคุณผมจะแนะนำกล่องรายการที่ถูกผูกไว้กับค่า enum เช่นนี้หรือนี้และที่มีแม่แบบรายการที่เขียนทับเพื่อ radiobuttons การใช้งานเช่นนี้
Rachel

คำตอบ:


389

คุณสามารถใช้ตัวแปลงทั่วไปมากขึ้น

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>

51
ทำงานเหมือนเสน่ห์สำหรับฉัน ในฐานะที่เป็นนอกจากนี้ฉันปรับเปลี่ยน ConvertBack เพื่อกลับ UnsetValue ใน "false" เพราะ Silverlight (และเหมาะสม WPF) เรียกตัวแปลงสองครั้ง - เมื่อยกเลิกการตั้งค่าปุ่มตัวเลือกเก่าและอีกครั้งเพื่อตั้งค่าใหม่ ฉันกำลังแขวนสิ่งอื่น ๆ ออกจากตัวตั้งค่าคุณสมบัติดังนั้นฉันต้องการเพียงเรียกมันครั้งเดียว - ถ้า (parameterString == null || value.Equals (false)) ส่งคืน DependencyProperty.UnsetValue;
MarcE

8
จากสิ่งที่ฉันสามารถบอกได้สิ่งนี้จะต้องทำเว้นแต่ว่าปุ่มตัวเลือกจะอยู่ในกลุ่มที่แตกต่างกัน (และปุ่ม AFAIK โดยไม่มีการตั้งค่า GroupName ที่มีพาเรนต์เดียวกันนั้นเป็นค่าเริ่มต้นในกลุ่มเดียวกัน) มิฉะนั้นการโทรเพื่อตั้งค่าคุณสมบัติ "ตีกลับ" และส่งผลให้เกิดพฤติกรรมที่แปลก
nlawalker

2
ใช่ แต่ถ้าคุณเรียก Unset ในตัวแปลงเมื่อตั้งค่าเป็นเท็จแสดงว่าไม่ใช่ EnumToBooleanConverter จริง แต่เป็น EnumToRadioButtonConverter ดังนั้นฉันจะตรวจสอบว่าค่านั้นแตกต่างกันใน setter ของฉันหรือไม่: ถ้า (_myEnumBackingField == value) กลับมา;
Stéphane

8
การเชื่อมโยงโซลูชันนี้ทำงานได้อย่างถูกต้องทางเดียวเท่านั้น ฉันไม่สามารถสลับ radiobutton โดยทางโปรแกรมโดยการกำหนดคุณสมบัติ binded ให้เป็นค่าอื่น หากคุณต้องการวิธีการทำงานที่เหมาะสมและดีกว่าให้ใช้แนวทางของสกอตต์
l46kok

2
@Marc ไม่ใช่สิ่งที่เหมาะสมที่จะส่งคืนในกรณีนั้น 'Binding.DoNothing' และไม่ใช่ 'DependencyProperty.UnsetValue'
Mark A. Donohoe

559

คุณสามารถทำให้คำตอบที่ยอมรับได้ง่ายขึ้น แทนที่จะพิมพ์ enums เป็นสตริงใน xaml และทำงานในตัวแปลงของคุณได้มากกว่าที่ต้องการคุณสามารถส่งผ่านค่า enum แทนการแสดงสตริงได้และอย่างที่ CrimsonX แสดงความคิดเห็นข้อผิดพลาดเกิดขึ้นในเวลารวบรวมมากกว่ารันไทม์:

ConverterParameter = {x: static local: 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>

จากนั้นทำการแปลงให้ง่ายขึ้น:

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

แก้ไข (16 ธันวาคม '10):

ขอบคุณ anon สำหรับการแนะนำการส่งคืน Binding.DoNothing มากกว่า DependencyProperty.UnsetValue


หมายเหตุ - RadioButtons หลายกลุ่มในที่เก็บเดียวกัน (17 ก.พ. 11):

ใน xaml หากปุ่มตัวเลือกใช้คอนเทนเนอร์หลักร่วมกันการเลือกหนึ่งรายการจะเป็นการยกเลิกการเลือกรายการอื่น ๆ ทั้งหมดภายในคอนเทนเนอร์นั้น (แม้ว่าจะถูกผูกไว้กับคุณสมบัติอื่น) ดังนั้นพยายามเก็บ RadioButton ของคุณที่ถูกผูกไว้กับคุณสมบัติทั่วไปจัดกลุ่มเข้าด้วยกันในภาชนะของตัวเองเช่นแผงสแต็ค ในกรณีที่ RadioButton ที่เกี่ยวข้องของคุณไม่สามารถแชร์คอนเทนเนอร์หลักเดียวจากนั้นตั้งค่าคุณสมบัติ GroupName ของแต่ละ RadioButton เป็นค่าทั่วไปเพื่อจัดกลุ่มตามหลักเหตุผล


แก้ไข (5 เม.ย. '11):

ตัวย่อ ConvertBack จะเป็น if-else เพื่อใช้ Ternary Operator


หมายเหตุ - ประเภท Enum ซ้อนกันในชั้นเรียน (28 เม.ย. 54):

ถ้าชนิด enum ของคุณจะซ้อนกันในชั้นเรียน (มากกว่าโดยตรงใน namespace) คุณอาจจะสามารถใช้ไวยากรณ์ '+' เพื่อเข้าถึง enum ใน XAML ตามที่ระบุไว้ใน (ไม่ได้ทำเครื่องหมาย) คำตอบของคำถามไม่พบ ประเภท enum สำหรับการอ้างอิงแบบคงที่ใน WPF :

ConverterParameter = {x: static local: YourClass + YourNestedEnumType.Enum1}

อย่างไรก็ตามเนื่องจากMicrosoft Connect Issueนี้ผู้ออกแบบใน VS2010 จะไม่โหลดอีกต่อไป"Type 'local:YourClass+YourNestedEnumType' was not found."แต่โครงการรวบรวมและทำงานได้สำเร็จ แน่นอนคุณสามารถหลีกเลี่ยงปัญหานี้ได้หากคุณสามารถย้ายประเภท enum ของคุณไปยังเนมสเปซได้โดยตรง


แก้ไข (27 ม.ค. 12):

หากใช้ค่าสถานะ Enum ตัวแปลงจะเป็นดังนี้:

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

แก้ไข (7 พฤษภาคม '15):

ในกรณีของ Enum Nullable (ที่ไม่ได้ถามในคำถาม แต่สามารถต้องการได้ในบางกรณีเช่น ORM คืนค่า null จาก DB หรือเมื่อใดก็ตามที่ทำให้รู้สึกว่าในตรรกะของโปรแกรมไม่มีค่า) อย่าลืมเพิ่ม การตรวจสอบ null ครั้งแรกในวิธีการแปลงและส่งกลับค่าบูลที่เหมาะสมซึ่งโดยทั่วไปจะเป็นเท็จ (หากคุณไม่ต้องการให้เลือกปุ่มตัวเลือก) ดังด้านล่าง:

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

หมายเหตุ - NullReferenceException (10 ต.ค. 18):

อัปเดตตัวอย่างเพื่อลบความเป็นไปได้ในการขว้าง NullReferenceException IsCheckedเป็นประเภท nullable ดังนั้นการส่งคืนจึงNullable<Boolean>น่าจะเป็นทางออกที่สมเหตุสมผล


26
ฉันเห็นด้วยฉันเชื่อว่านี่เป็นทางออกที่ดีกว่า นอกจากนี้การใช้การแปลงนี้จะทำให้โครงการหยุดพักในเวลารวบรวมไม่ใช่เวลาทำงานหากมีการเปลี่ยนแปลงค่าการแจงนับซึ่งเป็นข้อได้เปรียบใหญ่
CrimsonX

4
นี่เป็นทางออกที่ดีกว่าการยอมรับอย่างแน่นอน +1
OrPaz

7
ทางออกที่ดี ฉันจะเพิ่มว่านี่เป็นเพียงแปลงเปรียบเทียบการเปรียบเทียบ 2 ค่า มันอาจมีชื่อสามัญมากกว่า EnumToBooleanConverter เช่น ComparisonConverter
MikeKulls

5
@ Scott ดีมาก ตัวแปลงนี้ใช้ได้ดีในทุกกรณีโดยมีหรือไม่มีแอตทริบิวต์ Flags แต่ในกรณีส่วนใหญ่จะใช้ตัวกรองนี้โดยตรงเป็นตัวแปลงที่มี enum เป็นค่าสถานะ เหตุผลก็คือคุณควรบรรลุ boolean calc (| = หรือ ^ =) ด้วยค่าก่อนหน้าเพื่อให้ได้ผลลัพธ์ที่เหมาะสม แต่ตัวแปลงไม่สามารถเข้าถึงค่าก่อนหน้า จากนั้นคุณควรเพิ่มบูลสำหรับค่า enum แต่ละค่าและทำบูลีนที่เหมาะสมให้กับตัวเองในรูปแบบ MVVM ของคุณ แต่ขอขอบคุณสำหรับทุกข้อมูลที่มีประโยชน์มาก
Eric Ouellet

2
ใน Windows Phone 8 (อาจเป็นในกรณีของ Win Store Apps) เราไม่มี x: คงที่ดังนั้นเราจึงไม่สามารถใช้วิธีแก้ปัญหาได้โดยตรงที่นี่ อย่างไรก็ตาม IDE / Complier นั้นฉลาดพอและค้นหาสตริงกับตัวอักษรของสตริงทั้งหมด (ฉันเดาต่อไป) เช่นนี้ใช้งานได้ <RadioButton IsChecked = "{Binding TrackingMode, ConverterParameter = การขับรถ, Converter = {StaticResource EnumToBooleanConverter}, Mode = TwoWay}" /> ความผิดพลาดใด ๆ ในการขับขี่จะถูกจับในระหว่างการออกแบบ / รวบรวมมากกว่าเวลาทำงาน
Adarsha

26

สำหรับคำตอบ EnumToBooleanConverter: แทนที่จะส่งคืน DependencyProperty.UnsetValue ให้พิจารณาส่งคืน Binding.DoNothing สำหรับกรณีที่ปุ่มตัวเลือก IsChecked มีค่าเป็นเท็จ อดีตบ่งบอกถึงปัญหา (และอาจแสดงให้ผู้ใช้เห็นสี่เหลี่ยมสีแดงหรือตัวบ่งชี้การตรวจสอบที่คล้ายกัน) ในขณะที่หลังเพิ่งระบุว่าไม่ควรทำสิ่งใดซึ่งเป็นสิ่งที่ต้องการในกรณีนั้น

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


ไม่มีการเชื่อมใด ๆ ไม่มีใน Silverlight เป็น WPF เท่านั้น ใช้ null แทน
Alexander Vasilyev

1
การผูกไม่มีอะไรจะหายไปจาก UWP
BlackICE

5

ฉันจะใช้ RadioButtons ในกล่องรายการแล้วผูกเข้ากับค่าที่เลือก

นี่เป็นเธรดที่เก่ากว่าเกี่ยวกับหัวข้อนี้ แต่แนวคิดพื้นฐานควรเหมือนกัน: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/


ฉันได้รับการผูกสองทางทำวิธีที่คล้ายกันโดยใช้กล่องรายการและ DataTemplate ดังนั้นคุณควร
ไบรอันแอนเดอร์สัน

ข้อผิดพลาดนี้: geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspxทำลายวันให้ฉัน
Slampen

3
นี่คือทางออกที่ดีที่สุดทุกสิ่งที่ทำให้รหัสซ้ำซ้อน ( อีกตัวอย่างหนึ่งของการใช้กล่องรายการ)
HB

3

สำหรับ 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}}"/>

ตัวอย่างที่ 3

ใช้ได้กับ WPF เท่านั้น!

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

UWP ไม่สนับสนุนx:Staticดังนั้นตัวอย่างที่ 3ไม่ตอบคำถาม สมมติว่าคุณไปกับตัวอย่างที่ 1ผลที่ได้คือรหัส verbose เพิ่มเติม ตัวอย่างที่ 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แต่อันที่จริงแล้วมีความละเอียดน้อยกว่า

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

ข้อเสียคือคุณต้องกำหนดตัวแปลงสำหรับแต่ละประเภทที่คุณต้องการสนับสนุน


1

ฉันได้สร้างคลาสใหม่เพื่อจัดการ RadioButtons และ CheckBoxes เพื่อ enums มันใช้งานได้สำหรับ enums ที่ถูกตั้งค่าสถานะ (พร้อมการเลือกหลายช่อง) และ Enums ที่ไม่ถูกตั้งค่าสถานะสำหรับช่องทำเครื่องหมายเลือกเดียวหรือปุ่มตัวเลือก นอกจากนี้ยังไม่ต้องใช้ ValueConverters เลย

สิ่งนี้อาจดูซับซ้อนในตอนแรกอย่างไรก็ตามเมื่อคุณคัดลอกคลาสนี้ลงในโครงการของคุณเสร็จแล้ว เป็นแบบทั่วไปจึงสามารถนำกลับมาใช้ใหม่ได้อย่างง่ายดายสำหรับ enum ใด ๆ

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

และสำหรับวิธีการใช้งานสมมติว่าคุณมี enum สำหรับการทำงานด้วยตนเองหรือโดยอัตโนมัติและสามารถกำหนดเวลาสำหรับวันใดก็ได้ในสัปดาห์และตัวเลือกเสริมบางอย่าง ...

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" จะไม่ถูกตรวจสอบตั้งแต่แรก คุณสามารถตรวจสอบอย่างใดอย่างหนึ่งการตรวจสอบอย่างใดอย่างหนึ่งจะยกเลิกการเลือกอื่น ๆ (คล้ายกับ RadioButtons) แต่ตอนนี้คุณสามารถยกเลิกการเลือกพวกเขาทั้งสอง (ซึ่งคุณไม่สามารถทำได้กับ RadioButton ของ WPF ซึ่งเป็นเหตุผลว่าทำไม

สมมติว่าคุณมี 3 รายการใน Enum StartTask เช่น {Undefined, Manual, Automatic} คุณต้องการที่จะตั้งค่าเริ่มต้นเป็น Undefined เนื่องจากผู้ใช้ตั้งค่าที่ไม่ได้กำหนด ยัง: SelectedItem จัดการอย่างไร? ViewModel ของคุณไม่มีรายการที่เลือกเริ่มต้น
user1040323

ใน ViewModel ของฉันคุณสมบัติ StartUp เป็นEnumSelection<StartTask>วัตถุ ถ้าคุณดูคำจำกัดความของEnumSelection<T>คุณจะเห็นว่ามันมีคุณสมบัติค่า ดังนั้นโมเดลมุมมองจึงไม่จำเป็นต้องมี "SelectedStartTask" StartUp.Valueคุณจะใช้ และสำหรับการมีค่าเริ่มต้นเป็นไม่ได้กำหนดให้ดู enum ที่ 3, ExtraOptions มันมีไม่มีแทนการไม่ได้กำหนด แต่คุณสามารถเปลี่ยนชื่อมันเป็นสิ่งที่คุณต้องการ
Nick

1

ใช้งานได้กับช่องทำเครื่องหมายนี้ด้วย

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

ผูก Enum เดียวกับหลายช่องทำเครื่องหมาย


1
ฉันพูดคำขอบคุณกับคุณเพื่อความโปรดปรานที่คุณทำเพื่อฉัน มันใช้งานได้เหมือนเสน่ห์สำหรับฉัน
Elham Azadfar

0

อิง EnumToBooleanConverter จากสกอตต์ ฉันสังเกตเห็นว่าวิธีการแปลงกลับไม่ทำงานบน 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ดังนั้นฉันจึงทำให้มันยากที่จะNavigationProjectDatesenum ที่ฉันใช้ และtargetType == NavigationProjectDates...


แก้ไขสำหรับตัวแปลง Enum ทั่วไปเพิ่มเติม:

    คลาสสาธารณะ FlagsEnumToBooleanConverter: IValueConverter {
        ส่วนตัว int _flags = 0;
        การแปลงวัตถุสาธารณะ (ค่าวัตถุ, ประเภท targetType, พารามิเตอร์วัตถุ, ภาษาสตริง) {
            ถ้า (value == null) คืนค่าเท็จ
            ค่า _flags = (int);
            พิมพ์ t = value.GetType ();
            วัตถุ o = Enum.ToObject (t, พารามิเตอร์);
            return ((Enum) ค่า) .HasFlag ((Enum) o);
        }

        วัตถุสาธารณะ ConvertBack (ค่าวัตถุ, ประเภท targetType, พารามิเตอร์วัตถุ, ภาษาสตริง)
        {
            if (value? .Equals (true) ?? false) {
                _flags = _flags | (int) พารามิเตอร์
            }
            อื่น {
                พารามิเตอร์ _flags = _flags & ~ (int);
            }
            _flags กลับ
        }
    }

มีใครบางคนแก้ไขคำตอบของฉันเพื่อเพิ่มในด้วยรหัสธงดังนั้นจริงๆแล้วฉันไม่เคยลอง / ใช้มันด้วยตัวเองและคิดว่าจะลบมันออกไปเพราะฉันคิดว่ามันสมเหตุสมผลกว่าเป็นคำตอบของมันเอง หากฉันสามารถหาเวลาต่อมาฉันสามารถลองและรวบรวมบางสิ่งเพื่อทดสอบโค้ดนั้นรวมถึงสิ่งที่คุณมีและอาจช่วยแก้ไขปัญหาของคุณได้ดีขึ้น
สกอตต์

0

คุณสามารถสร้างปุ่มตัวเลือกแบบไดนามิกListBoxสามารถช่วยให้คุณทำเช่นนั้นได้โดยไม่ต้องใช้ตัวแปลงง่ายๆ

ขั้นตอนที่เกี่ยวข้องกันอยู่ด้านล่าง: สร้างกล่องรายการและตั้งค่าแหล่งข้อมูลสำหรับกล่องรายการเป็น enum MyLovelyEnumและผูกกับรายการที่เลือกของกล่องรายการให้มีVeryLovelyEnumคุณสมบัติ จากนั้นปุ่มตัวเลือกสำหรับแต่ละกล่องรายการจะถูกสร้างขึ้น

  • ขั้นตอนที่ 1 : เพิ่ม enum ให้กับสแตติกรีซอร์สสำหรับ Window, UserControl หรือ Grid เป็นต้น
    <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>
  • ขั้นตอนที่ 2 : ใช้กล่องรายการและControl Templateเติมแต่ละรายการภายในเป็นปุ่มตัวเลือก
    <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>

ประโยชน์อยู่ด้านล่าง: ถ้าสักวันหนึ่งการเปลี่ยนแปลงระดับ enum ของคุณคุณไม่จำเป็นต้องปรับปรุง GUI (ไฟล์ XAML)

ข้อมูลอ้างอิง: https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.