views:

573

answers:

1

I have an ObservableCollection that is using a FileSystemWatcher to automatically add other PNG images that have been added to the directories. The ListBox has the ItemsSource databound to the Photos object using the following XAML.

<ListBox ItemsSource="{Binding Source={StaticResource Photos}}" IsSynchronizedWithCurrentItem="True"/>

However when a PNG file is added to the monitored directory the OnPhotoCreated event is being called (a breakpoint confirms this) however the ListBox UI is not being updated. Any ideas?

Public Class Photos
Inherits Collections.ObjectModel.ObservableCollection(Of BitmapImage)

' Events
Public Event ItemsUpdated As EventHandler

' Fields
Private FileSystemWatchers As Dictionary(Of String, FileSystemWatcher) = New Dictionary(Of String, FileSystemWatcher)

' Methods
Protected Overrides Sub ClearItems()
    MyBase.ClearItems()
    Me.FileSystemWatchers.Clear()
End Sub

Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As BitmapImage)
    MyBase.InsertItem(index, item)
    Dim ImagePath As String = IO.Path.GetDirectoryName(item.UriSource.LocalPath)
    If Not Me.FileSystemWatchers.ContainsKey(ImagePath) Then
        Dim FileWatcher As New FileSystemWatcher(ImagePath, "*.png")
        FileWatcher.EnableRaisingEvents = True
        AddHandler FileWatcher.Created, New FileSystemEventHandler(AddressOf Me.OnPhotoCreated)
        AddHandler FileWatcher.Deleted, New FileSystemEventHandler(AddressOf Me.OnPhotoDeleted)
        AddHandler FileWatcher.Renamed, New RenamedEventHandler(AddressOf Me.OnPhotoRenamed)
        Me.FileSystemWatchers.Add(ImagePath, FileWatcher)
    End If
End Sub

Private Sub OnPhotoCreated(ByVal sender As Object, ByVal e As FileSystemEventArgs)
    MyBase.Items.Add(New BitmapImage(New Uri(e.FullPath)))
    RaiseEvent ItemsUpdated(Me, New EventArgs)
End Sub

Private Sub OnPhotoDeleted(ByVal sender As Object, ByVal e As FileSystemEventArgs)
    Dim index As Integer = -1
    Dim i As Integer
    For i = 0 To MyBase.Items.Count - 1
        If (MyBase.Items.Item(i).UriSource.AbsolutePath = e.FullPath) Then
            index = i
            Exit For
        End If
    Next i
    If (index >= 0) Then
        MyBase.Items.RemoveAt(index)
    End If
    RaiseEvent ItemsUpdated(Me, New EventArgs)
End Sub

Private Sub OnPhotoRenamed(ByVal sender As Object, ByVal e As RenamedEventArgs)
    Dim index As Integer = -1
    Dim i As Integer
    For i = 0 To MyBase.Items.Count - 1
        If (MyBase.Items.Item(i).UriSource.AbsolutePath = e.OldFullPath) Then
            index = i
            Exit For
        End If
    Next i
    If (index >= 0) Then
        MyBase.Items.Item(index) = New BitmapImage(New Uri(e.FullPath))
    End If
    RaiseEvent ItemsUpdated(Me, New EventArgs)
End Sub


End Class

Update #1: I have tried an Event as shown below. That causes a crash with InvalidOperationException, "The calling thread cannot access this object because a different thread owns it" When the new image is attempted to scroll into view. I was hoping that the Refresh method would not be needed.

Dim Photos As Photos = CType(Me.FindResource("Photos"), Photos)
AddHandler Photos.ItemsUpdated, AddressOf Me.Photos_ItemsUpdated

Private Sub RefreshPhotos()
    '
    If Me.ImageListBox.Dispatcher.CheckAccess = True Then
        Me.ImageListBox.Items.Refresh()
    Else
        Dispatcher.BeginInvoke(DispatcherPriority.Normal, New DispatcherMethodCallback(AddressOf Me.RefreshPhotos))
    End If
    '
End Sub

Private Sub Photos_ItemsUpdated(ByVal sender As Object, ByVal e As EventArgs)
    '
    Debug.WriteLine("PhotosUpdated")
    Me.RefreshPhotos()
    '
End Sub
+1  A: 

Take a look at the InsertItem code from the ObservableCollection class (from Reflector):

Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As T)
    Me.CheckReentrancy
    MyBase.InsertItem(index, item)
    Me.OnPropertyChanged("Count")
    Me.OnPropertyChanged("Item[]")
    Me.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index)
End Sub

Now use that as a guideline what notifications an ObservableCollection should perform. The OnCollectionChanged method is the main connection to the wpf notification system, if it gets called appropriately, then this calss is working properly, and somewhere else is the problem.

Also, in your OnPhotoCreated method, you call the Items.Add method, which doesn't do notifications, it doesn't belong to the ObservableCollection class, but the Collection<T> class from which ObservableCollection inherits.

As for the InvalidOperationException error, it sounds like a case of UI update from the wrong thread to me.

kek444