Search code examples
wpfbindingilist

WPF: What do I need to implement to make an extended IList( of T) bindable?


I'm extending IList so I can track the changes made to the list (updates, inserts and deletes).
Everything is ok, but I can not bind any ItemsControl to it.

Public Class TrackedList(Of T)
    Implements IList(Of T), INotifyCollectionChanged

    Public Sub New(Lista As IList(Of T))
        m_inner = Lista
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
    End Sub

    Private m_inner As IList(Of T)
    Public ReadOnly Property Inner() As IList(Of T)
        Get
            If m_inner Is Nothing Then
                m_inner = New List(Of T)()
            End If
            Return m_inner
        End Get
    End Property

    Private m_Updates As IList(Of T)
    Public ReadOnly Property Updates() As IList(Of T)
        Get
            If m_Updates Is Nothing Then
                m_Updates = New List(Of T)()
            End If
            Return m_Updates
        End Get
    End Property

    Private m_Inserts As IList(Of T)
    Public ReadOnly Property Inserts() As IList(Of T)
        Get
            If m_Inserts Is Nothing Then
                m_Inserts = New List(Of T)()
            End If
            Return m_Inserts
        End Get
    End Property

    Private m_Deletes As IList(Of T)
    Public ReadOnly Property Deletes() As IList(Of T)
        Get
            If m_Deletes Is Nothing Then
                m_Deletes = New List(Of T)()
            End If
            Return m_Deletes
        End Get
    End Property

    'Propietary methods'
    Public Sub ItemUpdated(ByVal item As T)
        If Not Updates.Contains(item) Then Updates.Add(item)
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace))
    End Sub

    Public Sub FlushChanges()
        m_Updates = Nothing
        m_Inserts = Nothing
        m_Updates = Nothing
    End Sub


    Public Sub Add(ByVal item As T) Implements System.Collections.Generic.ICollection(Of T).Add
        Inner.Add(item)
        Inserts.Add(item)
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add))
    End Sub

    Public Sub Clear() Implements System.Collections.Generic.ICollection(Of T).Clear
        If m_inner IsNot Nothing Then
            Inner.Clear()
        End If
    End Sub

    Public Function Contains(ByVal item As T) As Boolean Implements System.Collections.Generic.ICollection(Of T).Contains
        Return Inner.Contains(item)
    End Function

    Public Sub CopyTo(ByVal array() As T, ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of T).CopyTo
        Inner.CopyTo(array, arrayIndex)
    End Sub

    Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of T).Count
        Get
            Return Inner.Count
        End Get
    End Property

    Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of T).IsReadOnly
        Get
            Return Inner.IsReadOnly
        End Get
    End Property

    Public Function Remove(ByVal item As T) As Boolean Implements System.Collections.Generic.ICollection(Of T).Remove
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove))
        Deletes.Add(item)
        Return Inner.Remove(item)

    End Function


    Public Function IndexOf(ByVal item As T) As Integer Implements System.Collections.Generic.IList(Of T).IndexOf
        Return Inner.IndexOf(item)
    End Function

    Public Sub Insert(ByVal index As Integer, ByVal item As T) Implements System.Collections.Generic.IList(Of T).Insert
        Inserts.Add(item)
        Inner.Insert(index, item)
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add))
    End Sub

    Default Public Property Item(ByVal index As Integer) As T Implements System.Collections.Generic.IList(Of T).Item
        Get
            Return Inner(index)
        End Get
        Set(ByVal value As T)
            Inner(index) = value
        End Set
    End Property

    Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.Generic.IList(Of T).RemoveAt
        Deletes.Add(Inner(index))
        Inner.RemoveAt(index)
        RaiseEvent CollectionChanged(Me, New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove))
    End Sub

    Public Function GetEnumerator1() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
        Return Inner.GetEnumerator()
    End Function

    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return DirectCast(Inner, IEnumerable).GetEnumerator()
    End Function


    Public Event CollectionChanged(sender As Object, e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) Implements System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged

End Class

What do I need to do the make the list Bindable?


Solution

  • The trouble wasn't in the class, it does not even need to implement INotifyCollectionChanged

    The problem was in a intermediate control who has a DependencyProperty for bridging the binding

    Public Shared ReadOnly ItemsProperty As DependencyProperty = _
        DependencyProperty.Register("Items", GetType(IList), GetType(MainWindowViewModel))
    
    Public Property Items As IList
        Set(ByVal value As IList)
            SetValue(ItemsProperty, value)
        End Set
        Get
            Return DirectCast(GetValue(ItemsProperty), IList)
        End Get
    End Property
    

    Turns out that changing the dependency property to IEnumerable solved the problem.