Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove from Observable collection in ViewModel does not update View, but Updates of Existing Items does update View

I'm sure this will be a slam dunk for someone... fingers crossed

My ListView ItemsSource is bound to a Property on my ViewModel named TileItems.

Populating the list view updates perfectly.

In the ViewModel, where you see "existingTileItem.Transaction = e.Transaction" . . . The individual listview item updates perfectly.

In the ViewModel, where you see "Me.TileItems.Remove(existingTileItem)" ... The item is not removed from the View. It does successfully remove from the Me.TileItems collection, but the update is not depicted in the View.

Additional Info: AbstractViewModel implements INotificationPropertyChanged, I've tried overriding Equals in the TileItem and not overriding it, and the same results all around happen. I have seen this answer and this answer, but they do not answer the issue I am having.

XAML:

<UserControl.DataContext>
    <local:TransactionTileResultsViewControlViewModel />
</UserControl.DataContext>

<ListView Grid.Row="1"  Name="tileItems" ItemsSource="{Binding TileItems, Mode=TwoWay}" 
                  ItemTemplate="{StaticResource tileItemDataTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                  HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />

ViewModel:

Public Class TransactionTileResultsViewControlViewModel
    Inherits AbstractViewModel
    Implements INavigationAware

    Private _tileItems As TileItems
    Public Property TileItems As TileItems
        Get
            Return Me._tileItems
        End Get
        Set(value As TileItems)
            Me._tileItems = value
            MyBase.RaisePropertyChanged("TileItems")
        End Set
    End Property

'....


    #Region "TransactionUpdateReceived Methods"

        Private Sub TransactionUpdateReceived_Handler(ByVal e As TransactionUpdatedEvent)

            If e.Transaction IsNot Nothing Then

                Dim existingTileItem As TileItem = Me.TileItems.Where(Function(t) t.Transaction.TransactionQueueID = e.Transaction.TransactionQueueID).FirstOrDefault()
                If existingTileItem IsNot Nothing Then

                    If e.Transaction.Canceled Then

                          Me.TileItems.Remove(existingTileItem)

                    Else

                        If e.Transaction.ContainsFailedActivites() OrElse e.Transaction.ContainsCallbackActivities() Then

                            existingTileItem.Transaction = e.Transaction

                        Else

                            Me.TileItems.Remove(existingTileItem)

                        End If

                    End If

                End If

            End If

        End Sub

#End Region

End Class

TileItems Model:

Public Class TileItems
    Inherits ObservableCollection(Of TileItem)

End Class

TileItem Model:

 Imports Microsoft.Practices.Prism.ViewModel

    Public Class TileItem
        Inherits NotificationObject

        Private _created As Date
        Public Property Created As Date
            Get
                Return _created
            End Get
            Set(value As Date)
                _created = value
                MyBase.RaisePropertyChanged("Created")
            End Set
        End Property

        Private _category As String
        Public Property Category As String
            Get
                Return _category
            End Get
            Set(value As String)
                _category = value
                MyBase.RaisePropertyChanged("Category")
            End Set
        End Property

        Private _tileField1 As String
        Public Property TileField1 As String
            Get
                Return _tileField1
            End Get
            Set(value As String)
                _tileField1 = value
                MyBase.RaisePropertyChanged("TileField1")
            End Set
        End Property

        Private _tileField2 As String
        Public Property TileField2 As String
            Get
                Return _tileField2
            End Get
            Set(value As String)
                _tileField2 = value
                MyBase.RaisePropertyChanged("TileField2")
            End Set
        End Property

        Private _tileField3 As String
        Public Property TileField3 As String
            Get
                Return _tileField3
            End Get
            Set(value As String)
                _tileField3 = value
                MyBase.RaisePropertyChanged("TileField3")
            End Set
        End Property

        Private _transaction As Transaction
        Public Property Transaction As Transaction
            Get
                Return _transaction
            End Get
            Set(value As Transaction)
                _transaction = value
                MyBase.RaisePropertyChanged("Transaction")
            End Set
        End Property

    Public Overrides Function Equals(obj As Object) As Boolean

        If TypeOf obj Is TileItem Then

            Dim tileItem As TileItem = DirectCast(obj, TileItem)

            If tileItem.Transaction IsNot Nothing AndAlso Me.Transaction IsNot Nothing Then

                Return tileItem.Transaction.TransactionQueueID = Me.Transaction.TransactionQueueID

            Else
                Return False

            End If

        Else
            Return False
        End If

    End Function


    End Class

UPDATE:

Per @ReedCopsey 's answer, here is the update I made to get this working.

I updated Me.TileItems.Remove(existingTileItem) to be this now

Me.View.Dispatcher.Invoke(Sub()
                              Me.TileItems.Remove(existingTileItem)
                          End Sub, DispatcherPriority.ApplicationIdle)
like image 832
wakurth Avatar asked Mar 22 '23 13:03

wakurth


1 Answers

The code you're pasting should work, from what I can see. The most likely culprit is that your TransactionUpdateReceived event is being raised on a thread that is not the user interface thread. In WPF, single items can be modified on a background thread, but collections cannot (prior to .NET 4.5, but in .NET 4.5, they require extra work).

There are two options. If you're using .NET 4.5, you can use BindingOperations.EnableCollectionSynchronization to allow the ObservableCollection to be modified from a background thread.

Alternatively, you can use Dispatcher.Invoke to push the Add/Remove calls onto the main thread.

like image 162
Reed Copsey Avatar answered Mar 25 '23 07:03

Reed Copsey