Связывание 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;
Затем эти ошибки конструктора уходят. Это была другая вещь, которую я изменил. Тем не менее, я вскоре собираюсь опубликовать альтернативный способ получения значений перечисления.