none
如何绑定枚举类型和RadioButton组 RRS feed

  • 问题

  • 需求如下:

    三个RadioButton,分别对应枚举的三个枚举值。要将一个枚举变量与这三个RadioButton绑定,双向更新。

    查了一些资料,说是要进行类型转换,但是讲的都不详细。有没有比较熟悉这种绑定的高手给讲解下。

    虽然功能上不采用Binding也能实现,但是代码会显得比较暴力,我也不希望在WPF中使用这种方式。所以希望有人能给出WPF建议的方法。如果有别的好的方法也可以讲讲。

    2015年7月11日 14:36

答案

  • 抱歉我没表达清楚,意图是radio1选中,后台ee取值FUN;radio2选中,后台ee取值MSG;radio3选中,后台ee取值DIALOG;后台ee值变化时,前台radiobutton的状态也相应变化。

    我贴出来的代码先是使用了zq给的代码,没得到预期结果,然后没改工程,只换了Xavier Eoro函数体内容,自己在构造函数写了绑定,所以看起来比较混乱...

    根据Shi Xin给出的代码,添加后按照我自己的使用习惯,顺利得到结果,学习了!

        public class RadioButtonContentConverter : IValueConverter
        {
            public object Convert(object _Value, Type _TargetType, object _Parameter, CultureInfo _Culture)
            {
                return Enum.Equals(_Value, _Parameter);
            }
            public object ConvertBack(object _Value, Type _TargetType, object _Parameter, CultureInfo _Culture)
            {
                return (bool)_Value == true ? _Parameter : null;
            }
        }

    非常感谢各位!
    2015年7月17日 10:51

全部回复

  • 如果不是使用RadioButton,而使用ListBox的话,则可以比较方便的达到目的:

    <Window x:Class="WPF_Test.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Width="525" Name="Root" Height="350">
        <Grid>
            <ListBox Name="ListBox" ItemsSource="{Binding Data}" SelectedValue="{Binding SelectedSkill, Mode=TwoWay}" SelectedValuePath=""/>
            <StackPanel VerticalAlignment="Bottom" Orientation="Horizontal">
                <Button Name="ButtonCSharp" Click="ButtonCSharp_OnClick">ButtonCSharp</Button>
                <Button Name="ButtonDotNet" Click="ButtonDotNet_OnClick">ButtonDotNet</Button>
            </StackPanel>
        </Grid>
    </Window>

        public partial class MainWindow
        {
            private readonly ObservableCollection<Skill> _data;
    
            public ObservableCollection<Skill> Data
            {
                get
                {
                    return _data;
                }
            }
    
    
            public Skill SelectedSkill
            {
                get { return (Skill)GetValue(SelectedSkillProperty); }
                set { SetValue(SelectedSkillProperty, value); }
            }
            public static readonly DependencyProperty SelectedSkillProperty =
                DependencyProperty.Register("SelectedSkill", typeof(Skill), typeof(MainWindow), new PropertyMetadata(default(Skill)));
    
    
            public MainWindow()
            {
                InitializeComponent();
                var skills = Enum.GetValues(typeof(Skill)).Cast<Skill>();
                _data = new ObservableCollection<Skill>(skills);
                DataContext = this;
            }
    
    
            private void ButtonCSharp_OnClick(object sender, RoutedEventArgs e)
            {
                ListBox.Focus();
                SelectedSkill = Skill.CSharp;
            }
    
            private void ButtonDotNet_OnClick(object sender, RoutedEventArgs e)
            {
                ListBox.Focus();
                SelectedSkill = Skill.DotNet;
            }
        }
    
        public enum Skill
        {
            CSharp,
            DotNet,
            WindowsPresentationFoundation,
            VisualStudio
        }
    

    此例中:

    a. ObservableCollection<Skill> Data 是作为ListBox绑定源的集合,我将MainWindow的DataContext设置为自身,而其自身又定义了Data和SelectedSkill,所以ListBox便可以从上下文找到这些绑定源。

    你也可以使用任意的其他方法,只要能让ListBox能够获取内容集合,并且让ListBox的SelectedValue属性绑定到一个DependencyPropperty。

    b. ListBox的SelectedValuePath属性指示了ListBox如何在Skill类型中寻找值作为SelectedValue,这个属性对于复合类型比较有用。此例中我们需要的SelectedValue就是Skill类型自身,因此SelectedValuePath可以为空,也可以省略不写。

    c. 在设置SelectedSkill之前,即使不加Focus也是可以的。此例中则是因为点击按钮会使得按钮获得焦点,为了视觉效果添加的Focus。

    ps:如果你一定要RadioButton的视觉效果,那就需要更改ListBox的模板了。

    pss:绑定意味着两个类的某个属性之间具有链接关系并能够同步,这就要求这对属性类型相同。对于你的疑问:更新RadioButton状态的属性类型是bool?,而你的变量则是枚举类型,因此如果想要进行绑定的话,就需要类型转换。

    psss:RadioButton的Content属性并不会影响RadioButton的功能和状态,真正起作用的是IsChecked,绑定起来有意义的也只是IsChecked。

    2015年7月12日 2:02
  • UI布局:

    <Window x:Class="WpfApplication26.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:RadioButtonConverter="clr-namespace:WpfApplication26"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <RadioButtonConverter:RadioButtonContentConverter x:Key="RadioButtonConverter"/>
        </Window.Resources>
        <Grid>
            <RadioButton Width="120" Height="30" Name="RadioButton1" Content="{Binding ElementName=RadioButton1,Path=Name,Converter={StaticResource RadioButtonConverter}}" GroupName="0" Margin="198,111,199,179"></RadioButton>
    
            <RadioButton Width="120" Height="30" Name="RadioButton3" Content="{Binding ElementName=RadioButton2,Path=Name,Converter={StaticResource RadioButtonConverter}}" GroupName="0" Margin="198,180,199,110"></RadioButton>
    
            <RadioButton Width="120" Height="30" Name="RadioButton2" Content="{Binding ElementName=RadioButton3,Path=Name,Converter={StaticResource RadioButtonConverter}}" GroupName="0" Margin="198,259,199,31"></RadioButton>
        </Grid>
    </Window>


    后台:

    public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        public enum RadioButtonKind
        {
            Radio1 = 0,
            Radio2 = 1,
            Radio3 = 2,
        }
    
        public class RadioButtonContentConverter : IValueConverter
        {
            /// <summary>
            /// binding转化的实际方法
            /// </summary>
            /// <param name="value"></param>
            /// <param name="targetType"></param>
            /// <param name="parameter"></param>
            /// <param name="culture"></param>
            /// <returns></returns>
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                try
                {
                    var path = (string)value;
                    if (path.Equals("RadioButton1"))              // 如果path参数为“RadioButton1”,那么返回对应路径
                    {
                        return (RadioButtonKind)0;
                    }
                    else if (path.Equals("RadioButton2"))
                    {
                        return (RadioButtonKind)1;
                    }
                    else
                    {
                        return (RadioButtonKind)2;
                    }
                }
                catch                                    //err
                {
                    return (RadioButtonKind)0;
                }
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }


    具体你要做成什么样子,binding什么我不清楚,改改应该可以用,实在不行,搜搜Convert关键字

    2015年7月14日 2:27
  • Hi The Lake Shire,

    我对 zq1564171310 的回复做一点补充,正如zq所说,你需要自定义一个实现IValueConverter接口的转换类,并且实现其方法,在绑定时,将需要转换的枚举值通过你的转换方法显示到对应的RadioButton上,你可以参考下面的代码:

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

    Best Regards,

    Xavier Eoro


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    2015年7月14日 6:08
    版主
  • 非常感谢!

    如果我需要在后台进行动态绑定,使用上面提供的继承IValueConverter的类之后,在后台应该怎么做?

    我的前台呈现代码是:

                <RadioButton x:Name="radio1">FUN</RadioButton>
                <RadioButton x:Name="radio2">MSG</RadioButton>
                <RadioButton x:Name="radio3">DIALOG</RadioButton>

    后台有一个RadioButtonKind类型的枚举变量ee,枚举定义为:

        public enum RadioButtonKind
        {
            FUN,
            MSG,
            DIALOG
        }

    需要这三个radiobutton的ischecked分别对应ee的三个取值。

    我尝试使用以下代码来绑定,却无法得到预期结果(改变radiobutton的选择时,后台ee的值能对应改变,但是在后台改变ee的值,前台却不能做对应的改变:

            public event PropertyChangedEventHandler PropertyChanged;
            private RadioButtonKind ee;
            public RadioButtonKind EE {
                get
                {
                    return ee;
                }
                set
                {
                    ee = value;
                    if (PropertyChanged!=null)
                    {
                        PropertyChanged.Invoke(this, new PropertyChangedEventArgs("EE"));
                    }
                }
            }
    
            public MainWindow()
            {
                InitializeComponent();
    
                BindingOperations.SetBinding(radio1, RadioButton.IsCheckedProperty, new Binding("EE") { Source = this, Mode = BindingMode.TwoWay, Converter = new RadioButtonContentConverter(), ConverterParameter= "FUN" });
                BindingOperations.SetBinding(radio2, RadioButton.IsCheckedProperty, new Binding("EE") { Source = this, Mode = BindingMode.TwoWay, Converter = new RadioButtonContentConverter(), ConverterParameter = "MSG" });
                BindingOperations.SetBinding(radio3, RadioButton.IsCheckedProperty, new Binding("EE") { Source = this, Mode = BindingMode.TwoWay, Converter = new RadioButtonContentConverter(), ConverterParameter = "DIALOG" });
            }




    2015年7月16日 23:19
  • 我看你使用的Converter是RadioButtonContentConverter,所以你用的Converter代码是来自 zq1564171310 的吗?

    这里你要注意,zq1564171310提供的Converter类是没有实现ConvertBack的(可能因为不清楚你的需求),因此它并不支持从Enum转换为bool。

    所以你更改ee的时候Binding会尝试数据更新,但是该过程中调用ConvertBack失败了,没有获得对应的bool,因此Radio的状态没有改变。

    你可以尝试一下 Xavier Eoro 提供的Converter。

    ps: Shi Xin 提供的两个方案都非常详尽,你可以试试看。



    • 已编辑 Lymim 2015年7月17日 7:31 补充
    2015年7月17日 1:43
  • “需要这三个radiobutton的ischecked分别对应ee的三个取值”

    之前不知道你要binding什么,现在看来你想某一个radio被选中,让其ischecked属性值变为相应枚举,这个估计这个不是binding转化可以搞定的问题了,试想一下,ischeck=“FUN”,这个科学么?ischeck属性只能是bool类型或者null(本质还是false)。

    2015年7月17日 4:08
  • 抱歉我没表达清楚,意图是radio1选中,后台ee取值FUN;radio2选中,后台ee取值MSG;radio3选中,后台ee取值DIALOG;后台ee值变化时,前台radiobutton的状态也相应变化。

    我贴出来的代码先是使用了zq给的代码,没得到预期结果,然后没改工程,只换了Xavier Eoro函数体内容,自己在构造函数写了绑定,所以看起来比较混乱...

    根据Shi Xin给出的代码,添加后按照我自己的使用习惯,顺利得到结果,学习了!

        public class RadioButtonContentConverter : IValueConverter
        {
            public object Convert(object _Value, Type _TargetType, object _Parameter, CultureInfo _Culture)
            {
                return Enum.Equals(_Value, _Parameter);
            }
            public object ConvertBack(object _Value, Type _TargetType, object _Parameter, CultureInfo _Culture)
            {
                return (bool)_Value == true ? _Parameter : null;
            }
        }

    非常感谢各位!
    2015年7月17日 10:51