Связывание ComboBox с перечислением, вложенным в класс

Я схожу с ума, привязывая combobox к типизированному свойству перечисления класса, где само перечисление объявляется в том же классе.

Я пытаюсь выполнить приведенный здесь ответ (привязка wopf combobox к перечислению, что я сделал не так?) В частности, я использую предложенный код MarkupExtension и соответствующий xaml код.

Мой рабочий код:

Определение Enum в отдельном файле.

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

Метод расширения для отображения Enum

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>

Выше все хорошо и денди, но я хочу определить класс 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 с пакетом обновления 1 (SP1), а не проблемой с моей стороны.

Edit

  • Сделал мою задачу более явной.
  • Пробовал Joel 1-е решение, но я закончил с кодом и 2 xaml errors
  • Попробовал Joel 2nd solution, и это работало прямо из коробки - так что я собираюсь с этим!

Примечания Вопрос SO, который получил код MarkupExtension, использует этот стиль синтаксиса для xaml:

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

Когда я использую это, я получаю ошибку компиляции, говоря, что никакой конструктор EnumValues ​​не принимает 1 параметр. Я сделал некоторый googling, и это, кажется, ошибка в VS. Я использую VS 2008 SP1. Я видел некоторые комментарии, которые ссылались на него в бета-версии VS 2010. Во всяком случае, поэтому я использую синтаксис xaml

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

Как работает этот синтаксис!

Ответ 1

Другой способ получения значений перечисления для использования в качестве источника данных:

<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.

Ответ 2

Как использовать расширение разметки x:Type?

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

Забастовкa > За исключением реализации INotifyPropertyChanged, я скопировал ваш код точно. Я получаю ошибки, которые вы получаете, но, похоже, все работает нормально. Тем не менее, это очень раздражает, если вы не можете загрузить дизайнера. Ничто из того, что я пробовал, не решило проблему.

Я нашел эту страницу в MSDN о вложенных типах, а предложение в этом потоке было обычным 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;

Затем эти ошибки конструктора уходят. Это была другая вещь, которую я изменил. Тем не менее, я вскоре собираюсь опубликовать альтернативный способ получения значений перечисления.