【问题标题】:Properties generated at runtime (PropertyGrid.SelectedObject)运行时生成的属性 (PropertyGrid.SelectedObject)
【发布时间】:2011-05-04 18:17:17
【问题描述】:

好的,这很难。

简介: 我的想法是将我编写的实例化 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;}
}

挑战:现在,我不知道如何将每个字段硬编码为 QueryBuilder 类中的公共属性,而是想知道如何使用包含我所有字段的 List<string> 来“填充”我实例化的 QueryBuilder这些属性。

所以这就引出了三个问题:

  1. 这是否可以通过以某种方式覆盖 QueryBuilder 类的 Type 的 GetProperties() 来完成,如果可以,如何最好地完成?

  2. 然后如何在运行时遍历所有这些生成的 QBField 属性并实例化它们?想法:PropertyDescriptors 和 Activators?

  3. 如何遍历所有这些属性以读取每个 QBField 对象的值?我遇到的问题是,当使用反射读取 QBField 的属性并尝试 getValue(obj, null) 时,当然需要的第一个参数是一个对象,我不知道,因为我有很多这些 QBField 对象。也许将我所有的 QBFields 放入 List<QBField> 并遍历它?这在这个例子中有用吗?

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

【问题讨论】:

    标签: c# reflection properties propertygrid


    【解决方案1】:

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

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

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

    这是一个 basic 属性包,它将所有内容公开为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); } }
        }
    
    }
    

    【讨论】:

    • 感谢 Marc 为我指明了正确的方向。到目前为止一切正常,但不知道如何创建新属性。已实现 GetProperties 并能够在那里过滤属性。但是如何添加属性(即来自我的 List?有什么想法吗?
    • @Gregor - 在这种情况下,string 是什么?我已经编写了很多这样的动态/属性包模型,首先要了解的是确切的场景。不过,我会做一个简单的快速模型......
    • @Gregor - 请注意,我在 SO 和其他地方有许多其他帖子讨论了这个更复杂的角落。恐怕这不是一个微不足道的领域。
    • @Marc - 非常感谢!这对我帮助很大,从这一点开始,我应该能够通过使用对象列表而不是字符串作为属性来完成场景。这些对象具有静态属性,因此这应该与您的示例非常相似。
    • @Gregor - 实际上您应该能够组合 PropertyDescritor 数组以获得反射和动态属性的混合。
    猜你喜欢
    • 1970-01-01
    • 2023-02-22
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    相关资源
    最近更新 更多