1

I wrote a PCL with custom objects in there, and then I create a GUI that handles the objects from the PCL... and I try to use PropertyGrid to edit the properties... I've already read that in order for the grid to know what to do with the object, I need to specify the EditorAttribute as well as providing a TypeConverter... but I don't think I could add those 2 in the PCL...

Is there a way to handle this at the GUI level, like telling the PropertyGrid to use a specific type of Editor/TypeConverter at runtime? I went through the list of available function/properties of the grid and doesn't look like is possible.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
codenamezero
  • 2,724
  • 29
  • 64
  • Look into `ICustomTypeDescriptor`, this will let you to dynamically supply type information at runtime. Namely properties and their attributes. Google for "icustomtypedescriptor propertygrid c#". – Sinatr Sep 07 '17 at 15:28
  • Yeah but don't you need to add the `TypeConverter` or implement `ICustomTypeDescriptor` in the object class itself? `ICustomTypeDescriptor` is not PCL though. – codenamezero Sep 07 '17 at 15:36
  • Registering a custom `TypeDescriptionProvider` at run-time is what you are looking for. You can create the `TypeDescriptionProvider` and `TypeDescriptor` yourself and then register the provider to your class ro provide custom type description using the type descriptor class that you crated. Also as a cleaner and simpler option, you can create a metadata class containing same properties as your original class and decorate properties of metadata class with some attributes. Then use `AssociatedMetadataTypeTypeDescriptionProvider` to provide type description for your class using metadata class. – Reza Aghaei Sep 07 '17 at 18:22

1 Answers1

4

You can create a metadata class containing same properties as your original class and decorate properties of metadata class with some attributes. Then tell the type descriptor to use the metadata class for providing type description for your original class:

var provider = new AssociatedMetadataTypeTypeDescriptionProvider(
    typeof(MyClass),
    typeof(MyClassMetadata));
TypeDescriptor.AddProvider(provider, typeof(MyPortableClass));

The PropertyGrid control uses the type descriptor of your class to show the properties of your class, their display name, their description, their editor and so on. You can assign the type descriptor in different ways.

In your case, the best solution is registering a new TypeDescriptorProvider for your class at run-time. This way you can change the appearance of your class in PropertyGrid simply at run-time.

Using AssociatedMetadataTypeTypeDescriptionProvider you can create a type descriptor provider for your class that uses a metadata class to provide type description. Then you can register the provider using TypeDescriptor.AddProvider.

This way you can introduce a metadata class for your class that contains attributes for properties.

Step by Step Example

  1. Add a portable class library to solution and add a class to it:

    public class MyClass
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }
    }
    
  2. Add reference of the portable class library to your Windows Forms project. Just make sure that the target framework is consistent.

  3. Add System.Design and System.ComponentModel.DataAnnotations references to your Windows Forms Project.

  4. In Windows Forms Project, add a meta data class for your portable class. The class should contain exactly the same properties of your original class:

    public class MyClassMetadata
    {
        [Category("My Properties")]
        [DisplayName("First Property")]
        [Description("This is the first Property.")]
        public string Property1 { get; set; }
    
        [Category("My Properties")]
        [DisplayName("Second Property")]
        [Description("This is the second Property.")]
        [Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
        public string Property2 { get; set; }
    }
    

    You need to add these usings:

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.Design;
    using System.Drawing.Design; 
    
  5. In Load event of your form, register the metadata provider for your type this way:

    var provider = new AssociatedMetadataTypeTypeDescriptionProvider(
        typeof(MyClass),    
        typeof(MyClassMetadata));
    TypeDescriptor.AddProvider(provider, typeof(MyClass));
    
  6. Show an instance of your portable class in property grid:

    var myObject = new MyClass();
    this.propertyGrid1.SelectedObject = myObject ;
    

Here is the result after running the application:

enter image description here

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Holy crap, let me try this solution out and report back, although I dislike the fact that for every object I need to create a meta object for it... I guess there is no way around it... – codenamezero Sep 07 '17 at 20:02
  • I believe it's the cleanest way :) – Reza Aghaei Sep 07 '17 at 20:02
  • It's usually used in ASP.NET MVC solutions. For example when you have some entity generated using Entity Framework (using edmx), then you can not (should not) decorate properties of generated entity with attributes. Instead you create those metadata classes. – Reza Aghaei Sep 07 '17 at 20:05
  • As another alternative solution, you can define your own attributes on portable class and then replace them at run-time using `TypeDescriptor` mechanism. For example take a look at my post here: [Combining multiple Attributes to a single Attribute](https://stackoverflow.com/q/38503146/3110834). – Reza Aghaei Sep 07 '17 at 20:08
  • Any idea how I could use `StringCollectionEditor` or maybe `StringArrayEditor`? Despite including those `using` none of the editor can be reference inside my Metadata class. – codenamezero Sep 08 '17 at 20:28
  • `StringCollectionEditor` and `StringArrayEditor` are internal. But you can use them this way: `[Editor("System.Windows.Forms.Design.StringArrayEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]` and ``[Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]`` – Reza Aghaei Sep 08 '17 at 21:06
  • 1
    In most cases using internal editors is that easy. But in some cases it's tricky. For example take a look at [How to use built-in editors for a exposed properties in User Controls - Mask Property Editor Issue](https://stackoverflow.com/q/36307725/3110834) or [How can I override Winforms DockStyle Editor Property Grid behavior](https://stackoverflow.com/q/36192542/3110834). – Reza Aghaei Sep 08 '17 at 21:13
  • By the way, I believe the question you asked in the comment is worth to be asked as a separate question and not hide in the comments ... – Reza Aghaei Sep 09 '17 at 05:58