我有一个这样的enum:

public enum MyLovelyEnum
{
    FirstSelection,
    TheOtherSelection,
    YetAnotherOne
};

我在DataContext中有一个属性:

public MyLovelyEnum VeryLovelyEnum { get; set; }

我有三个电台按钮在我的WPF客户端。

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

现在,我如何绑定RadioButtons属性为适当的双向绑定?


当前回答

对于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,结果是更详细的代码。例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);
    }
}

然后,对于您希望支持的每一种类型,定义一个用于包装枚举类型的转换器。

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

它必须被装箱的原因是因为似乎没有办法在ConvertBack方法中引用该类型;拳击就能解决这个问题。如果您使用前两个例子中的任何一个,您可以只引用参数类型,消除了从盒装类继承的需要;如果您希望在一行中完成所有工作,并且尽可能少地冗长,则后一种解决方案是理想的。

用法类似于示例2,但实际上没有那么详细。

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

缺点是必须为希望支持的每种类型定义一个转换器。

其他回答

我将在ListBox中使用RadioButtons,然后绑定到SelectedValue。

这是一个关于这个主题的旧帖子,但基本思想应该是相同的:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/

您可以使用更通用的转换器

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部分中,您使用:

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

对于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,结果是更详细的代码。例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);
    }
}

然后,对于您希望支持的每一种类型,定义一个用于包装枚举类型的转换器。

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

它必须被装箱的原因是因为似乎没有办法在ConvertBack方法中引用该类型;拳击就能解决这个问题。如果您使用前两个例子中的任何一个,您可以只引用参数类型,消除了从盒装类继承的需要;如果您希望在一行中完成所有工作,并且尽可能少地冗长,则后一种解决方案是理想的。

用法类似于示例2,但实际上没有那么详细。

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

缺点是必须为希望支持的每种类型定义一个转换器。

你可以动态创建单选按钮,ListBox可以帮助你做到这一点,不需要转换器,非常简单。

具体步骤如下:

创建一个ListBox,并设置ListBox的ItemsSource为enum MyLovelyEnum,并将ListBox的SelectedItem绑定到VeryLovelyEnum属性。 然后为每个ListBoxItem创建单选按钮。

步骤1:将枚举添加到窗口、用户控件或网格等的静态资源中。

    <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:使用列表框和控制模板填充每个项目作为单选按钮

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

这样做的好处是:如果有一天你的枚举类改变了,你不需要更新GUI (XAML文件)。

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

UWP的双向绑定解决方案,需要使用Nullable:

c#的部分:

public class EnumConverter : IValueConverter
{
    public Type EnumType { get; set; }
    public object Convert(object value, Type targetType, object parameter, string lang)
    {
        if (parameter is string enumString)
        {
            if (!Enum.IsDefined(EnumType, value)) throw new ArgumentException("value must be an Enum!");
            var enumValue = Enum.Parse(EnumType, enumString);
            return enumValue.Equals(value);
        }
        return value.Equals(Enum.ToObject(EnumType,parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, string lang)
    {
        if (parameter is string enumString)
            return value?.Equals(true) == true ? Enum.Parse(EnumType, enumString) : null;
        return value?.Equals(true) == true ? Enum.ToObject(EnumType, parameter) : null;
    }
}

在这里,空值充当Binding.DoNothing。

private YourEnum? _yourEnum = YourEnum.YourDefaultValue; //put a default value here
public YourEnum? YourProperty
{
    get => _yourEnum;
    set{
        if (value == null) return;
        _yourEnum = value;
    }
}

Xaml“:

...
<Page.Resources>
    <ResourceDictionary>
        <helper:EnumConverter x:Key="YourConverter" EnumType="yournamespace:YourEnum" />
    </ResourceDictionary>
</Page.Resources>
...
<RadioButton GroupName="YourGroupName" IsChecked="{Binding Converter={StaticResource YourConverter}, Mode=TwoWay, Path=YourProperty, ConverterParameter=YourEnumString}">
    First way (parameter of type string)
</RadioButton>
<RadioButton GroupName="LineWidth">
    <RadioButton.IsChecked>
        <Binding
            Converter="{StaticResource PenWidthConverter}"
            Mode="TwoWay"   Path="PenWidth">
            <Binding.ConverterParameter>
                <yournamespace:YourEnum>YourEnumString</yournamespace:YourEnum>
            </Binding.ConverterParameter>
        </Binding>
    </RadioButton.IsChecked>
    Second way (parameter of type YourEnum (actually it was converted to int when passed to converter))
</RadioButton>