Binding a Collection as a component

G

Guest

I have a collection (PersonCollection inherits collectionbase and implements
ITypedList it's a collection of Person objects) this collection also
implements the IComponent interface, so I can drag the collection from the
toolbar to the form editor. Now the problem is when I add a new property to
the Person class, wich type is another collection (it also inherits
collectionbase and implements ITypedList) when I try to bind the new
collection to a text box or select the datamember property of the grid, it
throws an error "The specified method is not compatible"

I tryed everything and couldn't solve it, please help me.
 
B

Bart Mermuys

Hi,

Ruben said:
I have a collection (PersonCollection inherits collectionbase and
implements
ITypedList it's a collection of Person objects) this collection also
implements the IComponent interface, so I can drag the collection from the
toolbar to the form editor. Now the problem is when I add a new property
to
the Person class, wich type is another collection (it also inherits
collectionbase and implements ITypedList) when I try to bind the new
collection to a text box or select the datamember property of the grid, it
throws an error "The specified method is not compatible"

I tryed everything and couldn't solve it, please help me.

To get the properties of the items of a child-collection it will ask the
parent-collection for them using ITypedList.GetItemProperties, listAccessors
forms a path of properties to the child-collection.

And for some unknown reason IBindingList.AddNew must also be implemented, on
the other hand a custom collection without IBindingList isn't worth that
much. So if you haven't already you should implement IBindingList.

At the end i posted an example, to get basic support for hierarchical custom
collections both at design and runtime.

You can also find an example here (the ITypedList isn't sufficiently
implemented for hierarchical custom collections):
http://msdn.microsoft.com/msdnmag/issues/05/08/CollectionsandDataBinding/default.aspx

If you tried any of the code and still have problems, then post the code for
your custom item and collection.

And if you are using NET2.0, then use BindingList<T>, this already implement
IBindingList and ITypedList is handled by putting a BindingSource between
the collection and control.

HTH,
Greetings

public class PersonCollection : CollectionBase, ITypedList, IComponent,
IBindingList
{
#region Typed Methods
public int Add(Person p )
{
return List.Add(p);
}

public int IndexOf(Person p )
{
return List.IndexOf(p);
}

public void Remove(Person p)
{
List.Remove(p);
}

public Person this[int i]
{
get { return (Person)List; }
set { List = value; }
}
#endregion

#region CollectionBase overrides & notification
private void OnListChanged(ListChangedType t, int OldIndex, int NewIndex)
{
if ( ListChanged!=null )
ListChanged(this, new ListChangedEventArgs(t, OldIndex, NewIndex));
}

protected override void OnSetComplete(int index, object oldValue, object
newValue)
{
OnListChanged(ListChangedType.ItemChanged, index, index);
}

protected override void OnInsertComplete(int index, object value)
{
OnListChanged(ListChangedType.ItemAdded, index, index);
}

protected override void OnClearComplete()
{
OnListChanged(ListChangedType.Reset, -1, -1);
}

protected override void OnRemoveComplete(int index, object value)
{
OnListChanged(ListChangedType.ItemDeleted, index, index);
}
#endregion

#region ITypedList
public PropertyDescriptorCollection GetItemProperties(
PropertyDescriptor[] listAccessors )
{
if ( listAccessors == null || listAccessors.Length == 0 )
{
PropertyDescriptorCollection pdc =
TypeDescriptor.GetProperties(typeof(Person));
return pdc;
}
else
{
ITypedList childCol = null;
if ( Count > 0 )
childCol = (ITypedList) listAccessors[0].GetValue( List[0] );
else
childCol = (ITypedList)
Activator.CreateInstance(listAccessors[0].PropertyType);

// strip one property
PropertyDescriptor[] listAccessors_1 =
new PropertyDescriptor[listAccessors.Length-1];

Array.Copy( listAccessors, 1, listAccessors_1, 0,
listAccessors_1.Length);

return childCol.GetItemProperties(listAccessors_1);
}
}

public string GetListName( PropertyDescriptor[] listAccessors )
{
if ( listAccessors == null || listAccessors.Length ==0 )
return GetType().Name;
else
return listAccessors[listAccessors.Length-1].PropertyType.Name;
}
#endregion

#region IBindingList Members

public event System.ComponentModel.ListChangedEventHandler ListChanged;

public bool SupportsChangeNotification
{
get { return true; }
}

public bool AllowEdit
{
get { return true; }
}

public bool AllowNew
{
get { return true; }
}

public bool AllowRemove
{
get { return true; }
}

public object AddNew()
{
Person p = new Person("new");
int idx = List.Add(p);
// warning: if Person doesn't implement IEditableObject then you need to
// fire itemadded twice, but its already fired once because of List.Add,
so
// this is the second time, if they do implement IEditableObject then
you need
// to call this when IEditableObject.EndEdit is called on a new item.
OnListChanged(ListChangedType.ItemAdded, idx, idx);
return p;
}

public PropertyDescriptor SortProperty
{
get { return null; }
}

public int Find(PropertyDescriptor property, object key)
{
return 0;
}

public bool SupportsSorting
{
get { return false; }
}

public bool IsSorted
{
get { return false; }
}

public bool SupportsSearching
{
get { return false; }
}

public System.ComponentModel.ListSortDirection SortDirection
{
get { return new System.ComponentModel.ListSortDirection (); }
}

public void RemoveSort() { }
public void AddIndex(PropertyDescriptor property) { }
public void ApplySort(PropertyDescriptor property,
System.ComponentModel.ListSortDirection direction) { }
public void RemoveIndex(PropertyDescriptor property) { }

#endregion

#region IComponent Members
private ISite site;

public event System.EventHandler Disposed;

public ISite Site
{
get { return site; }
set { site = value; }
}

#endregion

#region IDisposable Members
public void Dispose()
{
if (Disposed!=null) Disposed(this, EventArgs.Empty);
}
#endregion
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top