2009-08-02 3 views
10

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

Я пытаюсь выполнить приведенный здесь ответ (wpf combobox binding to enum what i did wrong?) В частности, я использую предложенный код 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; 
     } 
    } 
} 

Так вопрос я говорил ссылается на синтаксис XAML согласования, как существа:

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

Но это (вроде) не работает для меня. Когда я сделать чистую сборку, я получаю «Build преуспели» сообщение в строке состояния VS 2008, но я получаю сообщение об ошибке сообщается в XAML

Type 'Test+TestEnum' was not found. 

Но код работает, как ожидалось!

Однако это означает, что конструктор xaml не загружается. Таким образом, я как бы прикручен к работе wpf, пока не могу очистить ошибку xaml.

Теперь я задаюсь вопросом, является ли это проблемой VS 2008 SP1, а не проблемой с моей стороны.

Редактировать

  1. Сделано моя проблема утверждение более явным.
  2. Пробовал 1-решение Джоэла, но я в конечном итоге с кодом, исполняемым и 2 ошибки Xaml
  3. 2 варианта Пробовал Джоэл и работал прямо из коробки - так что я буду с этим один!

Примечание Так вопрос, который я получил код 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}"/> 

Поскольку этот синтаксис работает!

+0

Что касается ошибки в конструкторе, то это происходит только в дизайнере.В противном случае он должен работать нормально. Чтобы избежать этой ошибки, определите расширение разметки в отдельном сборке –

+0

@Thomas: Я забыл, как дизайнер может быть проворным в отношении того, какие типы создаются, когда/где и как он их создает. –

+0

@Joel - И новички программисты wpf знают это как? Знание ставить класс в отдельную сборку, чтобы удовлетворить одну часть инструмента построения, довольно неясно ИМХО. Но я буду продолжать показываться, пока не получу разумное понимание. Еще раз спасибо. –

ответ

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 ресурсах.

+0

Большое вам спасибо за то, что он слишком долго меня заводил с ума! –

+1

Очень приятно - мне не хватало «+», объявляя перечисление в MethodParameters, и это приводило меня в бешенство. –

1

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

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

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

Я нашел this page в 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 в свои собственные библиотеки.

Edit: Как ни странно, если бы я изменить в EnumValues:

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

Для этого:

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

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

+0

Если вы имеете в виду: ItemsSource = "{Binding Source = {w: EnumValues ​​EnumType = {x: Тип w: Test + TestEnum}}}" Тогда это просто закончилось большой компиляцией –

+0

Интересно. После ответа я начал скомпоновать ваш примерный код для воссоздания (просто чтобы убедиться, что он сработал). Я отредактирую, если что-нибудь придумаю. –

+0

Спасибо за это. Я слишком устал, чтобы беспокоиться об этом прямо сейчас, но утром я буду смотреть первым. Я новичок в wpf, и мне приходится прыгать через эти виды обручей, чтобы делать то, что должно быть основным, раздражает меня. –