我有一个包含对象列表的类。
我以前把它作为IList的类型,它映射得很好。但是,我想通过PropertyGrid控件添加在此List中添加/删除/编辑项目的功能。
因此我需要将List设置为派生自CollectionBase的Collection类型,并且包含ICustomTypeDescriptor以使其工作而不是原始IList。
如果有人可以告诉我如何使用此方法映射此列表,或者我如何修改当前的方法以使列表中的项目可通过PropertyGrid进行编辑,那么我正在徘徊。< / p>
这是我的CollectionClass,我也想弄清楚如何制作泛型,所以我可以重用它,但是这里是我必须定义的两个类,以便通过以下方式使对象列表可编辑PropertyGrid中:
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 employee 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 employee 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;
}
}
这是我的Zone类:
[ComVisible(true)]
[TypeConverter(typeof(ExpandableObjectConverter))]
[CategoryAttribute("Configuration")]
[Serializable]
public class Zone
{
#region Private Fields
private bool active;
private string dir;
private Heading heading = new Heading();
private int id;
private int intID;
private Position start = new Position();
private Position finish = new Position();
private int width;
private Position[] corners = new Position[4];
private Streets streets = new Streets();
#endregion
#region Constructors
public Zone()
{
if (Program.main != null)
{
IntID = Program.main.intID;
Intersection intersection = Program.data.Intersections.list.Find(
delegate(Intersection tInt)
{
return tInt.ID == IntID;
}
);
if (intersection != null)
{
Streets.Crossing = intersection.Streets.Crossing;
Streets.Route = intersection.Streets.Route;
}
}
}
#endregion
#region Properties
public virtual long PK { get; set; }
[Browsable(false)]
public virtual bool Active
{
get { return active; }
set { active = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The direction for the Zone.")]
public virtual string Dir
{
get { return dir; }
set { dir = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The heading for the Zone.")]
public virtual Heading Heading
{
get { return heading; }
set { heading = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The Zone Identification Number.")]
public virtual int ID
{
get { return id; }
set { id = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The Identification Number associated with the Priority Detector of the Zone.")]
public virtual int IntID
{
get { return intID; }
set { intID = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The location of the Zone's Start.")]
public virtual Position Start
{
get { return start; }
set { start = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The location of the Zone's Finish.")]
public virtual Position Finish
{
get { return finish; }
set { finish = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The width of the Zone.")]
public virtual int Width
{
get { return width; }
set { width = value; }
}
[Browsable(false)]
public virtual Position[] Corners
{
get { return corners; }
set { corners = value; }
}
[CategoryAttribute("Configuration"),
DescriptionAttribute("The streets associated with the Zone."),
DisplayName("Zone Streets")]
public virtual Streets Streets
{
get { return streets; }
set { streets = value; }
}
#endregion
}
这是我的Intersection类中包含的Zone类对象列表的原始映射:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`" lazy="false">
<id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="PK" />
<generator class="identity" />
</id>
<many-to-one class="EMTRAC.Connections.Connection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="LocalConnection" lazy="false" cascade="all">
<column name="LocalConnection_id" />
</many-to-one>
<many-to-one class="EMTRAC.Connections.Connection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Connection" lazy="false" cascade="all">
<column name="Connection_id" />
</many-to-one>
<many-to-one class="EMTRAC.Packets.Packet, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Configuration" lazy="false" cascade="all">
<column name="Configuration_id" />
</many-to-one>
<joined-subclass name="EMTRAC.Intersections.Intersection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" lazy="false">
<key>
<column name="Device_id" />
</key>
<bag name="Zones" cascade="all-delete-orphan">
<key>
<column name="Intersection_id" />
</key>
<one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bag>
<many-to-one class="EMTRAC.Intersections.Streets, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Streets" lazy="false" cascade="all">
<column name="Streets_id" />
</many-to-one>
<many-to-one class="EMTRAC.Positions.Position, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Position" lazy="false" cascade="all">
<column name="Position" />
</many-to-one>
</joined-subclass>
</class>
我最初使用的是一个简单的包b / c我正在使用IList,但是我不知道我现在会做什么或怎么做,因为Zones列表不是IList而是来自CollectionBase的ZoneCollection类
我认为它是呕吐b / c我没有将ZoneCollection类映射出来,但我不知道如何开始映射这个b / c所有类都有一个区域对象列表。我只是映射课程并在该课程中有一个包吗?
有什么建议吗?
答案 0 :(得分:3)
回到这个以防万一其他人遇到这个。
我实际上能够实现我正在寻找的功能,但需要进行一些调整。
我偏离了使用CustomCollection实现的路径,而后者又使用了IList的泛型和非泛型实现,以便能够使用NHibernate完全映射集合,并将集合显示为基础的属性可通过集合编辑器编辑的类。
映射本身非常简单。我们只需定义一个组件,将访问权限指定为属性。在组件中我们定义了包名称,我们将把包的访问权限设置为字段。我还建议将级联选项转为“all-delete-orphan”。最后,我们通常用密钥声明包,然后用它包含的类声明。这是我的映射:
<component name="Zones" access="property">
<bag name="_list" cascade="all-delete-orphan" access="field" lazy="false">
<key>
<column name="Intersection_PK" />
</key>
<one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bag>
</component>
现在,棘手的部分。物业网格因为它的预期而变得非常挑剔。
您可能知道或者可能不知道,使用NHibernate映射实现CollectionBase的类并不是一件小事。所以我决定尝试通过实现IList来解决这个问题,其中Zone将是你在Collection中存储的对象类型。
事实证明,PropertyGrid控件不喜欢这样。它需要非通用的IList显式实现,而不是通用实现。因为我在这种情况下指定了一个特定的类型,所以我认为我对我的实现没问题。但是,我错了。
事实证明,我可以实现泛型和非泛型IList,并解决了我所有的问题。我现在可以使用NHibernate映射集合,在PropertyGrid控件中显示Property,并通过CollectionEditor编辑Collection,该CollectionEditor在PropertyGrid控件中单击Collection时自动打开。这是我对Collection类的实现:
public class ZoneCollection : IList<Zone>, IList, ICustomTypeDescriptor
{
private IList<Zone> _list = new List<Zone>();
//private IList _list = new ArrayList();
public ZoneCollection()
{
//_list = new ArrayList();
}
public int IndexOf(Zone item)
{
return _list.IndexOf(item);
}
public void Insert(int index, Zone item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public Zone this[int index]
{
get
{
return _list[index];
}
set
{
_list[index] = value;
}
}
public void Add(Zone item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(Zone item)
{
return _list.Contains(item);
}
public void CopyTo(Zone[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return ((IList)_list).IsReadOnly; }
}
public bool Remove(Zone item)
{
return _list.Remove(item);
}
public IEnumerator<Zone> GetEnumerator()
{
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
int IList.Add(object value)
{
int index = Count;
Add((Zone)value);
return index;
}
bool IList.Contains(object value)
{
return Contains((Zone)value);
}
int IList.IndexOf(object value)
{
return IndexOf((Zone)value);
}
void IList.Insert(int index, object value)
{
Insert(index, (Zone)value);
}
bool IList.IsFixedSize
{
get { return ((IList)_list).IsFixedSize; }
}
bool IList.IsReadOnly
{
get { return ((IList)_list).IsReadOnly; }
}
void IList.Remove(object value)
{
Remove((Zone)value);
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = (Zone)value;
}
}
void ICollection.CopyTo(Array array, int index)
{
CopyTo((Zone[])array, index);
}
bool ICollection.IsSynchronized
{
get { return ((ICollection)_list).IsSynchronized; }
}
object ICollection.SyncRoot
{
get { return ((ICollection)_list).SyncRoot; }
}
// 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 zones
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 = (Zone)this.collection[index];
return zone.ID.ToString();
}
}
public override string Description
{
get
{
Zone 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;
}
}
特别感谢Firo让我走上了正确的道路,而Simon Mourier则帮助我为我澄清其余部分。希望将来其他人可以从中受益。
答案 1 :(得分:2)
有一些选项
1:implementing a NH custom collection
2:use an inner collection in your own collection
例如
class ZoneCollection : IList<Zone>, ICustomTypeDescriptor
{
// Must be defined as an IList and not a List for NHibernate to save correctly
private IList<Zone> _inner;
public ZoneCollection()
{
_inner = new List<Zone>();
}
public int IndexOf(Zone item)
{
return _inner.IndexOf(item);
}
// ...
}
<component name="Zones" access="nosetter.camelcase-underscore">
<bag name="_inner" access="field" cascade="all-delete-orphan">
<key>
<column name="Intersection_id" />
</key>
<one-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bag>
</component>