【问题标题】:Binding a ComboBox to an enum nested in a class将 ComboBox 绑定到嵌套在类中的枚举
【发布时间】:2010-11-16 06:03:27
【问题描述】:

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

我正在尝试遵循此处提供的答案 (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 within,并放弃在全局范围内定义 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 设计器将不会加载。因此,在清除 xaml 错误之前,我有点搞砸了 wpf 工作。

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

编辑

  1. 使我的问题陈述更加明确。
  2. 尝试了 Joel 的第一个解决方案,但我最终发现代码运行 2 个 xaml 错误
  3. 尝试了 Joel 的第二个解决方案,该解决方案直接开箱即用 - 所以我将使用那个解决方案!

注意事项 我从中获得 MarkupExtension 代码的 SO 问题对 xaml 使用了这种语法:

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

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

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

因为这个语法有效!

【问题讨论】:

  • 关于构造函数的错误,它只发生在设计器中。否则它应该可以正常工作。为避免此错误,请在单独的程序集中定义标记扩展
  • @Thomas:我已经忘记了设计师是如何对何时/何地构建什么类型以及如何实例化它们感到愤怒的。
  • @Joel - wpf 新手程序员怎么知道这种事情?知道将一个类放在一个单独的程序集中以满足构建工具的一部分是相当模糊的恕我直言。但我会继续努力,直到我得到合理的理解。再次感谢。
  • @Thomas - 感谢您的提示。这不是我曾经考虑过的事情——即使在谷歌上搜索了那个特定问题之后也是如此。
  • @Peter:是的,它很模糊。不过,它对我来说并不经常出现。我正在尝试找到一个解释 Cider 如何实例化事物的链接,因为我只记得有关基类的内容。

标签: wpf class combobox enums nested


【解决方案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;

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

【讨论】:

  • 如果你的意思是:ItemsSource="{Binding Source={w:EnumValues EnumType={x:Type w:Test+TestEnum}}}" 那只是编译失败
  • 有趣。回答后,我开始将您的示例代码放在一起重新创建(只是看看它是否有效)。如果我发现任何东西,我会编辑。
  • 谢谢。我现在太累了,懒得理它,但我早上第一件事就是看它。我是 wpf 的新手,不得不跳过这些圈子来做应该是基本的事情,这让我非常恼火。
  • @Joel - 我已经实现了 Ty2Extension 类并使用了我们的 xaml,但它并没有让我更进一步。事实上,现在我在 xaml 中有更多错误“类型“Tye2extension”的构造函数有 1 个参数”“类型“EnumValuesExtension”的构造函数没有 1 个参数”。但代码按预期运行并找到枚举。一个真正的WTF!
  • @Joel - 看到它对你有用,我现在想知道我的 VS2008 SP1 安装是否有问题?顺便说一句,我只让您的 xaml 与 w:Test+TestEnum 一起使用
【解决方案2】:

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

<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 资源中声明它。

【讨论】:

  • 非常感谢您 - 太长时间以来它一直让我发疯!
  • 非常好 - 我在 MethodParameters 中声明枚举时遗漏了“+”,这让我发疯了。
猜你喜欢
  • 1970-01-01
  • 2011-07-03
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
  • 1970-01-01
  • 2015-07-03
  • 2023-03-03
相关资源
最近更新 更多