我使用了许多自定义类,我将对其进行解释并发布示例。在解释了它们的全部功能之后,我将尝试清楚地描述发生我的错误的条件。
首先,我使用PropertyGrid来显示几种不同类型的对象的属性。因为PropertyGrid的默认绑定(bind)不是我想要的那样具有描述性,所以我创建了一些自定义类,这些类将称为“显示”类。这些Display类的构造方法是:传入一个对象,然后创建属性,这些属性返回格式正确的字符串和对传入的真实对象的公共(public)属性(在某些情况下为方法)的描述。
我将用一些简短的示例代码来演示这一点:
这是我要在PropertyGrid中显示的对象的示例:
public class Joint
{
public Joint(...)
{...}
//properties
public string Name { get; set;}
public CustomObject CC { get; set;}
public List<CustomObject> Custom List { get; set;}
}
字符串属性“名称”在PropertyGrid中显示正常
但是,CustomObject和List的显示方式对我来说似乎非常用户友好。
因此,我尝试通过编写此类来创建解决方案:
public class DisplayJoint
{
private Joint _jnt;
public DisplayJoint(Joint jnt)
{
_jnt = jnt;
}
//properties
public string Name { get { return _jnt.Name; } }
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public DisplayCustomObject CC { get { return new DisplayCustomObject(_jnt.CC); } }
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public List<CustomObject> CustomList { get; set;}
}
如您在上面的代码中看到的,我为我的Joint类和CustomObject类都创建了特殊的DisplayClasses。在我的项目中,我有许多不同种类的对象,它们需要同一种重叠的Display Class属性。
在上方,您可以看到我在最后两个属性上方添加的线
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
这行代码解决了我的问题,即如何在propertGrid中显示CustomObject(几乎...稍后再详细介绍)。但是,对于我的“自定义列表”属性,它的工作方式不同。在“自定义列表”上,它扩展为仅显示“计数”和“容量”(列表的实际属性),这是很有意义的,但是这不是我想要的。我想查看列表中实际包含的对象。
所以这是我的复杂解决方案,最初取自this question:
我有两个用于以属性形式将对象动态添加到propertyGrid绑定(bind)列表的类。第一个(CustomClass)可以是downloaded here。它用于动态创建属性。我正在使用的第二个类(DisplayIEnumerable)是从第一个类派生的,可以找到here.
DisplayIEnumerable类循环遍历列表对象,并向其自身添加一个属性,其中包含每个对象中包含的信息。传入DisplayClass来确切定义那些对象属性应如何在Grid中表示。
到目前为止,一切正常!正如这张图片所证明的(图片不是使用所提供的类创建的,在我正在使用的类中,字符串的格式设置有所不同,删除了格式代码以帮助您专注于相关代码:
在经过漫长的介绍之后,现在才是真正的问题。使用以上技术,我想编写一个可以动态处理尚未为其编写唯一显示类的CustomObject的类。我打算将此代码留给使用该应用程序进行测试的人员使用,以便他们可以更有效地进行测试,而不必为我公司的每个CustomObjects拥有完整的Display Class。 (有数百个)相反,我希望通过将propertyGrid与下面的类绑定(bind),希望将所有具有列表的属性和具有对应DisplayClass的CustomObjects绑定(bind)在其位置。
这是我已经尝试过并遇到错误的类(class)。我还没有尝试用DisplayIEnumerable类实现List的替换,我想让基本功能首先起作用:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Reflection;
using System.Collections;
using System.Windows.Forms;
internal class DisplayObject : CustomClass<T>
{
#region Variables
protected T _obj;
#endregion
#region Constructor
public DisplayObject(T obj)
{
if (obj != null)
{
try
{
Type currentType = typeof(T);
foreach (PropertyInfo propertyInfo in currentType.GetProperties())
{
Attribute[] attributes = new Attribute[1];
if (propertyInfo.GetType() is IEnumerable)
attributes[0] = new TypeConverterAttribute(typeof(ExpandableObjectConverter));
else
attributes[0] = null;
this.Add(new CustomProperty(propertyInfo.Name, propertyInfo, propertyInfo.GetType(), false, true, attributes));
}
}
catch
{
MessageBox.Show("Failure!");
}
}
}
#endregion
#region Properties
[Browsable(false)]
public object Item
{
get { return _obj; }
set { _obj = value; }
}
#endregion
}
运行时,PropertyGrid如下显示:
但是,一旦单击“展开”箭头,则什么也没有发生,并且箭头消失了:
上面的类有什么问题,而我的DisplayIEnumerable类没有错,这会导致这种行为差异?
我正在这样使用DisplayObject类(在DisplayClass内部):
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public DisplayObject EndJoint { get { if (_member.bcEnd != null) { return new DisplayObject(_member.EndJoint); } else return null; } }
提前致谢!如果有人通过这个问题使我印象深刻。
最佳答案
您无需创建特殊的类即可使用属性网格。只需使用适当的属性装饰属性。这是一个例子:
两个自定义类:
public class MyObjType1
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public class MyObjType2
{
public string Reference { get; set; }
public override string ToString()
{
return Reference;
}
}
注意
ToString
被覆盖,如果没有为给定类型定义TypeConverter,默认情况下属性网格将使用该属性。一个“拥有者”类,具有一组自定义对象:
public class MyHolder
{
public MyHolder()
{
Objects = new List<object>();
}
public string Name { get; set; }
[TypeConverter(typeof(MyCollectionConverter))]
public List<object> Objects { get; private set; }
}
请注意直接应用于
TypeConverter
属性的自定义Objects
。来源如下:public class MyCollectionConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
IEnumerable enumerable = value as IEnumerable;
if (enumerable == null)
return base.GetProperties(context, value, attributes);
int i = 0;
List<PropertyDescriptor> list = new List<PropertyDescriptor>();
foreach (object obj in enumerable)
{
MyItemPropertyDescriptor index = new MyItemPropertyDescriptor(i.ToString(), obj);
list.Add(index);
i++;
}
return new PropertyDescriptorCollection(list.ToArray());
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != typeof(string))
return base.ConvertTo(context, culture, value, destinationType);
IEnumerable enumerable = value as IEnumerable;
if (enumerable == null)
return base.ConvertTo(context, culture, value, destinationType);
StringBuilder sb = new StringBuilder();
foreach (object obj in enumerable)
{
if (sb.Length > 0)
{
sb.Append(',');
}
sb.AppendFormat("{0}", obj);
}
return sb.ToString();
}
}
注意,我们重写了
ConvertTo
并给它一个特殊的字符串,该字符串显示列表中以逗号分隔的对象列表。 GetProperties
也被覆盖,并使用特殊的PropertyDescriptor
;它将ExpandableObjectConverter
属性添加到子对象,以便它们也可以扩展:public class MyItemPropertyDescriptor : PropertyDescriptor
{
private object _value;
public MyItemPropertyDescriptor(string name, object value)
: base(name, new[] { new TypeConverterAttribute(typeof(ExpandableObjectConverter)) })
{
_value = value;
}
public override bool IsReadOnly
{
get { return false; }
}
public override object GetValue(object component)
{
return _value;
}
public override Type PropertyType
{
get { return _value == null ? typeof(object) : _value.GetType(); }
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(object); }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
}
}
现在,这是一些示例代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MyHolder holder = new MyHolder();
for (int i = 0; i < 3; i++)
{
holder.Objects.Add(new MyObjType1 { Id = i, Name = i + "Name" });
}
for (int i = 0; i < 3; i++)
{
holder.Objects.Add(new MyObjType2 { Reference = "Ref" + i });
}
propertyGrid1.SelectedObject = holder;
}
}
结果是:
关于c# - 自定义,复杂的动态反射解决方案-C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11837812/