简体   繁体   中英

How does the PropertyGrid control display two levels of nested dynamic JSON objects?

I have a requirement that several of my colleagues' configuration files should be displayed uniformly with the PropertyGrid control, which I have implemented with reference to the following post: https://www.codeproject.com/Articles/193462/Using-PropertyGrid-to-Display-and-Edit-Dynamic-Obj .

My way is: define a ConfigObject object first, then deserialized json configuration file into ConfigObject object using JsonConvert.Convert(Newtonsoft.Json), and then assigned to the PropertyGrid.SelectedObject. But this way I can only display and edit the one level of nested json objects, if more than two levels of nested structure, so the nested property field will be not editable.

Consider the following two-level JSON structure:

{
  "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"
  }
}

This might give you a hint to achieve your goal.

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

OUTPUT:
在此处输入图像描述
CODE:

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;
    }

UPDATE
Creation of 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());
    }
}

Happy coding, cheers!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM