在运行时生成的属性(PropertyGrid.SelectedObject)
好的,这是一个艰难的。
简介:我的想法是将我编写的instanciated 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
来“填充”我的instanciated QueryBuilder这些属性。
所以这导致了三个问题:
-
这可以通过以某种方式覆盖QueryBuilder类的Type的GetProperties()来实现,如果是的话,它是如何做得最好的?
-
然后,我如何在运行时生成的QBField属性迭代所有这些属性并实例化它们? 想法:PropertyDescriptors和Activators?
-
如何迭代所有这些属性来读取每个QBField对象的值? 我遇到的问题是,当用reflection读取QBField的属性并尝试getValue(obj,null)时,当然需要的第一个参数是一个对象,我不知道,因为我有很多这些QBField对象。 也许将我的所有QBField放入
List
并迭代它? 这会在这个例子中起作用吗?
我只是有点迷失,但我觉得我非常接近解决方案。 因此,非常感谢任何帮助或指向正确方向的指针!
可以通过TypeConverter
, ICustomTypeDescriptor
和/或TypeDescriptionProvider
来影响PropertyGrid
。 其中, TypeConverter
是最简单的,通过重写GetProperties
(并将其标记为支持)。
无论如何,您还需要编写一个PropertyDescriptor
实现,该实现知道如何获取字段和对象,并获取/设置值,即
public override void SetValue(object component, object value) { ((YourType)component)[fieldNameSetInConstructor] = value; }
这是一个基本的属性包,将所有内容暴露为string
; 显然,当您扩展它(不同的属性类型,更改通知等)时,它会变得更加复杂。 另请注意,此TypeConverter
方法仅适用于PropertyGrid
; 对于DataGridView
等,您需要ICustomTypeDescriptor
或TypeDescriptionProvider
。 对于集合,您需要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 properties = new List (); public List Properties { get { return properties; } } private readonly Dictionary values = new Dictionary(); 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); } } } }