我正遇到PropertyGrid的显示问题。
我有一个名为 Product 的对象,它具有 Fields 属性作为 List
我在许多在线文章中都使用了自定义TypeConverters和PropertyDescriptors,并且实现了以下行为:
正如预期的那样,字段被很好地扩展了,但是我试图不将其扩展为一个单独的子类别,我只需要与根成员处于同一级别的字段成员即可。
现在,由于Product是可绑定的对象,因此我正在尝试使用转换器来实现此功能(即,不要循环并仅填充PG或创建一个新对象)。
我尝试了很多事情,是否可以欺骗TypeConverter来做到这一点?这是功能代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Product product = new Product
{
Symbol = "test",
Details = new PartDetails
{
FileLineNo = 123,
Orientation = "up",
X = 555,
Y = 888
},
Fields = new FieldList {
new Field { Name = "One", Value = "Value 1" },
new Field { Name = "Two", Value = "Value 2" },
new Field { Name = "Three", Value = 1234 }
}
};
propertyGrid1.SelectedObject = product;
propertyGrid1.ExpandAllGridItems();
}
}
public class Product
{
public string Symbol { get; set; }
[TypeConverter(typeof(FieldListTypeConverter))]
public FieldList Fields { get; set; }
[TypeConverter(typeof(ExpandableObjectConverter))]
public PartDetails Details { get; set; }
}
public class PartDetails
{
public int FileLineNo { get; set; }
public int X { get; set; }
public int Y { get; set; }
public string Orientation { get; set; }
}
public class Field
{
public string Name { get; set; }
public object Value { get; set; }
}
public class FieldList :
List<Field>
//, ICustomTypeDescriptor
{
}
public class FieldListTypeConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(string))
return base.ConvertTo(context, culture, value, destinationType);
return "";
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object obj, Attribute[] attributes)
{
List<PropertyDescriptor> pdList = new List<PropertyDescriptor>();
List<Field> fields = obj as List<Field>;
if (fields != null)
{
foreach (Field field in fields)
{
FieldDescriptor fd = new FieldDescriptor(field);
pdList.Add(fd);
}
}
return new PropertyDescriptorCollection(pdList.ToArray());
}
private class FieldDescriptor : SimplePropertyDescriptor
{
public Field field { get; private set; } // instance
public FieldDescriptor(Field field)
// component type, property name, property type
: base(field.GetType(), field.Name, field.Value.GetType())
{
this.field = field;
}
public override object GetValue(object obj)
{
return field.Value;
}
public override void SetValue(object obj, object value)
{
field.Value = value;
}
public override bool IsReadOnly
{
get { return false; }
}
}
}
答案 0 :(得分:1)
总的来说,您的想法正确,但是实现错误。
如果您希望字段显示为产品的属性,则产品必须为字段中的每个项目本身提供一个PropertyDescriptor。您可以使用应用于Product类的TypeConverter来实现此目的。
[TypeConverter(typeof(ProductTypeConverter))]
public class Product
{
public string Symbol { get; set; }
//[TypeConverter(typeof(FieldListTypeConverter))]
public FieldList Fields { get; set; }
[TypeConverter(typeof(ExpandableObjectConverter))]
public PartDetails Details { get; set; }
}
使用:
public class ProductTypeConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(string))
{
return base.ConvertTo(context, culture, value, destinationType);
}
return "";
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object instance, Attribute[] attributes)
{
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(instance, attributes, true);
PropertyDescriptor fieldsDescriptor = pdc.Find("Fields", false);
List<PropertyDescriptor> pdList = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in pdc)
{
if (pd == fieldsDescriptor)
{
List<Field> fields = ((Product)instance).Fields;
if (fields != null)
{
foreach (Field field in fields)
{
FieldDescriptor fd = new FieldDescriptor(field);
pdList.Add(fd);
}
}
}
else
{
pdList.Add(pd);
}
}
return new PropertyDescriptorCollection(pdList.ToArray());
}
private class FieldDescriptor : SimplePropertyDescriptor
{
private Field privatefield;
public Field field
{
get
{
return privatefield;
}
private set
{
privatefield = value;
}
}
public FieldDescriptor(Field field) : base(field.GetType(), field.Name, field.Value.GetType())
{
// component type, property name, property type
this.field = field;
}
public override object GetValue(object obj)
{
return field.Value;
}
public override void SetValue(object obj, object value)
{
field.Value = value;
}
public override bool IsReadOnly
{
get
{
return false;
}
}
}
}
请注意,从“字段”添加的属性不会分组在一起,并且会与其他未分类的属性(Symbol
)一起按字母顺序显示。
答案 1 :(得分:1)
要自定义对象的属性列表,可以为对象使用自定义类型描述符。为此,可以使用以下任一选项:
ICustomTypeDescriptor
CustomTypeDescriptor
TypeDescriptor
并将其注册为您的类或对象实例示例
在此示例中,我创建了一个名为MyClass
的类,该类具有自定义属性的列表。通过为该类实现ICustomTypeDescriptor
,我将在属性网格中像普通属性一样显示List<CustomProperty>
。
使用此机制时,自定义属性也可以用于数据绑定。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class MyClass : ICustomTypeDescriptor
{
public string OriginalProperty1 { get; set; }
public string OriginalProperty2 { get; set; }
public List<CustomProperty> CustomProperties { get; set; }
#region ICustomTypeDescriptor
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(this, true);
public string GetClassName() => TypeDescriptor.GetClassName(this, true);
public string GetComponentName() => TypeDescriptor.GetComponentName(this, true);
public TypeConverter GetConverter() => TypeDescriptor.GetConverter(this, true);
public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(this, true);
public PropertyDescriptor GetDefaultProperty()
=> TypeDescriptor.GetDefaultProperty(this, true);
public object GetEditor(Type editorBaseType)
=> TypeDescriptor.GetEditor(this, editorBaseType, true);
public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(this, true);
public EventDescriptorCollection GetEvents(Attribute[] attributes)
=> TypeDescriptor.GetEvents(this, attributes, true);
public PropertyDescriptorCollection GetProperties() => GetProperties(new Attribute[] { });
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = TypeDescriptor.GetProperties(this, attributes, true)
.Cast<PropertyDescriptor>()
.Where(p => p.Name != nameof(this.CustomProperties))
.Select(p => TypeDescriptor.CreateProperty(this.GetType(), p,
p.Attributes.Cast<Attribute>().ToArray())).ToList();
properties.AddRange(CustomProperties.Select(x => new CustomPropertyDescriptor(this, x)));
return new PropertyDescriptorCollection(properties.ToArray());
}
public object GetPropertyOwner(PropertyDescriptor pd) => this;
#endregion
}
自定义属性
此类模拟一个自定义属性:
public class CustomProperty
{
public string Name { get; set; }
public object Value { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string Category { get; set; } = "Custom Properties";
}
CustomPropertyDescriptor
此类是描述CustomProperty
的自定义属性描述符:
public class CustomPropertyDescriptor : PropertyDescriptor
{
object o;
CustomProperty p;
internal CustomPropertyDescriptor(object owner, CustomProperty property)
: base(property.Name, null) { o = owner; p = property; }
public override Type PropertyType => p.Value?.GetType() ?? typeof(object);
public override void SetValue(object c, object v) => p.Value = v;
public override object GetValue(object c) => p.Value;
public override bool IsReadOnly => false;
public override Type ComponentType => o.GetType();
public override bool CanResetValue(object c) => false;
public override void ResetValue(object c) { }
public override bool ShouldSerializeValue(object c) => false;
public override string DisplayName => p.DisplayName ?? base.DisplayName;
public override string Description => p.Description ?? base.Description;
public override string Category => p.Category ?? base.Category;
}
用法
private void Form1_Load(object sender, EventArgs e)
{
var o = new MyClass();
o.CustomProperties = new List<CustomProperty>()
{
new CustomProperty
{
Name ="Property1",
DisplayName ="First Property",
Value ="Something",
Description = "A custom description.",
},
new CustomProperty{ Name="Property2", Value= 100},
new CustomProperty{ Name="Property3", Value= Color.Red},
};
propertyGrid1.SelectedObject = o;
}