ObservableCollection FileSystemWatcher ListBox 更新问题

发布于 2024-07-29 06:07:07 字数 3387 浏览 5 评论 0原文

我有一个 ObservableCollection,它使用 FileSystemWatcher 自动添加已添加到目录中的其他 PNG 图像。 ListBox 使用以下 XAML 将 ItemsSource 数据绑定到 Photos 对象。

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

但是,当将 PNG 文件添加到受监视目录时,将调用 OnPhotoCreated 事件(断点确认这一点),但 ListBox UI 不会更新。 有任何想法吗?

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,“调用线程无法访问此对象,因为另一个线程拥有它”。 我希望不需要刷新方法。

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

佞臣 2024-08-05 06:07:07

看一下 ObservableCollection 类(来自 Reflector)的 InsertItem 代码:

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 类。

至于 InvalidOperationException 错误,对我来说,这听起来像是从错误线程进行 UI 更新的情况。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文