代码之家  ›  专栏  ›  技术社区  ›  wakurth

从ViewModel中的Observable集合中移除不会更新View,但现有项的更新会更新View

  •  4
  • wakurth  · 技术社区  · 12 年前

    我相信这对某人来说将是一次扣篮。。。 交叉手指

    我的ListView ItemsSource绑定到ViewModel上名为TileItems的属性。

    填充列表视图将完美更新。

    在ViewModel中,您可以看到“existingTileItem.Transaction=e.Transaction”…单个列表视图项将完美更新。

    在ViewModel中,您可以看到“Me.TileItems.Remove(existingTileItem)”…该项目不会从视图中删除。它确实成功地从Me.TileItems集合中删除了,但视图中没有显示更新。

    附加信息:AbstractViewModel实现了INotificationPropertyChanged,我尝试在TileItem中重写Equals,但没有重写它,结果总是一样的。我见过 this 回答和 this 回答,但他们没有回答我的问题。

    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" />
    

    视图模型:

    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模型:

    Public Class TileItems
        Inherits ObservableCollection(Of TileItem)
    
    End Class
    

    TileItem模型:

     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
    

    更新:

    根据@ReedCopsey的回答,这是我为实现这一目标所做的更新。

    我更新了Me.TileItems.Remove(现有的TileItem)为现在的样子

    Me.View.Dispatcher.Invoke(Sub()
                                  Me.TileItems.Remove(existingTileItem)
                              End Sub, DispatcherPriority.ApplicationIdle)
    
    1 回复  |  直到 9 年前
        1
  •  7
  •   Reed Copsey    12 年前

    据我所见,你正在粘贴的代码应该可以工作。最有可能的罪魁祸首是 TransactionUpdateReceived 在不是用户界面线程的线程上引发事件。在WPF中,单个项可以在后台线程上修改,但集合不能(在.NET 4.5之前,但在.NET 4.5中,它们需要额外的工作)。

    有两种选择。如果您使用的是.NET 4.5,则可以使用 BindingOperations.EnableCollectionSynchronization 以允许 ObservableCollection 以便从后台线程进行修改。

    或者,您可以使用 Dispatcher.Invoke 将Add/Remove调用推送到主线程上。