问题描述
我正在制作一个带有“propertygrid 控件”的表单应用程序。
我希望所有属性始终以非粗体文本显示 --- 始终将所有属性视为“默认”值。这个“测试”类有很多属性。
我认为最好的方法是动态实现 'ShouldSerializeXXX' 方法。手动实现缺乏灵活性,不智能。
我知道使用“Dynamicmethod 类”动态实现函数的方法[https://docs.microsoft.com/en-US/dotnet/api/system.reflection.emit.dynamicmethod?view=net-5.0]。但是'ShouldSerializeXXX'函数只是定义就起作用了,我不知道如何实现该函数。
谁能告诉我如何做到这一点?对不起,我的英语不好。
public class Test
{
public int AAA {get;set;}
public string BBB {get;set;}
public bool CCC {get;set;}
...
...
//This class has a lot of property,so I want to dynamically implement the function like this:
private bool ShouldSerializeAAA(){ return false; }
private bool ShouldSerializeBBB(){ return false; }
...
}
解决方法
动态实现一个方法然后需要被反射发现不是微不足道的,并且需要动态创建 Test
的子类型并确保您的所有实例实际上是子类型 - 不是一个吸引人的命题。
不过,还有一些更吸引人的选择;
- 生成额外的方法——这可能是几行丢弃的反射代码(以及一个检查它们是否都存在的单元测试),它只是吐出很多版本的{{ 1}}
- 查看 type-descriptor/property-descriptor API;自定义类型描述提供程序可以提供自定义属性描述符,并且他们可以决定某些内容是否需要序列化;只是 default 实现寻找
public bool ShouldSerialize{Name}() => false;
老实说,选项 1 在这里似乎是最简单的选项 - 它在运行时不会造成混乱;您可以在几分钟内实施选项 1,包括测试以确保您不会错过任何新测试
,基本思想是使用自定义类型描述符,因为它已在 Marc 的回答中解决。您可以在我的帖子 here 中看到一个实现。您可以通过更改 ShouldSerializeValue 的覆盖并返回 false 来轻松地使链接的帖子为您工作。仅此而已。
但在这里我想分享另一种选择,一个更短的答案,它需要更少的努力,但基本上为你做同样的事情。将对象传递给 PropertyGrid 时使用代理:
假设你有一个这样的公共类:
public class MyClass
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
这是您使用代理的方式:
var myOriginalObject = new MyClass();
this.propertyGrid1.SelectedObject = new ObjectProxy(myOriginalObject);
这是更改属性后的结果:
这是 ObjectProxy
,它是从 CustomTypeDescriptor
派生的类,将为您带来神奇的效果。这是课程:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class ObjectProxy : CustomTypeDescriptor
{
public object Original { get; private set; }
public List<string> BrowsableProperties { get; private set; }
public ObjectProxy(object o)
: base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o)) => Original = o;
public override PropertyDescriptorCollection GetProperties(Attribute[] a)
{
var props = base.GetProperties(a).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p));
return new PropertyDescriptorCollection(props.ToArray());
}
public override object GetPropertyOwner(PropertyDescriptor pd) => Original;
}
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor o;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) => o = originalProperty;
public override bool CanResetValue(object c) => o.CanResetValue(c);
public override object GetValue(object c) => o.GetValue(c);
public override void ResetValue(object c) => o.ResetValue(c);
public override void SetValue(object c,object v) => o.SetValue(c,v);
public override bool ShouldSerializeValue(object c) => false;
public override AttributeCollection Attributes => o.Attributes;
public override Type ComponentType => o.ComponentType;
public override bool IsReadOnly => o.IsReadOnly;
public override Type PropertyType => o.PropertyType;
}