none
DataGridView not binding to implemented IList class?

    Question

  • I have a class that implements IList, yet the datagridview does not appear to be binding to it.  There are no errors, just no data is ever displayed.  I have read in several places that an object that implements IList should bind the same as an ordinary List, which makes sense.  But when I switch out my custom List for a generic list, the datagridview binds normally, which suggests the problem lies with implementing IList.  So I guess I am first just searching for some reassurance that this should work.  I am using VB2008, my implemented list has some events, but I don't see how either of those could possibly matter. 
    Thursday, February 26, 2009 12:10 AM

Answers

  • Hi Tekito,

    a generic list always enables type hinting. Whenever you are using IList<> or List<>, it's possible to state the contained object's type, regardless of whether the list is empty or not. Thus DataGridView will be able to retriee type information from the list. Using IList only, this is not (always) possible.

    An object that implements IList will bind mostly reliable, if:
      the list is a generic,
      the list implements ICustomTypeDescriptor or ITypedList.
      the list offers an indexer, which will enable type hinting.
      the list contains only objects of the same type and the list is not empty at this time.
      (this might be incomplete...)

    You should not expect major performace penalties on using IBindingList instead of IList. But this depends on how IBindingList is implemented....BindingList<> is quite good, I guess....never tried any profiling on this class.

    It does work if you employ a BindingSource, because BindingSource does a lot of investigation on the contained object's type. It queries the properties for an indexer to gain type information, it searches for the ITypedList interface and ICustomTypeDescritpor and IBindingList (of course). At least the list should be enumerable...and if it contains any objects, BindingSource will take the fist item as some kind of sample.

    What do you mean by, "[...] can't bind to the datagridview (even after adding some items) [...]" ? Of course, you can't bind to DataGridView >>after<< adding some items.
    If you set the DataGridView.DataSource property >>before<< adding items to the list, the grid will use System.Object as sample object, which does not expose any properties. Using a generic list (like List<>) solves this problem, because the grid can get type information using reflection.

    If I'm completely missing something, or this doesn' t help you at all,, please post some code to clarify.

    regards,
    franking

    • Marked as answer by Tekito Tuesday, March 03, 2009 11:09 PM
    Monday, March 02, 2009 10:48 PM

All replies

  • And for those with experience, would attempting to use a bindingsource as an intermediary be a more robust method?
    Thursday, February 26, 2009 12:15 AM
  • I was able to bind my custom list just fine using bindingsource as an intermediary.  That still leaves me wondering why I could not directly bind directly to the datagridview - as long as my object is implementing IList why shouldn't it work?  Not that I care that badly, as long as one approach works.  Adding or removing list members are not automatically reflected to the datagridview, but that is a separate issue and one that I am sure has been worked out already on this forum.
    Thursday, February 26, 2009 7:34 PM
  • Hi Tekito,

    you can you IList as source for DataGridView. But IList itself can't incorporate with DataGridView because it lacks support for change notifications. You can only use it as long as the grid is read-only. And you can even use it without a BindingsSource inbetween.

    If the list is empty, the grid won't be able to show any columns unless you specify the explicitly. This is because the grid can't retrieve any type information about the list items. As soon as you add some objects to the list, the grid will also be able to bind to the object's properties.

    IList might contain many objects which are not of the same type. This is a problem, because it won't work properly with DataGridView if the objects are completely different from one to another. However, IList is the base interface for all other list based implementations or interfaces...

    To come to an end: Use IBindingList instead! If you want to use the list view DataGridView, you should also implement IBindingListView to support sorting and filtering. For most cases, it's not necessary to implement IBindingList. You can derive from BindingList<>. But that's up to you.


    regards,
    franking





    Thursday, February 26, 2009 8:56 PM
  • Thanks for the explanations.  However, I don't think I am having the problems you are describing.  I can bind to a list normally (with or without use of a bindingsource) and have the columns show up fine (all my list's objects are of the same type).  My confusion is that when I switch out the generic list instead for an object that implements IList, I cannot bind to the datagridview (even after adding some items) without using a bindingsource (but then it DOES work if I do use a bindingsource).  So my main question is why an object that implements IList would bind differently than a list, under some circumstances?

    That being said, you are right, a bindinglist appears to be easiest way to go by far.  And since there are ways around this issue (either via bindinglist or bindingsource) then it probably isn't worth probing further.  I would still like to know what the performance of bindinglist verse generic list is though. 

    Sunday, March 01, 2009 11:57 PM
  • Hi Tekito,

    Would you post some of your codes? IList is the least interface we need to implment to support databinding. I'm interesting to know in which circumstances the behavior will turn out to be different.

    Best regards,
    Bruce Zhou


    Please mark the replies as answers if they help and unmark if they don't.
    Monday, March 02, 2009 9:36 AM
  • Hi Tekito,

    a generic list always enables type hinting. Whenever you are using IList<> or List<>, it's possible to state the contained object's type, regardless of whether the list is empty or not. Thus DataGridView will be able to retriee type information from the list. Using IList only, this is not (always) possible.

    An object that implements IList will bind mostly reliable, if:
      the list is a generic,
      the list implements ICustomTypeDescriptor or ITypedList.
      the list offers an indexer, which will enable type hinting.
      the list contains only objects of the same type and the list is not empty at this time.
      (this might be incomplete...)

    You should not expect major performace penalties on using IBindingList instead of IList. But this depends on how IBindingList is implemented....BindingList<> is quite good, I guess....never tried any profiling on this class.

    It does work if you employ a BindingSource, because BindingSource does a lot of investigation on the contained object's type. It queries the properties for an indexer to gain type information, it searches for the ITypedList interface and ICustomTypeDescritpor and IBindingList (of course). At least the list should be enumerable...and if it contains any objects, BindingSource will take the fist item as some kind of sample.

    What do you mean by, "[...] can't bind to the datagridview (even after adding some items) [...]" ? Of course, you can't bind to DataGridView >>after<< adding some items.
    If you set the DataGridView.DataSource property >>before<< adding items to the list, the grid will use System.Object as sample object, which does not expose any properties. Using a generic list (like List<>) solves this problem, because the grid can get type information using reflection.

    If I'm completely missing something, or this doesn' t help you at all,, please post some code to clarify.

    regards,
    franking

    • Marked as answer by Tekito Tuesday, March 03, 2009 11:09 PM
    Monday, March 02, 2009 10:48 PM
  • Franking, thanks for the reply, that was quite helpful, as I didn't understand your previous post initially.  From reading these details, it sounds like a lot more is going on "under the hood" than I appreciated, and points to some possible explanations.  I am not implementing ICustomTypeDescriptor or ITypedList (at least not explicitly in code) so that could be the problem.  Otherwise, I am not doing anything else too crazy, and the object's typehinting is fully functional in the rest of my code, so I feel this would be a "reliable" situation.  But given the complexities involved, I will mark this as the answer if there are no further insights.

    Below is a sample of my implemented list.  This was just a testing program, so as you can see it is pretty vanilla and the only custom thing is raising a few events.  But if it had worked, I would have considered dealing with the more complex lists of my real program. (I wasn't clear on the significance of GetEnumerator1 so I had it return GetEnumerator).  As for the databinding code, there was not too much to that, the one line setting the datagridview datasource (with autogeneratecolumns=true) directly to the list, or declaring a bindingsource and connecting the two that way.  I will post if requested.  Again, thanks for the information.


    Public Class FoodList  
        Implements IList(Of FoodItem)  
        Private Base As New List(Of FoodItem)  
        Public Event evListChange()  
        Public Event evSSChange()  
     
        Public Sub FoodSSChange(ByVal sender As Object)  
            RaiseEvent evSSChange()  
        End Sub 
     
        Public Sub Add(ByVal item As FoodItem) Implements System.Collections.Generic.ICollection(Of FoodItem).Add  
            Base.Add(item)  
            AddHandler item.evSSChanged, AddressOf FoodSSChange  
            RaiseEvent evListChange()  
        End Sub 
     
        Public Sub Clear() Implements System.Collections.Generic.ICollection(Of FoodItem).Clear  
            Base.Clear()  
            RaiseEvent evListChange()  
        End Sub 
     
        Public Function Contains(ByVal item As FoodItem) As Boolean Implements System.Collections.Generic.ICollection(Of FoodItem).Contains  
            Base.Contains(item)  
        End Function 
     
        Public Sub CopyTo(ByVal array() As FoodItem, ByVal arrayIndex As IntegerImplements System.Collections.Generic.ICollection(Of FoodItem).CopyTo  
            Base.CopyTo(array)  
        End Sub 
     
        Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of FoodItem).Count  
            Get 
                Return Base.Count  
            End Get 
        End Property 
     
        Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of FoodItem).IsReadOnly  
            Get 
                Return False 
            End Get 
        End Property 
     
        Public Function Remove(ByVal item As FoodItem) As Boolean Implements System.Collections.Generic.ICollection(Of FoodItem).Remove  
            Return Base.Remove(item)  
            RaiseEvent evListChange()  
        End Function 
     
        Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of FoodItem) Implements System.Collections.Generic.IEnumerable(Of FoodItem).GetEnumerator  
            Return Base.GetEnumerator  
        End Function 
     
        Public Function IndexOf(ByVal item As FoodItem) As Integer Implements System.Collections.Generic.IList(Of FoodItem).IndexOf  
            Return Base.IndexOf(item)  
        End Function 
     
        Public Sub Insert(ByVal index As IntegerByVal item As FoodItem) Implements System.Collections.Generic.IList(Of FoodItem).Insert  
            Base.Insert(index, item)  
            RaiseEvent evListChange()  
        End Sub 
     
        Default Public Property Item(ByVal index As IntegerAs FoodItem Implements System.Collections.Generic.IList(Of FoodItem).Item  
            Get 
                Return Base.Item(index)  
            End Get 
            Set(ByVal value As FoodItem)  
                Base.Item(index) = value  
            End Set 
        End Property 
     
        Public Sub RemoveAt(ByVal index As IntegerImplements System.Collections.Generic.IList(Of FoodItem).RemoveAt  
            Base.RemoveAt(index)  
            RaiseEvent evListChange()  
        End Sub 
     
        Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator  
            Return Base.GetEnumerator  
        End Function 
    End Class 
     
    Tuesday, March 03, 2009 12:14 AM