2015-11-20 2 views
0

Я пытаюсь создать PropertyGrid с динамически созданным объектом.Невозможно правильно создать Generic CustomAttributeData при использовании программно сгенерированного Enum

Для комбинированных выборов на этой собственности сетки, я построил TypeConverter (где T является перечисление, определяющий список опций):

public class TypedConverter<T> : StringConverter where T : struct, IConvertible 
    { 
     ... 

     public override System.ComponentModel.TypeConverter.StandardValuesCollection 
      GetStandardValues(ITypeDescriptorContext context) 
     { 
      if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");    

      string[] values = Enum.GetValues(typeof(T)).OfType<object>().Select(o => o.ToString()).ToArray(); 

      return new StandardValuesCollection(values); 
     } 

    } 

Затем я могу добавить пользовательский атрибут к свойству, ссылки это TypeConverter, как показано ниже (typedConverterGenericType является типом TypedConverter с перечислениями родового аргументом)

CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType }); 

propertyBuilder.SetCustomAttribute(attributeBuilder); 

Это работает большого, до тех пор, как Enum в вопросе зашито: AddTypeConverterAttribute(propertyBuilder, typeof(TypedConverter<Fred>));. В отладчике атрибут свойства дает мне {[System.ComponentModel.TypeConverterAttribute(....

Однако, когда я использую динамически построенный перечисление (что я определил правильно генерироваться в отражении) не работает:

Type enumType = enumBuilder.CreateType();//This generates a proper enum, as I have determined in reflection 

    Type converterType = typeof(TypedConverter<>); 

    Type typedConverterType = converterType.MakeGenericType(enumType); 

    AddTypeConverterAttribute(propertyBuilder, typedConverterType); 

В отладчике, атрибут на имущество теперь дает мне {System.Reflection.CustomAttributeData}, и сверление в этом, у меня есть ошибка на ConstructorArguments ... Mscorlib_CollectionDebugView<System.Reflection.CustomAttributeData>(type.GetProperties()[1].CustomAttributes).Items[4].ConstructorArguments' threw an exception of type 'System.IO.FileNotFoundException'

Что я делаю неправильно? Как я могу правильно установить атрибут TypeConverter?

EDIT: В случае, если кто-то хочет посмотреть, как добавить атрибут

private void AddTypeConverterAttribute(PropertyBuilder propertyBuilder, Type typedConverterGenericType) 
{ 
    CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType }); 

    propertyBuilder.SetCustomAttribute(attributeBuilder);   
} 

EDIT2

тестирование подтверждает это проблема с динамически построенного перечисления - если я создаю общий тип с Type typedConverterType = converterType.MakeGenericType(typeof(Fred)); он отлично работает.

EDIT 3

Мой тестовый проект доступен here. Он читает некоторые JSON из Resouces и пытается сгенерировать класс, тип которого описан этим JSON.

Я создаю экземпляр этого класса (Activator.CreateInstance), который будет источником PropertyGrid. Чтобы получить комбо-выбор в этом PropertyGrid, я создаю Type с атрибутом TypedConverter, где T представляет собой перечисление, которое описывает значения в комбинированном выборе.

Это прекрасно работает для закодированных перечислений, но не для тех, сгенерированных программно

+0

Вы пробовали 'общественного EnumBuilder DefineEnum (имя строки , Видимость TypeAttributes, Тип baseType); 'в' System.Reflection.Emit.ModuleBuilder'? Вот пример: https://msdn.microsoft.com/en-us/library/system.reflection.emit.enumbuilder(v=vs.110).aspx – mrtig

+0

@mrtig Да, я не включил его для краткости ради. 'EnumBuilder builder = moduleBuilder.DefineEnum (name, TypeAttributes.Public, typeof (int));' как я получаю свой enumBuilder. – johnc

ответ

0

Я считаю, что я был в состоянии получить эту работу, используя различные динамические сборки. Дайте мне знать, если это сработает для вас:

AppDomain currentDomain = AppDomain.CurrentDomain; 
AssemblyName enumAssembly = new AssemblyName("enumAssembly"); 

AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
    enumAssembly, AssemblyBuilderAccess.RunAndSave); 
ModuleBuilder mb = ab.DefineDynamicModule(enumAssembly.Name, 
    enumAssembly.Name + ".dll"); 

// Define a public enumeration with the name "Foo" and an 
// underlying type of Integer. 
EnumBuilder eb = mb.DefineEnum("Foo", TypeAttributes.Public, typeof(int)); 

eb.DefineLiteral("Bar", 0); 
eb.DefineLiteral("Baz", 1); 

Type final_foo = eb.CreateType(); 

ab.Save(enumAssembly.Name + ".dll"); 
var converterType = typeof(TypedConverter<>); 

AssemblyName dynamicAsm = new AssemblyName(); 
dynamicAsm.Name = "DynamicAsm"; 

// To generate a persistable assembly, specify AssemblyBuilderAccess.RunAndSave. 
AssemblyBuilder myAsmBuilder = currentDomain.DefineDynamicAssembly(dynamicAsm, 
               AssemblyBuilderAccess.RunAndSave); 
// Generate a persistable single-module assembly. 
ModuleBuilder myModBuilder = 
    myAsmBuilder.DefineDynamicModule(dynamicAsm.Name, dynamicAsm.Name + ".dll"); 

TypeBuilder myTypeBuilder = myModBuilder.DefineType("CustomerData", 
               TypeAttributes.Public); 

PropertyBuilder custNamePropBldr = myTypeBuilder.DefineProperty("elevation", 
               PropertyAttributes.HasDefault, 
               final_foo, 
               null); 


var typedConverterType = converterType.MakeGenericType(final_foo); 

CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
    typeof(TypeConverterAttribute).GetConstructor(
     new Type[] { typeof(Type) }), 
     new Type[] { typedConverterType } 
    ); 

custNamePropBldr.SetCustomAttribute(attributeBuilder); 
+0

Спасибо за то, что на поезде по дороге домой сейчас в пятницу arvo. Обещаю, я проверю это как можно скорее и дам вам знать – johnc

+0

Извините, чувак, попробовал разные сборки, те же сборки, RunAndSave, просто запустил, просто сохранил, сохранил и не сохранил dll. Смешная вещь, однако, мои сохраненные dll кажутся пустыми в соответствии с dotpeek, – johnc

+0

Итак, что вы пытаетесь сделать существенно отличным от примера, который я использовал? – mrtig

0

Я просто понял, что есть простое решение этой проблемы.Вы должны установить событие AssemblyResolve вашего текущего домена и возвращает запрошенную сборку в обработчик события:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 

    return AppDomain 
       .CurrentDomain 
       .GetAssemblies() 
       .FirstOrDefault(assembly => assembly.FullName == args.Name); 
} 

Это сделает ваши динамически генерируемые Перечисления работы

Смежные вопросы