2015-11-20 5 views
0

J'essaie de générer un PropertyGrid avec un objet généré dynamiquement.Impossible de créer correctement GenerA CustomAttributeData lors de l'utilisation de Enum généré par programme

Pour les sélections de combo sur cette grille de la propriété, j'ai construit un TypeConverter (où T est un ENUM, la définition de la liste des options):

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

    } 

je peux alors ajouter un attribut personnalisé à la propriété, référencement ce TypeConverter, comme ci-dessous (typedConverterGenericType est le type de TypedConverter avec un argument générique ENUM)

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

propertyBuilder.SetCustomAttribute(attributeBuilder); 

cela fonctionne grand, tant que le Enum en question est hardcoded: AddTypeConverterAttribute(propertyBuilder, typeof(TypedConverter<Fred>));. Dans le débogueur, l'attribut sur la propriété me donne {[System.ComponentModel.TypeConverterAttribute(....

Cependant, lorsque j'utilise un ENUM dynamique construit (que j'ai déterminé est généré correctement dans la réflexion) ne fonctionne pas:

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

Dans le débogueur, l'attribut sur la propriété me donne maintenant {System.Reflection.CustomAttributeData}, et forage dans ce, j'ai une erreur sur le ConstructorArguments ... Mscorlib_CollectionDebugView<System.Reflection.CustomAttributeData>(type.GetProperties()[1].CustomAttributes).Items[4].ConstructorArguments' threw an exception of type 'System.IO.FileNotFoundException'

Qu'est-ce que je fais mal? Comment puis-je obtenir l'attribut TypeConverter correctement?

EDIT: Dans le cas où quelqu'un veut voir comment ajouter l'attribut

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

test confirme qu'il est un problème avec le ENUM dynamiquement construit - si je crée le générique tapez avec Type typedConverterType = converterType.MakeGenericType(typeof(Fred)); cela fonctionne bien.

EDIT 3

Mon projet de test est disponible here. Il lit un certain JSON de Resouces, et essaie de générer une classe dont le type est décrit par ce JSON.

Je crée une instance de cette classe (Activator.CreateInstance) qui va générer un PropertyGrid. Pour obtenir la sélection de liste déroulante sur ce PropertyGrid, je crée un type, avec une propriété attribuée à TypedConverter, où T est une énumération qui décrit les valeurs dans la sélection de liste déroulante.

Cela fonctionne très bien pour les énumérations hardcoded, mais pas pour les

+0

Avez-vous essayé 'publique EnumBuilder DefineEnum (nom de chaîne , TypeAttribute la visibilité, Type de type sous-jacent); 'dans' System.Reflection.Emit.ModuleBuilder'? Voici un exemple: https://msdn.microsoft.com/en-us/library/system.reflection.emit.enumbuilder(v=vs.110).aspx – mrtig

+0

@mrtig Oui, je ne l'ai pas inclus pour des raisons de concision Saké. 'Builder EnumBuilder = moduleBuilder.DefineEnum (nom, TypeAttributes.Public, typeof (int));' est comment je reçois mon enumBuilder. – johnc

Répondre

0

Je crois Généré automatiquement que je suis en mesure d'obtenir ce travail en utilisant différents ensembles dynamiques. Faites-moi savoir si cela fonctionne pour vous:

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

Merci d'avoir regardé cela, en train sur le chemin du retour maintenant un vendredi arvo. Promesse Je vais vérifier cela dès que possible et vous faire savoir – johnc

+0

Désolé, mec, essayé différents assemblées, mêmes assemblées, RunAndSave, juste Run, juste Enregistrer, sauvegardés et non enregistrés dll. Chose amusante, cependant, mes dlls sauvées apparaissent vides selon dotpeek, – johnc

+0

Alors, est ce que vous essayez de faire matériellement différent de l'exemple que j'ai utilisé? – mrtig

0

Je viens de comprendre qu'il existe une solution simple à ce problème.Vous devez définir l'événement AssemblyResolve de votre domaine actuel et renvoyer le l'ensemble demandé dans le gestionnaire d'événements:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 

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

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

Cela rendra votre travail énumérations générées dynamiquement