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

问题描述

我要求我的几个同事的配置文件应与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"
  }
}

解决方法

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

资料来源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

输出:
enter image description here
代码:

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",attributes.ToArray());
    }
}

编码愉快,加油!

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...