将ComboBox绑定到嵌套在类中的枚举

时间:2009-08-02 23:42:13

标签: wpf class combobox enums nested

我一直在疯狂地将组合框绑定到类的枚举类型属性,其中枚举本身在同一个类中声明。

我正在尝试按照此处提供的答案(wpf combobox binding to enum what i did wrong?)具体而言,我使用建议的MarkupExtension代码和匹配的xaml代码。

我的工作代码是:

在单独的文件中定义枚举。

namespace EnumTest
{
    public enum TestEnum {one, two, three, four };
}

使用Enum的类(请注意,已删除propertyChanged代码以简化操作):

namespace EnumTest
{
    public class Test : INotifyPropertyChanged
    {
        private TestEnum _MyVar;
        public TestEnum MyVar { 
            get { return _MyVar; } 
            set 
            { 
                _MyVar = value;
                OnPropertyChanged("MyVar");
            } 
        }

        public Test()
        {
            _MyVar = TestEnum.three;
        }
    }
}

使用该类的程序文件:

namespace EnumTest
{
    public partial class Window1 : Window
    {
        Test _oTest = new Test();

        public Window1()
        {
            InitializeComponent();
            cmbBox.DataContext = _oTest;
        }
    }
 }

显示枚举的扩展方法

namespace EnumTest
{
    [MarkupExtensionReturnType(typeof(object[]))]
    public class EnumValuesExtension : MarkupExtension
    {
        public EnumValuesExtension()
        {
        }

        public EnumValuesExtension(Type enumType)
        {
            this.EnumType = enumType;
        }

        [ConstructorArgument("enumType")]
        public Type EnumType { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (this.EnumType == null)
                throw new ArgumentException("The enum type is not set");
            return Enum.GetValues(this.EnumType);
        }
    }
}

用于显示数据的xaml代码:

<Window x:Class="EnumTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:w="clr-namespace:EnumTest"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <ComboBox Name="cmbBox" 
                  Height="20" 
                  Width="80" 
                  ItemsSource="{Binding Source={w:EnumValues EnumType=w:TestEnum}}" 
                  SelectedItem="{Binding Path=MyVar}"
                  />
    </Grid>
</Window>

以上都是好的和花花公子,但我想在Test类中定义Enum 并且放弃Enum在全局范围内的定义。像这样:

namespace EnumTest
{
    public class Test : INotifyPropertyChanged
    {
        // Declare Enum **INSIDE** the class
        public enum TestEnum {one, two, three, four };
        private TestEnum _MyVar;
        public TestEnum MyVar { 
            get { return _MyVar; } 
            set 
            { 
                _MyVar = value;
                OnPropertyChanged("MyVar");
            } 
        }

        public Test()
        {
            _MyVar = TestEnum.three;
        }
    }
}

我提到的SO问题暗示匹配的xaml语法为:

        <ComboBox Name="cmbBox" 
                  ...
                  ItemsSource="{Binding Source={w:EnumValues EnumType=w:Test+TestEnum}}" 
                  ...
                  />

但这种(某种)对我不起作用。当我执行干净的构建时,我在VS 2008状态栏上收到“构建成功”消息,但我也在xaml中报告了错误

Type 'Test+TestEnum' was not found.  

但代码按预期运行!

但是这意味着xaml设计器不会加载。所以我有点不再做更多的wpf工作,直到我能清除xaml错误。

我现在想知道这是否是VS 2008 SP1问题,而不是我的问题。

修改

  1. 使我的问题陈述更明确。
  2. 尝试了Joel的第一个解决方案,但最终我的代码运行 2 xaml errors
  3. 尝试了乔尔的第二个解决方案,并且开箱即用 - 所以我要去那个!

  4. 备注 我得到MarkupExtension代码的SO问题是使用xaml的这种语法:

    <ComboBox ItemsSource="{w:EnumValues w:TestEnum}"/>
    

    当我使用它时,我得到一个编译错误,说没有EnumValues构造函数需要1个参数。我做了一些谷歌搜索,这似乎是VS中的一个错误。我正在使用VS 2008 SP1。我确实看到一些评论暗示它出现在VS 2010测试版中。无论如何,这就是我使用

    的xaml语法的原因
    <ComboBox ItemsSource="{w:EnumValues EnumType=w:TestEnum}"/>
    

    因为这种语法有效!

2 个答案:

答案 0 :(得分:3)

获取枚举值以用作数据源的另一种方法:

<Window.Resources>
    <ObjectDataProvider
        MethodName="GetValues"
        ObjectType="{x:Type sys:Enum}"
        x:Key="TestValues">
        <ObjectDataProvider.MethodParameters>
            <w:Type2
                TypeName="w:Test+TestEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

...

ItemsSource="{Binding Source={StaticResource TestValues}}"

请注意,由于Type2Extension和嵌套类型存在奇怪现象,您仍然需要TypeExtension。但是您不需要额外的自定义标记扩展。如果您要在多个位置使用列表,这种方式会更好,因为您可以在App.xaml资源中声明它。

答案 1 :(得分:1)

使用x:Type标记扩展怎么样?

{w:EnumValues EnumType={x:Type w:Test+TestEnum}}

<击> 除了实现INotifyPropertyChanged之外,我完全复制了您的代码。我确实得到了你得到的错误,但似乎运行得很好。不过,不能加载设计师是非常烦人的。我尝试过的任何事都没有解决问题。

我在MSDN上找到了关于嵌套类型的this page,并且该线程中的建议是用于解析嵌套类型名称的自定义MarkupExtension。我试图让它发挥作用,但到目前为止没有运气。我有时会在Type2Extension上遇到类似的错误,并且通过其他调整得到“枚举类型未设置”。

啊哈!原作者如何调用GetType()有一个错误!以下是更正的Type2Extension以及我如何使用它:

public class Type2Extension : System.Windows.Markup.TypeExtension {
    public Type2Extension() {
    }

    public Type2Extension( string typeName ) {
        base.TypeName = typeName;
    }

    public override object ProvideValue( IServiceProvider serviceProvider ) {
        IXamlTypeResolver typeResolver = (IXamlTypeResolver) serviceProvider.GetService( typeof( IXamlTypeResolver ) );
        int sepindex = TypeName.IndexOf( '+' );
        if ( sepindex < 0 )
            return typeResolver.Resolve( TypeName );
        else {
            Type outerType = typeResolver.Resolve( TypeName.Substring( 0, sepindex ) );
            return outerType.Assembly.GetType( outerType.FullName + "+" + TypeName.Substring( sepindex + 1 ) );
        }
    }
}

和XAML:

ItemsSource="{Binding Source={w:EnumValues {w:Type2 w:Test+TestEnum}}}"

这似乎工作正常,设计师加载。我将Type2Extension添加到我自己的小库中。

修改:奇怪的是,如果我在EnumValues中更改此内容:

if ( this.EnumType == null )
    throw new ArgumentException( "The enum type is not set" );

对此:

if ( this.EnumType == null )
    return null;

然后那些构造函数错误消失了。那是我改变的另一件事。但是,我很快就会发布另一种获取枚举值的方法。