无论如何,ComboBox应该如何与Enums一起使用?

时间:2013-10-14 13:41:27

标签: wpf xaml data-binding combobox

我无辜地在我的应用程序(以及我的学习曲线)中找到了我需要通过ComboBox在GUI中设置实体属性的地方。具体来说,在Person表单中,有一个Gender字段,此字段应为ComboBox,用户可在其中选择MaleFemale和{ {1}}选项。

当然,这个ComboBox必须是TwoWay数据绑定到Unspecified ViewModel的Gender属性。

好吧,我首先想到“显然,我应该创建一个枚举,然后使用该枚举作为Person属性和Gender数据源的基石,如果我更改枚举本身,我甚至不需要更改ComboBox类或XAML格式。

问题是:EVERY example实现我所描述的Person应该简单的使用是非常扭曲的,使用辅助类,enum s,{{1}等等。考虑到“应该是简单的”部分,这似乎很奇怪......

所以,问题是:

  • “WPF DESIGN中的ComboBox首先与Enums一起使用吗?或者这个显而易见的选择实际上是一种人为约束,使事情变得比它们应该更复杂?”
  • “如果没有使用Enum,那么WPF中的ComboBox应该如何使用。对于将一堆值双向数据绑定到ViewModel属性的明显应用,使用它的规范方法是什么?”

感谢阅读。


最终代码,在Sheridan的回答转换为字典而不是IEnumerable之后:

在ViewModel中,其中包含ValueConverter属性,其中ObjectProvider枚举位于可用的命名空间中:

SelectedPerson.Gender

在XAML中:

Gender

现在,这违反了我在问题中所说的内容,“如果枚举更改,则无需更改课程”,但如果您有显示名称和i18n,则无论如何都必须更改,因此您必须如果枚举改变,无论如何都要“更新东西”。但是枚举不应该经常改变。

当然,如果不需要显示名称,那么 // this won't be set, so only getter needed, I think // Using dictionary as a placeholder for i18n implementation. public Dictionary<Gender, String> Genders { get { return new Dictionary<Gender,string> { {Gender.Unspecified, "Não especificado"}, {Gender.Female, "Feminino"}, {Gender.Male, "Masculino"} }; } } 方式就可以了。

3 个答案:

答案 0 :(得分:2)

enum实例用作WPF中任何集合控件中的项目是完全可以接受的。这是一个简单的例子来说明:

public enum Gender
{
    Male, Female
} 

private ObservableCollection<Gender> genders = new ObservableCollection<Gender>() 
    { Gender.Male, Gender.Female };

public ObservableCollection<Gender> Genders
{
    get { return genders; }
    set { genders = value; NotifyPropertyChanged("Genders"); }
}

private Gender selectedGender = Gender.Female;

public Gender SelectedGender
{
    get { return selectedGender; }
    set { selectedGender = value; NotifyPropertyChanged("SelectedGender"); }
}

<ComboBox ItemsSource="{Binding Genders}" SelectedItem="{Binding SelectedGender}" />

此处,SelectedGender属性可以在调用时替换为“实体”中的属性。这样,设置ComboBox选项将更新您实体的该属性。

更新&gt;&gt;&gt;

对不起,我一定忽略了那个微小的细节......你可以使用Enum.GetValues方法来迭代枚举值:

Array values = Enum.GetValues(typeof(Gender));
Genders = new ObservableCollection<Gender>(values.OfType<Gender>());

关于绑定到您的视图模型的SelectedItem属性,我 SelectedGender属性可以替换为您的'实体'中的属性。具体如何完成将取决于您如何设置数据类型和查看模型类,但我想象它会是这样的:

在您的视图模型中:

private ObservableCollection<Gender> genders = new ObservableCollection<Gender>();

public ObservableCollection<Gender> Genders
{
    get { return genders; }
    set { genders = value; NotifyPropertyChanged("Genders"); }
}

private YourDataObjectType yourDataObject = new YourDataObjectType();

public YourDataObjectType YourDataObject 
{
    get { return yourDataObject; }
    set { yourDataObject = value; NotifyPropertyChanged("YourDataObject"); }
}

然后在XAML中:

<ComboBox ItemsSource="{Binding Genders}" 
    SelectedItem="{Binding YourDataObject.YourGenderProperty}" />

答案 1 :(得分:1)

大多数情况下,当我在wpf中使用组合框时,将字典作为itemssource,其中字典条目的值部分是我显示的用户友好文本(DisplayMember),键值绑定到我的viewmodel。

答案 2 :(得分:0)

[虽然它已经回答了,但我希望展示'XAML友好'的方式]

在数据上下文中使用集合会导致需要选择相同enum值的每个视图模型的复制代码,即,如果您有多个视图模型,其中包含要编辑的Person对象 - 每个视图模型都需要定义,初始化和填充集合。或者,或者,需要更改继承拓扑。

我们定义GenderPerson

public enum Gender
{
    Female,
    Male,
    Unspecified
}
public class Person : INotifyPropertyChanged
{
    private Gender _gender = Gender.Unspecified;

    public Gender Gender
    {
        get { return _gender; }
        set
        {
            if (value == _gender) return;
            _gender = value;
            OnPropertyChanged();
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

我在MainWindow中定义了person的属性以避免此示例的MVVM开销,但如果有一个持有Person属性的视图模型,它将非常相似(仅限) DockPanel不会在xaml文件中设置数据上下文)

public partial class MainWindow
{
    #region ActivePerson

    /// <summary>
    /// Gets or sets an instance of <see cref="Person"/> object to use for testing.
    /// </summary>
    public Person ActivePerson
    {
        get { return (Person) GetValue(ActivePersonProperty); }
        set { SetValue(ActivePersonProperty, value); }
    }

    /// <summary>
    /// Identifies the <see cref="ActivePerson"/> property.
    /// </summary>
    public static readonly DependencyProperty ActivePersonProperty =
        DependencyProperty.Register("ActivePerson", typeof (Person), typeof (MainWindow), new UIPropertyMetadata(null));

    #endregion

    public MainWindow()
    {
        ActivePerson = new Person();
        InitializeComponent();
    }
}

由于Enum不是组合框所期望的IEnumerable ItemsSource,我们需要一些可以Enum并枚举所有可用值的内容。这是通过声明为资源的ObjectDataProvider完成的 - 在本例中是在App.xaml中,但可以是任何加载的资源字典的一部分,这样组合就可以到达其范围(当然,{{1} }或Window.Resources是有效选项)

UserControl.Resorces

现在<Application x:Class="EnimObjectProvider.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:enumObjectProvider="clr-namespace:EnumObjectProvider" StartupUri="MainWindow.xaml"> <Application.Resources> <ObjectDataProvider x:Key="Genders" ObjectType="{x:Type system:Enum}" MethodName="GetValues"> <ObjectDataProvider.MethodParameters> <system:Type>enumObjectProvider:Gender</system:Type> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </Application.Resources> </Application> 中的使用很简单:

MainWindow

这样我们只针对每个应用程序声明<Window x:Class="EnimObjectProvider.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <DockPanel DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Margin="20"> <ComboBox DockPanel.Dock="Top" ItemsSource="{Binding Source={StaticResource Genders}}" SelectedItem="{Binding ActivePerson.Gender}" /> <!--This text block is for testing of the Gender property of ActivePerson--> <TextBlock Text="{Binding ActivePerson.Gender, StringFormat='Current gender is: {0}'}" HorizontalAlignment="Center" VerticalAlignment="Center" /> </DockPanel> </Window> 一次,并且我们想要使用组合,列表框或任何项目控件来列出此枚举的可用值,我们可以对{具有相同的绑定{1}}属性。