2015-03-06 2 views
1

Я пытаюсь получить ObservableCollection, чтобы запустить событие CollectionChanged, когда изменяется свойство объекта. Я использовал код от here, но, возможно, я сделал ошибку при переводе с C# на Vb.net. В любом случае item_PropertyChanged не стреляет. Что мне здесь не хватает?Обновление ObservableCollection по изменению свойства объекта

Код:

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 

Edit:

Оба Бьорн и Liero при условии, большие ответы и в этой ситуации мне трудно отметить один, как правильно, а не другой, так мои рассуждения на мой выбор что, хотя пост Бьорна ответил на мой вопрос, как я выразился, я отметил ответ Лиеро, поскольку его комментарии привели меня к лучшему решению для моего сценария.

ответ

1

Вы правы, что вам нужно прикрепить событие PropertyChanged каждого элемента. Но при подписке вы не можете полагаться на событие CollectionChanged. Например, когда вы очищаете коллекцию, старые элементы не входят в аргументы события. Также элементы могут быть переданы в ObservableCollection ctor.

Лучше способ переопределить методы ClearItems, RemoveItem, InsertItem, SetItem

Есть уже некоторые реализации, например:

Конечно, я написал свои собственные НЕКОТОРЫХ время назад :) Но с тех пор, как WPF представил живое формирование, он мне не нужен:

Edit: Не забудьте стрелять PropertyChanged событие, когда изменяется свойство, так же, как @ Бьорн Роджер Kringsjå предложил. Основываясь на комментариях, ваша проблема заключается не в наблюдаемом объединении, а в выполнении INotifyPropertyChanged вашего класса

+0

Спасибо за информацию. Я не знал о живом формировании, поэтому читал на нем. Мне интересно, однако, в моем сценарии, где у меня есть статический список машин и вы хотите обновить статус с заданным интервалом и внести те изменения, которые отражены в 'DataGrid', как мне реализовать живое форматирование?Я не собираюсь группировать, фильтровать или сортировать, так что это правильный подход здесь? – doovers

+0

Он должен просто работать. Вам вообще не требуется ObservableCollection. Событие CollectionChanged имеет смысл только тогда, когда вам нужно добавлять/удалять элементы из коллекции и отражать их в datagrid. ObervableCollection.PropertyChanged уведомляет только об изменении свойства Count. Если вы хотите просто обновить ячейку в datagrid при выполнении chages, вам необходимо запустить событие propertychanged в вашей «машинной» сущности. Связывание автоматически обновит ячейку. Если это не проблема, это где-то в другом месте, но, конечно, не в событии CollectionChanged – Liero

+0

Спасибо. Кажется, я поступил неправильно. Получил его, нажав 'PropertyChanged' в свой установщик свойств класса, как вы предложили. – doovers

2

Вам необходимо поднять измененное событие свойства в установщике каждого свойства, если значение отличается от поля поддержки.

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 класс должен выглядеть следующим образом:

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 в его/ее answer, лучше переопределить 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