DataGridView: properties of T not shown when using ICollection<T>, but shown when using List<T>
I have a class that implements ICollection<T>
, because I don’t want all functions of a List<T>
. I also need to register an event handler inside the Add
method. My whole code with sample data:
public Form1() { InitializeComponent(); Random rnd = new Random(); List<ElementContainer> list = new List<ElementContainer>(); for (int i = 0; i < 9; i++) { ElementContainer c = new ElementContainer() { Name = "Name " + (i + 1).ToString(), Number = Guid.NewGuid().ToString() }; for (int j = 0; j < 12; j++) { Element el = new Element() { Name = "Element " + (12 * i + j).ToString(), Value1 = rnd.NextDouble(), Value2 = rnd.Next(10, 100), Value3 = rnd.Next(100, 1000) }; c.Elements.Add(el); } list.Add(c); } ContainerBindingSource.DataSource = list; CollectionBindingSource.DataSource = ContainerBindingSource; CollectionBindingSource.DataMember = "Elements"; elementsDataGridView.AutoGenerateColumns = true; elementsDataGridView.DataSource = CollectionBindingSource; } } public class ElementContainer { public ElementContainer() { Elements = new ElementCollection(); } public ElementCollection Elements { get; set; } public string Name { get; set; } public string Number { get; set; } } public class ElementCollection : ICollection<Element> { private readonly List<Element> internList = new List<Element>(); public int Count => internList.Count; public bool IsReadOnly => false; public void Add(Element item) => internList.Add(item); public void Clear() => internList.Clear(); public bool Contains(Element item) => internList.Contains(item); public void CopyTo(Element[] array, int arrayIndex) => internList.CopyTo(array, arrayIndex); public bool Remove(Element item) => internList.Remove(item); public IEnumerator<Element> GetEnumerator() => internList.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => internList.GetEnumerator(); } //public class ElementCollection : List<Element> //{ } public class Element { public string Name { get; set; } public double Value1 { get; set; } public double Value2 { get; set; } public double Value3 { get; set; } }
At first sight everything looks good and as expected. BUT when using a BindingNavigator to move between the records I get an exception:
The BindingSource thinks the underlying type is ElementCollection
instead of Element
, even though the CollectionBindingSource
has DataMember
set to "Elements". If I would set up all the binding via Visual Studios Form designer the DataGridView would show me the columns "Count" and "IsReadOnly", which actually match ElementCollection
. It looks like this:
Remember: This is the result only when using the designer for managing the DataBinding!
Now the different part with List<T>
. Below is the complete code I used.
public Form1() { InitializeComponent(); Random rnd = new Random(); List<ElementContainer> list = new List<ElementContainer>(); for (int i = 0; i < 9; i++) { ElementContainer c = new ElementContainer() { Name = "Name " + (i + 1).ToString(), Number = Guid.NewGuid().ToString() }; for (int j = 0; j < 12; j++) { Element el = new Element() { Name = "Element " + (12 * i + j).ToString(), Value1 = rnd.NextDouble(), Value2 = rnd.Next(10, 100), Value3 = rnd.Next(100, 1000) }; c.Elements.Add(el); } list.Add(c); } ContainerBindingSource.DataSource = list; CollectionBindingSource.DataSource = ContainerBindingSource; CollectionBindingSource.DataMember = "Elements"; elementsDataGridView.AutoGenerateColumns = true; elementsDataGridView.DataSource = CollectionBindingSource; } } public class ElementContainer { public ElementContainer() { Elements = new ElementCollection(); } public ElementCollection Elements { get; set; } public string Name { get; set; } public string Number { get; set; } } public class ElementCollection : List<Element> { } public class Element { public string Name { get; set; } public double Value1 { get; set; } public double Value2 { get; set; } public double Value3 { get; set; } }
As you can see I replaced the definition of ElementCollection
. It’s not implementing ICollection<Element>
anymore. Instead it inherits the whole List<Element>
. Now I can freely move between all records without any exceptions.
Why doesn’t recognize the DataGridView
the underlying type correctly when I use a type that implements ICollection<T>
? But recognize it correctly when I use a type which inherits from List<T>
? How do I have to implement ElementCollection
to work as expected? Remember the fact that I cannot use List<T>
as I have to handle events from underlying elements. A List<T>
would make it more complicated and inconsistent.
Any help is appreciated.
Why doesn’t detect the DataGridView
the properties of Element
when using ICollection<T>
? This also happens when I implement IList<T>
. What makes the class List<T>
different?