0

在我的应用程序(和我的学习曲线)中,我天真地来到了需要通过 ComboBox 在 GUI 中设置我的实体的属性的地步。具体来说,在Person表单中,有一个Gender字段,这个字段应该是一个ComboBox用户可以选择的地方MaleFemaleUnspecifiedoptions。

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

好吧,我首先想到“显然,我应该创建一个枚举,然后将该枚举用作Gender属性和ComboBox数据源的基石,以至于如果我更改枚举本身,我什至不需要更改类或PersonXAML 形式。

问题是:实现我所描述的应该简单使用的每个示例都非常扭曲,使用辅助类、 s、s 等等。考虑到“应该简单”的部分,这似乎很奇怪......enumValueConverterObjectProvider

所以,问题是:

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

谢谢阅读。


最终代码,按照 Sheridan 的答案转换为 Dictionary 而不是 IEnumerable:

在包含SelectedPerson.Gender属性的 ViewModel 中,Gender枚举在可用命名空间中的位置:

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

在 XAML 中:

<ComboBox 
    ItemsSource="{Binding Genders}"
    DisplayMemberPath="Value"
    SelectedValuePath="Key"
    SelectedValue="{Binding SelectedPerson.Gender, Mode=TwoWay}"/>

目前,这违反了我在问题中所说的“如果枚举更改,则无需更改类”,但是如果您有显示名称和 i18n,它们无论如何都必须更改,因此您必须“更新内容“无论如何,如果枚举发生变化。但是枚举不应该经常更改。

当然,如果不需要显示名称,那么Enum.GetNames方法就是要走的路。

4

3 回答 3

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选择将更新您的实体的该属性。

更新>>>

抱歉,我一定忽略了那个微小的细节......您可以使用该Enum.GetValues方法来迭代枚举值:

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

关于SelectedItem绑定到您的视图模型的属性,我确实说过可以用您的 'entity' 中的属性替换该属性。具体如何完成将取决于您如何设置数据类型和视图模型类,但我它会是这样的: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}" />
于 2013-10-14T13:51:23.543 回答
1

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

于 2013-10-14T13:51:23.650 回答
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));
    }
}

我已经定义了 person in 的一个属性MainWindow以避免这个例子的 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不是 aIEnumerable组合框期望的 a ItemsSource,我们需要一些可以采用 aEnum并枚举所有可用值的东西。这是通过ObjectDataProvider声明为资源的 - 在本例中为 App.xaml,但可以是加载的任何资源字典的一部分,因此组合可以进入其范围(当然,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>

现在 in 的使用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>

这样,我们在ObjectDataProvider每个应用程序和任何我们想要使用组合、列表框或任何项目控件的地方声明唯一一次来列出这个枚举的可用值,我们可以对ItemsSource属性进行相同的绑定。

于 2013-10-14T18:20:12.343 回答