在运行时生成的属性(PropertyGrid.SelectedObject)

Gre*_*vre 3 c# reflection propertygrid properties

好的,这是一个艰难的.

简介:我的想法是连接我写了一个实例化的QueryBuilder类,到PropertyGrid中.QueryBuilder类现在包含几个字段,这些字段是硬编码的,如下例所示.因此,允许用户指定,哪些字段应该以何种方式(排序,分组等)的查询使用.在用户为这些属性指定了所有设置后(通过代码或通过PropertyGrid GUI),QueryBuilder能够生成查询.一切都很好.伪代码:

class QueryBuilder {
  public QBField name {get; set;}
  public QBField prename {get; set;}
  public QBField zip {get; set;}
  // ...

  public void QueryBuilder() {
    name = new QBField();
    prename = new QBField();
    // ...
  }

  public getQuery() {
    // logic to build the query
  }
}

class QBField {
  public bool shown {get; set;}
  public bool sortby {get; set;}
  public bool groupby {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

挑战:现在不是硬编码的每个字段作为QueryBuilder的类公共属性,我不知道我怎么会用即List<string>包含所有我的领域"填充"具有这些属性我实例化QueryBuilder的.

所以这导致了三个问题:

  1. 难道这由QueryBuilder的类的类型莫名其妙压倒一切的GetProperties()来完成,如果是,它是如何做得最好?

  2. 然后我如何在运行时生成QBField属性并在实例化时迭代所有这些?想法:PropertyDescriptors和Activators?

  3. 如何迭代所有这些属性来读取每个QBField对象的值?我遇到的问题是,当用反射读取QBField的属性并尝试getValue(obj,null)时,当然需要的第一个参数是一个对象,我不知道,因为我有很多这些QBField对象.也许把我所有的QBField都放进去List<QBField>并迭代它?这会在这个例子中起作用吗?

我只是有点迷失,但我觉得我非常接近解决方案.因此,非常感谢任何帮助或只是指向正确的方向!

Mar*_*ell 5

PropertyGrid可通过影响TypeConverter,ICustomTypeDescriptor和/或TypeDescriptionProvider.其中,TypeConverter最简单的是,通过覆盖GetProperties(并将其标记为支持).

无论如何,您还需要编写一个PropertyDescriptor知道如何获取字段和对象的实现,并获取/设置值,即

public override void SetValue(object component, object value) {
    ((YourType)component)[fieldNameSetInConstructor] = value;
}
Run Code Online (Sandbox Code Playgroud)

这是一个基本的属性包,暴露一切为string; 显然,当您扩展它(不同的属性类型,更改通知等)时,它会变得更加复杂.另请注意,此TypeConverter方法仅适用于PropertyGrid; 为DataGridView等您将需要ICustomTypeDescriptorTypeDescriptionProvider.对于您需要的收藏品ITypedList.对于特定场景,边缘周围还有大约20个其他接口.但是你得到了重点; p关键是我们的PropertyDescriptor行为是你的实际模型(在我的情况下是字典)和你暴露的模型TypeDescriptor(每个键的假属性)之间的转换.

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

}
Run Code Online (Sandbox Code Playgroud)