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

10

我一直在尝试将一个枚举类型的属性绑定到ComboBox上,这个枚举本身是在同一个类中声明的。

我试着去遵循这里提供的答案 (wpf combobox binding to enum what i did wrong?)。具体来说,我正在使用建议的MarkupExtension代码和对应的XAML代码。

我的代码已经可以正常工作:

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

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

使用枚举的类(注意,已经删除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类内部定义枚举,不再将枚举定义在全局作用域中。像这样:

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

但这对我来说(有点)不起作用。当我进行清理构建时,Visual Studio 2008 状态栏会显示“构建成功”的消息,但同时我也会收到在 XAML 中报告的错误。

Type 'Test+TestEnum' was not found.  

但代码按预期运行!

然而,这意味着XAML设计器将不会加载。因此,在我能够解决XAML错误之前,我无法进行更多的WPF工作。

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

编辑

  1. 使我的问题陈述更明确。
  2. 尝试了Joel的第一种解决方案,但最终代码可以运行2个XAML错误。
  3. 尝试了Joel的第二种解决方案,并且直接奏效,所以我选择它!

:我从SO问题中获取了MarkupExtension代码,并使用了这种语法样式的XAML:

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

当我使用这个时,编译错误显示没有EnumValues构造函数接受1个参数。我尝试了一些搜索,似乎这是一个VS的错误。我正在使用VS 2008 SP1。我看到一些评论提到这在VS 2010 beta中有过类似问题。无论如何,这就是我使用xaml语法的原因。

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

这个语法是可行的!


关于构造函数的错误,这仅在设计器中发生。否则应该能够正常工作。为避免此错误,请在单独的程序集中定义标记扩展。 - Thomas Levesque
@Thomas:我忘了设计师在何时/何地以及如何实例化它们时,对于构建哪些类型可能会很固执。 - Joel B Fant
@Joel - 新手wpf程序员怎么会知道这种事情呢?我认为知道将一个类放在单独的程序集中以满足构建工具的某个部分是相当晦涩的。但我会一直努力,直到理解为止。再次感谢。 - Peter M
@Thomas - 感谢你的提示。那是我从未考虑过的事情,即使在谷歌搜索这个特定问题后也没有想到。 - Peter M
@Peter:是的,这很难理解。但我并没有经常遇到这个问题。我正在尝试找到一个链接来解释Cider如何实例化对象,因为我只记得一些关于基类的内容。 - Joel B Fant
2个回答

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

请注意,由于TypeExtension和嵌套类型的奇怪性质,您仍然需要Type2Extension。但是,您不需要额外的自定义标记扩展。如果您将在多个地方使用列表,则此方法更好,因为您可以在App.xaml资源中声明它。

非常感谢你的帮助 - 这个问题已经让我疯狂了很长时间! - Peter M
1
非常好 - 在MethodParameters中声明枚举时我漏了"+"号,这一点让我感到非常恼火。 - Wonko the Sane

1

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

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

除了实现INotifyPropertyChanged,我完全复制了你的代码。我确实遇到了你遇到的错误,但它似乎运行得很好。不过不能加载设计师真的很烦人。我尝试过的所有方法都没有解决这个问题。

我在MSDN上找到了这个页面关于嵌套类型的内容,并且该主题中的建议是使用自定义MarkupExtension来解析嵌套类型名称。我正在尝试让它工作,但迄今为止没有成功。有时候我会遇到与Type2Extension类似的错误,而其他调整则会出现“未设置枚举类型”的错误。

啊哈!原始作者调用GetType()的方式存在一个bug!这里是已更正的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;

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


如果你的意思是: ItemsSource="{Binding Source={w:EnumValues EnumType={x:Type w:Test+TestEnum}}}" 那么这只会导致编译失败。 - Peter M
有趣。回答后,我开始把你的示例代码组合起来重新创建(只是为了看看它是否能工作)。如果我弄清楚了什么,我会进行编辑。 - Joel B Fant
谢谢你。我现在太累了,不想去管它,但明天一早我会看看它。我是wpf的新手,必须跳过这些应该是基本操作的麻烦事情,这让我非常恼火。 - Peter M
@Joel - 看到它对你起作用,我现在想知道我的VS2008 SP1安装是否存在问题?顺便说一下,我只能在w:Test+TestEnum中让你的xaml工作。 - Peter M
@Thomas 我知道这很奇怪。这就是为什么我怀疑我的机器上存在安装问题。我将尝试修复或清洁安装,然后看看会发生什么。 - Peter M
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接