PropertyGrid 控件如何显示两层嵌套的动态 JSON 对象?

Posted

技术标签:

【中文标题】PropertyGrid 控件如何显示两层嵌套的动态 JSON 对象?【英文标题】:How does the PropertyGrid control display two levels of nested dynamic JSON objects? 【发布时间】:2020-12-01 22:27:34 【问题描述】:

我有一个需求,我的几个同事的配置文件要用PropertyGrid控件统一显示,我参考下面的帖子实现了:https://www.codeproject.com/Articles/193462/Using-PropertyGrid-to-Display-and-Edit-Dynamic-Obj。

我的方法是:先定义一个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"
  

【问题讨论】:

顺便说一句:嵌套的 JSON 属性字段在 PropertyGrid 控件中显示为 Collections,单击 [...] 时会显示 JToken Collection 编辑器。 【参考方案1】:

这可能会提示您实现目标。

来源1:Dynamically Create a Class at Runtime 来源2:PropertyGrid Browsable not found for entity framework created property, how to find it? 来源3: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());
    

编码愉快,干杯!

【讨论】:

谢谢,tontonsevilla。我会试试看,稍后再回复你。 tontonsevilla,我通过在PropertyDescriptor类中添加一个SetReadOnlyAttribute扩展方法来实现PropertyDescriptor.SetReadOnlyAttribute,但是我不知道如何实现SetExpandableAttribute方法。 @JackZhang 我更新了我的答案。您可以在那里查看解决方案。干杯! 你好,tontonsevilla,我能达到下面新问题所示的效果吗?如果我不能做到这一点,我将使用文本编辑器来完成它,它更具可扩展性,并且我将在用户更改后使用序列化来检查错误。

以上是关于PropertyGrid 控件如何显示两层嵌套的动态 JSON 对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何让PropertyGrid显示控件的Name属性

C#中自定义propertygrid控件的属性,要求当点击不同的其他控件时,能在propertygrid控件中显示基本的信息

c#用propertyGrid控件

C# 自定义PropertyGrid中显示控件的大小,要求限定PropertyGrid中大小的值。

关于C# PropertyGrid控件的属性只读

PropertyGrid自定义控件