2

I'm making a form application with a 'propertygrid control.'
I want all properties to always display with non-bold text --- always treat all properties as a 'default' value. And this 'Test' class has a lot of properties.
I think the best way is to dynamically implement 'ShouldSerializeXXX' methods. Manually implementation is lacking in flexibility and not smart.

I know the way to dynamically implement a function using ’DynamicMethod class' [https://learn.microsoft.com/en-US/dotnet/api/system.reflection.emit.dynamicmethod?view=net-5.0]. But the 'ShouldSerializeXXX' function has an effect just by defining, I have no idea how to implement the function.
Can anybody tell me the way to do this? Sorry for my poor English.
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; }
      ...
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
J.Green
  • 51
  • 4
  • You can achieve it using a custom type descriptor: Look at this example: [Set the default value of DesignerSerializationVisibility to hidden](https://stackoverflow.com/q/36337249/3110834). – Reza Aghaei Jan 21 '21 at 07:05
  • Or use a proxy like this: [Hide some properties in PropertyGrid at run-time](https://stackoverflow.com/a/51618536/3110834). – Reza Aghaei Jan 21 '21 at 16:27

2 Answers2

0

Dynamically implementing a method that needs to then be discovered by reflection is not trivial, and would require dynamically creating a sub-type of Test and making sure that all your instances are actually of the sub-type - not an appealing proposition.

There are some more appealing options, though;

  1. generate the extra methods - this could be a few lines of throw-away reflection code (and a unit test that checks that they all exist) that just spits out however-many versions of public bool ShouldSerialize{Name}() => false;
  2. look into the type-descriptor/property-descriptor API; a custom type-description-provider can provide custom property-descriptors, and it is they that get to say whether something needs to be serialized or not; it is just that the default implementation looks for ShouldSerialize{Name}()

Honestly, option 1 seems by far the easiest option here - and it will involve less mess at runtime; you could get option 1 implemented in a few minutes, including the test to make sure you don't miss any new ones

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thank you for your answer. I have a question: what is 'the extra properties'? Do you mean properties in which generate 'public bool ShouldSerialize{Name}() => false;' methods? – J.Green Jan 20 '21 at 10:07
  • @J.Green I meant the extra methods instead; just auto-generate them, and write a test that checks that they all exist using reflection – Marc Gravell Jan 21 '21 at 16:25
0

The basic idea is using a custom type descriptor as it's already addressed in Marc's answer. You can see an implementation in my post here. You can make the linked post working for you easily by changing override of ShouldSerializeValue and return false. That's all.

But here I'd like to share another option, a shorter answer, which needs less effort but basically do the same for you. Using a Proxy when passing objects to PropertyGrid:

Assuming you have a common class like this:

public class MyClass
{
    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
}

This is how you use the proxy:

var myOriginalObject = new MyClass();
this.propertyGrid1.SelectedObject = new ObjectProxy(myOriginalObject);

And here is the result after changing properties:

enter image description here

This is the ObjectProxy which is a class derived from CustomTypeDescriptor will do the magic for you. And here's the class:

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;
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398