我正在尝试使用动态生成的对象来源PropertyGrid。
对于此属性网格上的组合选择,我构建了一个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属性?
编辑:如果有人想看我如何添加属性
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));
创建泛型类型,它可以正常工作。
编辑3
我的测试项目可用here。它正在从Resouces中读取一些JSON,并尝试生成一个类,该类的类型由该JSON描述。
我正在创建一个将要获取PropertyGrid的类(Activator.CreateInstance
)的实例。为了在该PropertyGrid上获得组合选择,我创建了一个Type,其属性归属于TypedConverter,其中T是一个枚举,用于描述组合选择中的值。
这适用于硬编码的枚举,但不适用于以编程方式生成的枚举
答案 0 :(得分: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);
答案 1 :(得分: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);
}
这将使动态生成的枚举工作