很不幸,属性在运行时不能更改。你基本上有两个选项:
Recreate a similar type on the fly using System.Reflection.Emit
as shown below.
Ask your vendor to add this functionality. If you are using Xceed.WpfToolkit.Extended you can download the source code from here and easily implement an interface like IResolveCategoryName
that would resolve the attribute at runtime. I did a bit more than that, it was pretty easy to add more functionality like limits when editing a numeric value in a DoubleUpDown
inside the PropertyGrid
, etc.
namespace Xceed.Wpf.Toolkit.PropertyGrid
{
public interface IPropertyDescription
{
double MinimumFor(string propertyName);
double MaximumFor(string propertyName);
double IncrementFor(string propertyName);
int DisplayOrderFor(string propertyName);
string DisplayNameFor(string propertyName);
string DescriptionFor(string propertyName);
bool IsReadOnlyFor(string propertyName);
}
}
对于第一种选项:然而,它缺乏适当的属性绑定来反映结果回到实际被编辑的对象。
private static void CreatePropertyAttribute(PropertyBuilder propertyBuilder, Type attributeType, Array parameterValues)
{
var parameterTypes = (from object t in parameterValues select t.GetType()).ToArray();
ConstructorInfo propertyAttributeInfo = typeof(RangeAttribute).GetConstructor(parameterTypes);
if (propertyAttributeInfo != null)
{
var customAttributeBuilder = new CustomAttributeBuilder(propertyAttributeInfo,
parameterValues.Cast<object>().ToArray());
propertyBuilder.SetCustomAttribute(customAttributeBuilder);
}
}
private static PropertyBuilder CreateAutomaticProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo)
{
string propertyName = propertyInfo.Name;
Type propertyType = propertyInfo.PropertyType;
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType,
null);
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
MethodBuilder currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, getSetAttr, propertyType, Type.EmptyTypes);
ILGenerator currGetIl = currGetPropMthdBldr.GetILGenerator();
currGetIl.Emit(OpCodes.Ldarg_0);
currGetIl.Emit(OpCodes.Ldfld, field);
currGetIl.Emit(OpCodes.Ret);
MethodBuilder currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName, getSetAttr, null, new[] { propertyType });
ILGenerator currSetIl = currSetPropMthdBldr.GetILGenerator();
currSetIl.Emit(OpCodes.Ldarg_0);
currSetIl.Emit(OpCodes.Ldarg_1);
currSetIl.Emit(OpCodes.Stfld, field);
currSetIl.Emit(OpCodes.Ret);
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
return property;
}
public static object EditingObject(object obj)
{
AssemblyName assembly = new AssemblyName("EditingWrapper");
AppDomain appDomain = System.Threading.Thread.GetDomain();
AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name);
TypeBuilder typeBuilder = moduleBuilder.DefineType("EditingWrapper",
TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit, typeof(System.Object));
Type objType = obj.GetType();
foreach (var propertyInfo in objType.GetProperties())
{
string propertyName = propertyInfo.Name;
Type propertyType = propertyInfo.PropertyType;
PropertyBuilder propertyBuilder = CreateAutomaticProperty(typeBuilder, propertyInfo);
CreatePropertyAttribute(propertyBuilder, typeof(Category), new[]{"My new category value"});
}
Type generetedType = typeBuilder.CreateType();
object generetedObject = Activator.CreateInstance(generetedType);
return generetedObject;
}
}