我有一个具有IList属性的MyClassA类。我正在使用PropertyGrid控件来显示MyClassA的所有属性,我希望通过PropertyGrid for MyClassA显示和编辑MyClassB列表。
我目前所有其他属性都显示在属性网格中,但属性是MyClassB列表。如何将MyClassB列表添加到属性网格中,用户可以在列表中添加/编辑/删除项目?
尽管我还在挖掘,但我还没有真正找到任何详细说明的例子。
答案 0 :(得分:1)
这是迄今为止我已经解决的一个解决方案,尽管它仍然不能100%满足我的需求。
我发现此引用可以根据自己的喜好进行修改:http://www.codeproject.com/KB/tabs/customizingcollectiondata.aspx
我所做的是创建一个继承自CollectionBase并使用ICustomTypeDescriptor的新类。
在我这样做并实现了基本功能后,我不得不为该类创建一个PropertyDescriptor。
以下是代码:
public class ZoneCollection : CollectionBase, ICustomTypeDescriptor
{
#region Collection Implementation
/// <summary>
/// Adds an zone object to the collection
/// </summary>
/// <param name="emp"></param>
public void Add(Zone zone)
{
this.List.Add(zone);
}
/// <summary>
/// Removes an zone object from the collection
/// </summary>
/// <param name="emp"></param>
public void Remove(Zone zone)
{
this.List.Remove(zone);
}
/// <summary>
/// Returns an zone object at index position.
/// </summary>
public Zone this[int index]
{
get
{
return (Zone)this.List[index];
}
}
#endregion
// Implementation of interface ICustomTypeDescriptor
#region ICustomTypeDescriptor impl
public String GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public String GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
/// <summary>
/// Called to get the properties of this type. Returns properties with certain
/// attributes. this restriction is not implemented here.
/// </summary>
/// <param name="attributes"></param>
/// <returns></returns>
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return GetProperties();
}
/// <summary>
/// Called to get the properties of this type.
/// </summary>
/// <returns></returns>
public PropertyDescriptorCollection GetProperties()
{
// Create a collection object to hold property descriptors
PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
// Iterate the list of employees
for (int i = 0; i < this.List.Count; i++)
{
// Create a property descriptor for the zone item and add to the property descriptor collection
ZoneCollectionPropertyDescriptor pd = new ZoneCollectionPropertyDescriptor(this, i);
pds.Add(pd);
}
// return the property descriptor collection
return pds;
}
#endregion
}
/// <summary>
/// Summary description for CollectionPropertyDescriptor.
/// </summary>
public class ZoneCollectionPropertyDescriptor : PropertyDescriptor
{
private ZoneCollection collection = null;
private int index = -1;
public ZoneCollectionPropertyDescriptor(ZoneCollection coll, int idx) :
base("#" + idx.ToString(), null)
{
this.collection = coll;
this.index = idx;
}
public override AttributeCollection Attributes
{
get
{
return new AttributeCollection(null);
}
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get
{
return this.collection.GetType();
}
}
public override string DisplayName
{
get
{
Zone zone = this.collection[index];
return zone.ID.ToString();
}
}
public override string Description
{
get
{
Zone zone = this.collection[index];
StringBuilder sb = new StringBuilder();
sb.Append(zone.ID.ToString());
if ( zone.Streets.Route != String.Empty || zone.Streets.Crossing != String.Empty)
sb.Append("::");
if (zone.Streets.Route != String.Empty)
sb.Append(zone.Streets.Route);
if ( zone.Streets.Crossing != String.Empty)
{
sb.Append(" and ");
sb.Append(zone.Streets.Crossing);
}
return sb.ToString();
}
}
public override object GetValue(object component)
{
return this.collection[index];
}
public override bool IsReadOnly
{
get { return false; }
}
public override string Name
{
get { return "#" + index.ToString(); }
}
public override Type PropertyType
{
get { return this.collection[index].GetType(); }
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
public override void SetValue(object component, object value)
{
// this.collection[index] = value;
}
}
Intersection现在包含一个ZoneCollection而不是IList,我现在可以编辑/添加/删除集合中包含的区域。
现在,如果我能让这个更通用,我会相对高兴。我的模型的另一个障碍是我必须使用它而不是IList从Collection base继承。这完全破坏了我的NHibernate类的映射,我现在不得不尝试使用上面提到的方法重新映射这个列表。
如果有人想进一步阐述这一点,我会非常感谢更多的见解。
答案 1 :(得分:0)
我知道这个主题已经超过2年了,但也许这对你来说很有意思。
我遇到了类似的问题。 从:我需要一个3D空间中的Point,它应该可以在Property-Grid中配置 为此,我创建了一个Class Koord。为了使它在PropertyGrid中可以更改,我创建了一个新的类“KoordConverter:TypeConverter” 这用于Vexel(检查维基百科以找出它的用途:-))
要创建一个TestBock(一些3D对象),我正在使用一个Vexels列表。 不幸的是,我需要一个程序列表,通过Property-Grid可以看到我的程序。
开始最顶层:
public partial class FormMain : Form
{
private BlockProperties _bp = new BlockProperties();
public FormMain()
{
InitializeComponent();
pgProperties.SelectedObject = _bp;
}
[...]
}
Class BlockProperties包括TestBocks列表,我填写了一些,以显示内部的内容。
class BlockProperties
{
public List<TestBocks> Testing { get; set; }
public BlockProperties()
{
Testing = new List<TestBocks>(3);
List<Vexel> t1 = new List<Vexel>(1);
t1.Add(new Vexel(new Koord(1,0,1), 1));
List<Vexel> t2 = new List<Vexel>(2);
t2.Add(new Vexel(new Koord(2, 0, 1), 2));
t2.Add(new Vexel(new Koord(2, 0, 2), 2));
List<Vexel> t3 = new List<Vexel>(3);
t3.Add(new Vexel(new Koord(3, 0, 1), 3));
t3.Add(new Vexel(new Koord(3, 0, 2), 3));
t3.Add(new Vexel(new Koord(3, 0, 3), 3));
TestBocks tb1 = new TestBocks();
tb1.Koords = t1;
TestBocks tb2 = new TestBocks();
tb2.Koords = t2;
TestBocks tb3 = new TestBocks();
tb3.Koords = t3;
Testing.Add(tb1);
Testing.Add(tb2);
Testing.Add(tb3);
[...]
}
[...]
}
接下来是我的TestBlock类,它简单直接
[Serializable]
public class TestBocks
{
public List<Vexel> Vexels{ get; set; }
public TestBocks()
{
Vexels = new List<Vexel>();
}
}
在Vexels中,我的程序需要的大部分魔力: 我甚至在这里放了一个ToString(),以便在调试过程中轻松实现。
public class Vexel
{
private Koord _origin;
private double _extent;
public Koord Origin { get { return _origin; } set { _origin = value; } }
public double Extent { get { return _extent; } set { _extent = value; } }
public string ToString()
{
NumberFormatInfo nFormatInfo = new NumberFormatInfo
{
NumberDecimalSeparator = ".",
NumberGroupSeparator = ""
};
return String.Format(nFormatInfo, "Origin;{0};{1};{2};Extent;{3}", _origin.X, _origin.Y, _origin.Z, _extent);
}
public Vexel()
{
_origin = new Koord(0,0,0);
Extent = 0;
}
public Vexel(Koord origin, double extent)
{
//TODO do some checking
_origin = origin;
_extent = extent;
}
到目前为止,一切都运行良好的PropertyGrid,但我无法编辑Koords。 Class非常简单,但在PropertyGrid中无法编辑。 添加TypeConverterClass解决了这个问题(你可以在Koord的代码下找到TypeConverter)
[TypeConverter(typeof(KoordConverter))]
[Serializable]
public class Koord
{
private double p_1;
private double p_2;
private double p_3;
public Koord(double x, double y, double z)
{
this.p_1 = x;
this.p_2 = y;
this.p_3 = z;
}
public string ToString()
{
return String.Format("X;{0};Y;{1};Z;{2}", p_1, p_2, p_3);
}
public double X { get { return p_1; } }
public double Y { get { return p_2; } }
public double Z { get { return p_3; } }
}
Typeconverter是最复杂的代码。你可以在下面找到它:
public class KoordConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string text = value as string;
if (text == null)
{
return base.ConvertFrom(context, culture, value);
}
string text2 = text.Trim();
if (text2.Length == 0)
{
return null;
}
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
char c = culture.TextInfo.ListSeparator[0];
string[] array = text2.Split(new char[]
{
c
});
int[] array2 = new int[array.Length];
TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
for (int i = 0; i < array2.Length; i++)
{
array2[i] = (int)converter.ConvertFromString(context, culture, array[i]);
}
if (array2.Length == 3)
{
return new Koord(array2[0], array2[1], array2[2]);
}
throw new ArgumentException("TextParseFailedFormat");
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == null)
{
throw new ArgumentNullException("destinationType");
}
if (value is Koord)
{
if (destinationType == typeof(string))
{
Koord Koord = (Koord)value;
if (culture == null)
{
culture = CultureInfo.CurrentCulture;
}
string separator = culture.TextInfo.ListSeparator + " ";
TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
string[] array = new string[3];
int num = 0;
array[num++] = converter.ConvertToString(context, culture, Koord.X);
array[num++] = converter.ConvertToString(context, culture, Koord.Y);
array[num++] = converter.ConvertToString(context, culture, Koord.Z);
return string.Join(separator, array);
}
if (destinationType == typeof(InstanceDescriptor))
{
Koord Koord2 = (Koord)value;
ConstructorInfo constructor = typeof(Koord).GetConstructor(new Type[]
{
typeof(double),
typeof(double),
typeof(double)
});
if (constructor != null)
{
return new InstanceDescriptor(constructor, new object[]
{
Koord2.X,
Koord2.Y,
Koord2.Z
});
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
if (propertyValues == null)
{
throw new ArgumentNullException("propertyValues");
}
object obj = propertyValues["X"];
object obj2 = propertyValues["Y"];
object obj3 = propertyValues["Z"];
if (obj == null || obj2 == null || obj3 == null || !(obj is double) || !(obj2 is double) || !(obj3 is double))
{
throw new ArgumentException("PropertyValueInvalidEntry");
}
return new Koord((double)obj, (double)obj2, (double)obj3);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(Koord), attributes);
return properties.Sort(new string[]
{
"X",
"Y",
"Z"
});
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
基本上在设置了所有这些之后,修改任何对象列表(TestBlocks或每个TestBlock中的Vexels)都没有问题。 如果他们跨过这个话题,希望它可以帮到某人。
最好的问候
罗宾血液PS:在PropertyGrid中编辑是没有问题的,也许你只是没有让你的构造函数正确! http://i.stack.imgur.com/LD3zf.png