ObservableCollection FileSystemWatcher ListBox Проблема с обновлением - PullRequest
2 голосов
/ 01 августа 2009

У меня есть ObservableCollection, которая использует FileSystemWatcher для автоматического добавления других изображений PNG, которые были добавлены в каталоги. ListBox имеет привязку данных ItemsSource к объекту Photos с использованием следующего XAML.

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

Однако, когда файл PNG добавляется в отслеживаемый каталог, вызывается событие OnPhotoCreated (точка останова подтверждает это), однако интерфейс ListBox не обновляется. Есть идеи?

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

Обновление № 1: Я попробовал событие, как показано ниже. Это приводит к сбою с InvalidOperationException, «вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им», когда новое изображение пытается прокрутить в представление. Я надеялся, что метод Refresh не понадобится.

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 Ответ

1 голос
/ 01 августа 2009

Посмотрите на код InsertItem из класса ObservableCollection (из 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

Теперь используйте это в качестве руководства, какие уведомления должен выполнять ObservableCollection. Метод OnCollectionChanged является основным подключением к системе уведомлений wpf, если он вызывается надлежащим образом, тогда этот запрос работает правильно, и проблема возникает еще где-то.

Кроме того, в вашем методе OnPhotoCreated вы вызываете метод Items.Add, который не делает уведомлений , он принадлежит не классу ObservableCollection, а классу Collection<T>, из которого ObservableCollection наследует.

Что касается ошибки InvalidOperationException, то это похоже на случай обновления пользовательского интерфейса со стороны не того потока.

...