PropertyGrid控件如何显示两层嵌套的动态JSON对象?
How does the PropertyGrid control display two levels of nested dynamic JSON objects?
我有几个同事的配置文件需要用PropertyGrid控件统一显示,我参考下面post:https://www.codeproject.com/Articles/193462/Using-PropertyGrid-to-Display-and-Edit-Dynamic-Obj.[=13实现了=]
我的做法是:先定义一个ConfigObject对象,然后使用JsonConvert.Convert(Newtonsoft.Json)将json配置文件反序列化为ConfigObject对象,然后赋值给PropertyGrid.SelectedObject.但是这样我只能显示和编辑一级嵌套的json对象,如果超过两级的嵌套结构,那么嵌套的属性字段将不可编辑。
考虑以下两级 JSON 结构:
{
"DAMultiCast": "18:80:c2:00:00:0e",
"SA": "18:60:24:A8:77:FF",
"gPTPType": "0x88f7",
"AVTPType": "0x22f0",
"Initial": {
"SyncMessageType": "0x10",
"FollowupMessageType": "0x18",
"ReqMessageType": "0x12",
"RespMessageType": "0x13",
"RespFollowupMessageType": "0x1A",
"versionPTP": "0x02",
"SyncMessagelength": "44",
"FollowupMessagelength": "76",
"ReqMessagelength": "54",
"subdomainnumber": "0",
"resv0": "0x00",
"Syncflagfield": "0x0208",
"Followupflagfield": "0x0008",
"correctionField": "00:00:00:00:00:00:00:00",
"resv1": "00:00:00:00",
"SyncClockIdentity": "01:02:03:ff:fe:46:76:34",
"RespClockIdentity": "00:22:97:ff:fe:80:0d:f2",
"sourcePortId": "0x0001",
"sequenceId": "143",
"SyncControlField": "0",
"FollowupControlField": "2",
"DelayReqControlField": "5",
"logMessagePeriod": "-3",
"tlvType": "3",
"lengthField": "28",
"organizationId": "32962",
"organizationSubType": "1",
"cumulativeScaledRateOffset": "0",
"gmTimeBaseIndicator": "0",
"lastGmPhaseChange": "00:00:00:00:00:00:00:00:00:00:00:00",
"scaledLastGmFreqChange": "0",
"requestingPortIdentity": "01:02:03:ff:fe:46:76:34",
"requestingPortId": "1"
},
"TM1_TG1_6.1.1B": {
"WaitTime1": "10",
"WaitTime2": "2"
}
}
这可能会给您一些实现目标的提示。
Source1: Dynamically Create a Class at
Runtime
Source2: PropertyGrid Browsable not found for entity framework
created property, how to find
it?
Source3: Make a Property Read-Only in PropertyGrid
输出:
代码:
private void loadJsonToPropertyGrid(string jsonString)
{
var jsonObject = JsonConvert.DeserializeObject<JObject>(jsonString);
var obj = createClass("Item", jsonObject);
var customClass = JsonConvert.DeserializeObject(jsonString, obj.GetType());
var customClassType = customClass.GetType();
DynamicTypeDescriptor typeDescriptor = new DynamicTypeDescriptor(customClassType);
var propertyDescriptorList = typeDescriptor.Properties.Cast<PropertyDescriptor>().ToList()
.Where(p => p.PropertyType.Name != "String").ToList();
propExpandAndReadOnly(propertyDescriptorList);
propertyGrid1.SelectedObject = typeDescriptor.FromComponent(customClass);
}
private void propExpandAndReadOnly(List<PropertyDescriptor> propertyDescriptorList)
{
foreach (var propertyDescriptor in propertyDescriptorList)
{
propertyDescriptor.SetReadOnlyAttribute(true);
propertyDescriptor.SetExpandableAttribute(true);
DynamicTypeDescriptor typeDescriptor = new DynamicTypeDescriptor(propertyDescriptor.PropertyType);
var chilPropertyDescriptorList = typeDescriptor.Properties.Cast<PropertyDescriptor>().ToList()
.Where(p => p.PropertyType.Name != "String").ToList();
propExpandAndReadOnly(chilPropertyDescriptorList);
}
}
private Type[] getPropertiesType(string[] properties, JObject jsonObject)
{
var propertyTypes = new List<Type>();
foreach (var property in properties)
{
var jToken = jsonObject.GetValue(property);
Type propertyType;
if (jToken.HasValues)
{
var obj = createClass(property, (JObject)jsonObject.GetValue(property));
propertyType = obj.GetType();
}
else
{
propertyType = typeof(string);
}
propertyTypes.Add(propertyType);
}
return propertyTypes.ToArray();
}
private object createClass(string name, JObject jsonObject)
{
MyClassBuilder MCB = new MyClassBuilder(name);
var properties = jsonObject.Properties().Select(p => p.Name).ToArray();
var propertiesType = getPropertiesType(properties, jsonObject);
var obj = MCB.CreateObject(properties, propertiesType);
return obj;
}
更新
创建 PropertyDescriptorExtensions
public static class PropertyDescriptorExtensions
{
public static void SetReadOnlyAttribute(this PropertyDescriptor p, bool value)
{
var attributes = p.Attributes.Cast<Attribute>()
.Where(x => !(x is ReadOnlyAttribute)).ToList();
attributes.Add(new ReadOnlyAttribute(value));
typeof(MemberDescriptor).GetProperty("AttributeArray",
BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue((MemberDescriptor)p, attributes.ToArray());
}
public static void SetExpandableAttribute(this PropertyDescriptor p, bool value)
{
var attributes = p.Attributes.Cast<Attribute>()
.Where(x => !(x is ReadOnlyAttribute)).ToList();
if (value)
{
attributes.Add(new TypeConverterAttribute(typeof(ExpandableObjectConverter)));
}
typeof(MemberDescriptor).GetProperty("AttributeArray",
BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue((MemberDescriptor)p, attributes.ToArray());
}
}
编码愉快,干杯!
我有几个同事的配置文件需要用PropertyGrid控件统一显示,我参考下面post:https://www.codeproject.com/Articles/193462/Using-PropertyGrid-to-Display-and-Edit-Dynamic-Obj.[=13实现了=]
我的做法是:先定义一个ConfigObject对象,然后使用JsonConvert.Convert(Newtonsoft.Json)将json配置文件反序列化为ConfigObject对象,然后赋值给PropertyGrid.SelectedObject.但是这样我只能显示和编辑一级嵌套的json对象,如果超过两级的嵌套结构,那么嵌套的属性字段将不可编辑。
考虑以下两级 JSON 结构:
{
"DAMultiCast": "18:80:c2:00:00:0e",
"SA": "18:60:24:A8:77:FF",
"gPTPType": "0x88f7",
"AVTPType": "0x22f0",
"Initial": {
"SyncMessageType": "0x10",
"FollowupMessageType": "0x18",
"ReqMessageType": "0x12",
"RespMessageType": "0x13",
"RespFollowupMessageType": "0x1A",
"versionPTP": "0x02",
"SyncMessagelength": "44",
"FollowupMessagelength": "76",
"ReqMessagelength": "54",
"subdomainnumber": "0",
"resv0": "0x00",
"Syncflagfield": "0x0208",
"Followupflagfield": "0x0008",
"correctionField": "00:00:00:00:00:00:00:00",
"resv1": "00:00:00:00",
"SyncClockIdentity": "01:02:03:ff:fe:46:76:34",
"RespClockIdentity": "00:22:97:ff:fe:80:0d:f2",
"sourcePortId": "0x0001",
"sequenceId": "143",
"SyncControlField": "0",
"FollowupControlField": "2",
"DelayReqControlField": "5",
"logMessagePeriod": "-3",
"tlvType": "3",
"lengthField": "28",
"organizationId": "32962",
"organizationSubType": "1",
"cumulativeScaledRateOffset": "0",
"gmTimeBaseIndicator": "0",
"lastGmPhaseChange": "00:00:00:00:00:00:00:00:00:00:00:00",
"scaledLastGmFreqChange": "0",
"requestingPortIdentity": "01:02:03:ff:fe:46:76:34",
"requestingPortId": "1"
},
"TM1_TG1_6.1.1B": {
"WaitTime1": "10",
"WaitTime2": "2"
}
}
这可能会给您一些实现目标的提示。
Source1: Dynamically Create a Class at Runtime
Source2: PropertyGrid Browsable not found for entity framework created property, how to find it?
Source3: Make a Property Read-Only in PropertyGrid
输出:
代码:
private void loadJsonToPropertyGrid(string jsonString)
{
var jsonObject = JsonConvert.DeserializeObject<JObject>(jsonString);
var obj = createClass("Item", jsonObject);
var customClass = JsonConvert.DeserializeObject(jsonString, obj.GetType());
var customClassType = customClass.GetType();
DynamicTypeDescriptor typeDescriptor = new DynamicTypeDescriptor(customClassType);
var propertyDescriptorList = typeDescriptor.Properties.Cast<PropertyDescriptor>().ToList()
.Where(p => p.PropertyType.Name != "String").ToList();
propExpandAndReadOnly(propertyDescriptorList);
propertyGrid1.SelectedObject = typeDescriptor.FromComponent(customClass);
}
private void propExpandAndReadOnly(List<PropertyDescriptor> propertyDescriptorList)
{
foreach (var propertyDescriptor in propertyDescriptorList)
{
propertyDescriptor.SetReadOnlyAttribute(true);
propertyDescriptor.SetExpandableAttribute(true);
DynamicTypeDescriptor typeDescriptor = new DynamicTypeDescriptor(propertyDescriptor.PropertyType);
var chilPropertyDescriptorList = typeDescriptor.Properties.Cast<PropertyDescriptor>().ToList()
.Where(p => p.PropertyType.Name != "String").ToList();
propExpandAndReadOnly(chilPropertyDescriptorList);
}
}
private Type[] getPropertiesType(string[] properties, JObject jsonObject)
{
var propertyTypes = new List<Type>();
foreach (var property in properties)
{
var jToken = jsonObject.GetValue(property);
Type propertyType;
if (jToken.HasValues)
{
var obj = createClass(property, (JObject)jsonObject.GetValue(property));
propertyType = obj.GetType();
}
else
{
propertyType = typeof(string);
}
propertyTypes.Add(propertyType);
}
return propertyTypes.ToArray();
}
private object createClass(string name, JObject jsonObject)
{
MyClassBuilder MCB = new MyClassBuilder(name);
var properties = jsonObject.Properties().Select(p => p.Name).ToArray();
var propertiesType = getPropertiesType(properties, jsonObject);
var obj = MCB.CreateObject(properties, propertiesType);
return obj;
}
更新
创建 PropertyDescriptorExtensions
public static class PropertyDescriptorExtensions
{
public static void SetReadOnlyAttribute(this PropertyDescriptor p, bool value)
{
var attributes = p.Attributes.Cast<Attribute>()
.Where(x => !(x is ReadOnlyAttribute)).ToList();
attributes.Add(new ReadOnlyAttribute(value));
typeof(MemberDescriptor).GetProperty("AttributeArray",
BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue((MemberDescriptor)p, attributes.ToArray());
}
public static void SetExpandableAttribute(this PropertyDescriptor p, bool value)
{
var attributes = p.Attributes.Cast<Attribute>()
.Where(x => !(x is ReadOnlyAttribute)).ToList();
if (value)
{
attributes.Add(new TypeConverterAttribute(typeof(ExpandableObjectConverter)));
}
typeof(MemberDescriptor).GetProperty("AttributeArray",
BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue((MemberDescriptor)p, attributes.ToArray());
}
}
编码愉快,干杯!