Привязка ComboBox к перечислению, вложенному в класс - PullRequest
10 голосов
/ 03 августа 2009

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

Я пытаюсь следовать приведенному здесь ответу (wbf combobox привязка к enum, что я сделал не так?) В частности, я использую предложенный код 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 в пределах класса Test и исключить 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. Пробовал первое решение Джоэла, но в итоге я получил код, запускающий и 2 ошибки xaml
  3. Попробовал 2-е решение Джоэла, и оно сработало прямо из коробки - так что я пойду с этим!

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

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 03 августа 2009

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

<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 голос
/ 03 августа 2009

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

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

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

Я нашел эту страницу на MSDN о вложенных типах, и в этом потоке было предложено MarkupExtension для разрешения имени вложенного типа. Я пытаюсь заставить его работать, но пока не повезло. Иногда я получаю похожие ошибки на Type2Extension, а с другими настройками я получаю «Тип перечисления не установлен».

Aha! Была ошибка в том, как оригинальный автор звонил 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;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...