Question

  • I use a PropertyGrid to allow the enduser to edit properties from a class ClassA
  • This class has a List<ClassB> property.
  • For the List<ClassB> property, the PropertyGrid shows (Collection) and a button with 3 dots which opens a new windows which looks like this (taken from another SO post).

enter image description here

  • I want to customize the Members: DisplayName on the left, so for ClassB I have overridden the ToString() method

    public class ClassB
    {
        public string Name { get; set; }
        public TimeSpan Value { get; set; }
    
        public override ToString() { return String.Format("{0} ({1})", this.Name, this.Value); }
    }
    

Now here comes the problem:

  • If Name is empty, it shows (00:00:00) as expected.
  • If I change the name to Test I expect it to show Test (00:00:00), but it just shows Test
  • If I rename the property Name to something else, it works as expected.

I suppose this is a special conventions that, if a class has a property Name and the value is not null or empty, the control shows this property rather than the name.

However, I have not found a doc that verifies that and I don't know how to change this behaviour. How do I achive this?

Note: Changing the property name is not an option.

Was it helpful?

Solution

Unfortunately, the logic is pretty hardcoded in the CollectionEditor.GetDisplayText Method. It's not documented, but you can disassemble it with a tool. This is the code:

protected virtual string GetDisplayText(object value)
{
    string str;
    if (value == null)
        return string.Empty;

    // use the Name property
    PropertyDescriptor defaultProperty = TypeDescriptor.GetProperties(value)["Name"];
    if ((defaultProperty != null) && (defaultProperty.PropertyType == typeof(string)))
    {
        str = (string) defaultProperty.GetValue(value);
        if ((str != null) && (str.Length > 0))
        {
            return str;
        }
    }

    // or use the DefaultPropertyAttribute
    defaultProperty = TypeDescriptor.GetDefaultProperty(this.CollectionType);
    if ((defaultProperty != null) && (defaultProperty.PropertyType == typeof(string)))
    {
        str = (string) defaultProperty.GetValue(value);
        if ((str != null) && (str.Length > 0))
        {
            return str;
        }
    }

    // or use the TypeConverter
    str = TypeDescriptor.GetConverter(value).ConvertToString(value);
    if ((str != null) && (str.Length != 0))
    {
        return str;
    }

    // or use the type name
    return value.GetType().Name;
}

This code is pretty nasty because it does things basically the other way around. It should use the Name property as the last resort instead of focusing on it...

But all hope is not lost since the CollectionEditor class is not sealed. This is how you can fix it:

1) declare the EditorAttribute on the class holding the collection, something like this:

public class ClassA
{
    [Editor(typeof(MyCollectionEditor), typeof(UITypeEditor))]
    public List<ClassB> List { get; set; }
}

2) define you custom collection editor, like this;

public class MyCollectionEditor : CollectionEditor // needs a reference to System.Design
{
    public MyCollectionEditor(Type type)
        : base(type)
    {
    }

    protected override string GetDisplayText(object value)
    {
        // force ToString() usage, but
        // you also could implement some custom logic here
        return string.Format("{0}", value);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top