Properties generated at runtime (PropertyGrid.SelectedObject)

落花浮王杯 提交于 2019-11-29 08:17:26

PropertyGrid can be influenced via TypeConverter, ICustomTypeDescriptor and/or TypeDescriptionProvider. Of these, TypeConverter is the simplest, by overriding GetProperties (and mark it as supported).

In any event, you will also need to write a PropertyDescriptor implementation that knows how to take a field and an object, and get/set the value, i.e.

public override void SetValue(object component, object value) {
    ((YourType)component)[fieldNameSetInConstructor] = value;
}

Here's a basic property bag that exposes everything as string; obviously as you extend this (different property types, change-notification, etc) it gets more complex very quickly. Note also that this TypeConverter approach only works for PropertyGrid; for DataGridView etc you'll need either ICustomTypeDescriptor or TypeDescriptionProvider. For collections you'll need ITypedList. And there are about 20 other interfaces around the edges for specific scenarios. But you get the point ;p The key thing is that our PropertyDescriptor acts as the translation between your actual model (the dictionary in my case), and the model you expose to TypeDescriptor (the fake properties per key).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;


static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        var bag = new BasicPropertyBag { Properties = {
            new MetaProp("Name", typeof(string)),
            new MetaProp("Description", typeof(string)),
            new MetaProp("DateOfBirth", typeof(DateTime)
                , new CategoryAttribute("Personal"), new DisplayNameAttribute("Date Of Birth"))
        } };
        bag["Name"] = "foo";
        bag["DateOfBirth"] = DateTime.Today;
        Application.Run(new Form { Controls = { new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = bag } } });
    }
}

public class MetaProp
{
    public MetaProp(string name, Type type, params Attribute[] attributes)
    {
        this.Name = name;
        this.Type = type;
        if (attributes != null)
        {
            Attributes = new Attribute[attributes.Length];
            attributes.CopyTo(Attributes, 0);
        }
    }
    public string Name { get; private set; }
    public Type Type { get; private set; }
    public Attribute[] Attributes { get; private set; }
}

[TypeConverter(typeof(BasicPropertyBagConverter))]
class BasicPropertyBag
{

    private readonly List<MetaProp> properties = new List<MetaProp>();
    public List<MetaProp> Properties { get { return properties; } }
    private readonly Dictionary<string, object> values = new Dictionary<string, object>();

    public object this[string key]
    {
        get { object value; return values.TryGetValue(key, out value) ? value : null; }
        set { if (value == null) values.Remove(key); else values[key] = value; }
    }

    class BasicPropertyBagConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            PropertyDescriptor[] metaProps = (from prop in ((BasicPropertyBag)value).Properties
                                              select new PropertyBagDescriptor(prop.Name, prop.Type, prop.Attributes)).ToArray();
            return new PropertyDescriptorCollection(metaProps);
        }
    }
    class PropertyBagDescriptor : PropertyDescriptor
    {
        private readonly Type type;
        public PropertyBagDescriptor(string name, Type type, Attribute[] attributes)
            : base(name, attributes) {
            this.type = type;
        }
        public override Type PropertyType { get { return type; } }
        public override object GetValue(object component) { return ((BasicPropertyBag)component)[Name]; }
        public override void SetValue(object component, object value) { ((BasicPropertyBag)component)[Name] = (string)value; }
        public override bool ShouldSerializeValue(object component) { return GetValue(component) != null; }
        public override bool CanResetValue(object component) { return true; }
        public override void ResetValue(object component) { SetValue(component, null); }
        public override bool IsReadOnly { get { return false; } }
        public override Type ComponentType { get { return typeof(BasicPropertyBag); } }
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!