在项目 属性 更改时更新 ObservableCollection
Update ObservableCollection on item property change
我正在尝试让 ObservableCollection
在更改项目的 属性 时触发 CollectionChanged
事件。我使用了 here 中的代码来完成它,但也许我在从 C# 转换为 Vb.net 时出错了。无论如何 item_PropertyChanged
都没有开火。我在这里错过了什么?
代码:
Imports System.ComponentModel
Imports System.Collections.Specialized
Imports System.Collections.ObjectModel
Class MainWindow
Public Class TrulyObservableCollection(Of T As INotifyPropertyChanged)
Inherits ObservableCollection(Of T)
Public Sub New()
MyBase.New()
AddHandler CollectionChanged, AddressOf TrulyObservableCollection_CollectionChanged
End Sub
Private Sub TrulyObservableCollection_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
If e.NewItems IsNot Nothing Then
For Each item As [Object] In e.NewItems
AddHandler TryCast(item, INotifyPropertyChanged).PropertyChanged, AddressOf item_PropertyChanged
Next
End If
If e.OldItems IsNot Nothing Then
For Each item As [Object] In e.OldItems
RemoveHandler TryCast(item, INotifyPropertyChanged).PropertyChanged, AddressOf item_PropertyChanged
Next
End If
End Sub
Private Sub item_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
Dim a As New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)
OnCollectionChanged(a)
End Sub
End Class
Public Class edm
Implements INotifyPropertyChanged
Property ip As String
Property status As String
Public Sub New(ip As String, status As String)
Me.ip = ip
Me.status = status
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
Public Property edms As New TrulyObservableCollection(Of edm)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
DataContext = Me
edms.Add(New edm("192.168.1.111", "On"))
edms.Add(New edm("192.168.1.112", "Off"))
edms.Add(New edm("192.168.1.113", "On"))
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
edms.Where(Function(edm) edm.ip = "192.168.1.111").First().status = "Off"
End Sub
End Class
编辑:
Bjørn 和 Liero 都提供了很好的答案,在这种情况下我发现很难将一个标记为正确而不是另一个所以我选择的理由是 Bjørn 的 post 回答了我提出的问题它,我标记了 Liero 的回答,因为他的评论引导我找到适合我的场景的最佳解决方案。
如果值与支持字段不同,您需要在每个 属性 的 setter 中引发 属性 更改事件。
Public Property Foo() As String
Get
Return Me.m_foo
End Get
Set(value As String)
If (value <> Me.m_foo) Then
Me.m_foo = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Foo"))
End If
End Set
End Property
所以你的 edm
class 应该看起来更像这样:
Public Class Edm
Implements INotifyPropertyChanged
Public Sub New(ip As String, status As String)
Me.m_ip = ip
Me.m_status = status
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property Ip() As String
Get
Return Me.m_ip
End Get
Set(value As String)
If (value <> Me.m_ip) Then
Me.m_ip = value
Me.NotifyPropertyChanged("Ip")
End If
End Set
End Property
Public Property Status() As String
Get
Return Me.m_status
End Get
Set(value As String)
If (value <> Me.m_status) Then
Me.m_status = value
Me.NotifyPropertyChanged("Status")
End If
End Set
End Property
Private Sub NotifyPropertyChanged(propertyName As String)
Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
End Sub
Protected Overridable Sub OnPropertyChanged(e As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, e)
End Sub
Private m_ip As String
Private m_status As String
End Class
而且,正如 liero in his/her 正确指出的那样,您最好覆盖 InsertItem
、SetItem
、RemoveItem
和 ClearItems
而不是处理 CollectionChanged
事件。
Public Class TrulyObservableCollection(Of T As INotifyPropertyChanged)
Inherits ObservableCollection(Of T)
Protected Overrides Sub InsertItem(index As Integer, item As T)
MyBase.InsertItem(index, item)
Me.HookItem(item)
End Sub
Protected Overrides Sub SetItem(index As Integer, newItem As T)
Dim oldItem As T = Me.Items(index)
MyBase.SetItem(index, newItem)
Me.UnhookItem(oldItem)
Me.HookItem(newItem)
End Sub
Protected Overrides Sub RemoveItem(index As Integer)
Dim item As T = Me.Items(index)
MyBase.RemoveItem(index)
Me.UnhookItem(item)
End Sub
Protected Overrides Sub ClearItems()
For Each item As T In Me.Items
Me.UnhookItem(item)
Next
MyBase.ClearItems()
End Sub
Private Sub HookItem(item As T)
If (Not item Is Nothing) Then AddHandler item.PropertyChanged, AddressOf Me.HandleItemPropertyChanged
End Sub
Private Sub UnhookItem(item As T)
If (Not item Is Nothing) Then RemoveHandler item.PropertyChanged, AddressOf Me.HandleItemPropertyChanged
End Sub
Private Sub HandleItemPropertyChanged(sender As Object, e As PropertyChangedEventArgs)
Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
End Sub
End Class
你是对的,你需要附加到每个项目的 PropertyChanged 事件。但是您不能在订阅时依赖 CollectionChanged 事件。例如,当您清除集合时,旧项目不在事件参数中。项目也可以传递给 ObservableCollection ctor。
更好的方法是覆盖方法 ClearItems
、RemoveItem
、InsertItem
、SetItem
已经有一些实现,例如:
当然我前段时间自己写过:)
但是自从 WPF 引入了 live shaping 我真的不需要它:
- MSDN: Repositioning data as the data's values change (Live shaping)
- WPF 4.5 – Part 10 : Live shaping (live filtering, grouping and sorting of collections)
编辑: 不要忘记在 属性 更改时触发 属性changed 事件,就像@Bjørn-Roger Kringsjå 提议的那样。根据评论,您的问题不在 observablecollection 中,而是 class
的 INotifyPropertyChanged 实现
我正在尝试让 ObservableCollection
在更改项目的 属性 时触发 CollectionChanged
事件。我使用了 here 中的代码来完成它,但也许我在从 C# 转换为 Vb.net 时出错了。无论如何 item_PropertyChanged
都没有开火。我在这里错过了什么?
代码:
Imports System.ComponentModel
Imports System.Collections.Specialized
Imports System.Collections.ObjectModel
Class MainWindow
Public Class TrulyObservableCollection(Of T As INotifyPropertyChanged)
Inherits ObservableCollection(Of T)
Public Sub New()
MyBase.New()
AddHandler CollectionChanged, AddressOf TrulyObservableCollection_CollectionChanged
End Sub
Private Sub TrulyObservableCollection_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
If e.NewItems IsNot Nothing Then
For Each item As [Object] In e.NewItems
AddHandler TryCast(item, INotifyPropertyChanged).PropertyChanged, AddressOf item_PropertyChanged
Next
End If
If e.OldItems IsNot Nothing Then
For Each item As [Object] In e.OldItems
RemoveHandler TryCast(item, INotifyPropertyChanged).PropertyChanged, AddressOf item_PropertyChanged
Next
End If
End Sub
Private Sub item_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
Dim a As New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)
OnCollectionChanged(a)
End Sub
End Class
Public Class edm
Implements INotifyPropertyChanged
Property ip As String
Property status As String
Public Sub New(ip As String, status As String)
Me.ip = ip
Me.status = status
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
Public Property edms As New TrulyObservableCollection(Of edm)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
DataContext = Me
edms.Add(New edm("192.168.1.111", "On"))
edms.Add(New edm("192.168.1.112", "Off"))
edms.Add(New edm("192.168.1.113", "On"))
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
edms.Where(Function(edm) edm.ip = "192.168.1.111").First().status = "Off"
End Sub
End Class
编辑:
Bjørn 和 Liero 都提供了很好的答案,在这种情况下我发现很难将一个标记为正确而不是另一个所以我选择的理由是 Bjørn 的 post 回答了我提出的问题它,我标记了 Liero 的回答,因为他的评论引导我找到适合我的场景的最佳解决方案。
如果值与支持字段不同,您需要在每个 属性 的 setter 中引发 属性 更改事件。
Public Property Foo() As String
Get
Return Me.m_foo
End Get
Set(value As String)
If (value <> Me.m_foo) Then
Me.m_foo = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Foo"))
End If
End Set
End Property
所以你的 edm
class 应该看起来更像这样:
Public Class Edm
Implements INotifyPropertyChanged
Public Sub New(ip As String, status As String)
Me.m_ip = ip
Me.m_status = status
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property Ip() As String
Get
Return Me.m_ip
End Get
Set(value As String)
If (value <> Me.m_ip) Then
Me.m_ip = value
Me.NotifyPropertyChanged("Ip")
End If
End Set
End Property
Public Property Status() As String
Get
Return Me.m_status
End Get
Set(value As String)
If (value <> Me.m_status) Then
Me.m_status = value
Me.NotifyPropertyChanged("Status")
End If
End Set
End Property
Private Sub NotifyPropertyChanged(propertyName As String)
Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
End Sub
Protected Overridable Sub OnPropertyChanged(e As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, e)
End Sub
Private m_ip As String
Private m_status As String
End Class
而且,正如 liero in his/her InsertItem
、SetItem
、RemoveItem
和 ClearItems
而不是处理 CollectionChanged
事件。
Public Class TrulyObservableCollection(Of T As INotifyPropertyChanged)
Inherits ObservableCollection(Of T)
Protected Overrides Sub InsertItem(index As Integer, item As T)
MyBase.InsertItem(index, item)
Me.HookItem(item)
End Sub
Protected Overrides Sub SetItem(index As Integer, newItem As T)
Dim oldItem As T = Me.Items(index)
MyBase.SetItem(index, newItem)
Me.UnhookItem(oldItem)
Me.HookItem(newItem)
End Sub
Protected Overrides Sub RemoveItem(index As Integer)
Dim item As T = Me.Items(index)
MyBase.RemoveItem(index)
Me.UnhookItem(item)
End Sub
Protected Overrides Sub ClearItems()
For Each item As T In Me.Items
Me.UnhookItem(item)
Next
MyBase.ClearItems()
End Sub
Private Sub HookItem(item As T)
If (Not item Is Nothing) Then AddHandler item.PropertyChanged, AddressOf Me.HandleItemPropertyChanged
End Sub
Private Sub UnhookItem(item As T)
If (Not item Is Nothing) Then RemoveHandler item.PropertyChanged, AddressOf Me.HandleItemPropertyChanged
End Sub
Private Sub HandleItemPropertyChanged(sender As Object, e As PropertyChangedEventArgs)
Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
End Sub
End Class
你是对的,你需要附加到每个项目的 PropertyChanged 事件。但是您不能在订阅时依赖 CollectionChanged 事件。例如,当您清除集合时,旧项目不在事件参数中。项目也可以传递给 ObservableCollection ctor。
更好的方法是覆盖方法 ClearItems
、RemoveItem
、InsertItem
、SetItem
已经有一些实现,例如:
当然我前段时间自己写过:) 但是自从 WPF 引入了 live shaping 我真的不需要它:
- MSDN: Repositioning data as the data's values change (Live shaping)
- WPF 4.5 – Part 10 : Live shaping (live filtering, grouping and sorting of collections)
编辑: 不要忘记在 属性 更改时触发 属性changed 事件,就像@Bjørn-Roger Kringsjå 提议的那样。根据评论,您的问题不在 observablecollection 中,而是 class
的 INotifyPropertyChanged 实现