简体   繁体   English

WPF中DispatcherObject的问题和Async / ObservableCollection问题

[英]DispatcherObject cast woes and Async / ObservableCollection issues in WPF

The code below pulls out a bunch of records from an Access 2010 database; 下面的代码从Access 2010数据库中拉出一堆记录。 hence rolling my own connector bits. 因此滚动我自己的连接器位。 I've succeeded in doing the observablecollection and made it all bind up with nice drag and drop data sources, from my own objects. 我已经成功地进行了observablecollection,并通过我自己的对象中的漂亮的拖放数据源将其绑定在一起。 However, like a daft person, I want to do this Asynchronously. 但是,我想像一个愚蠢的人那样异步地执行此操作。 Yet, I've got a small cast monster problem, and I don't know what to feed it! 但是,我遇到了一个小型怪物问题,而且我不知道该怎么喂! Can anyone advise me - I've tried a lot of reading around, but the concepts are just a little too many at once on a Friday afternoon and I'm struggling to make any real headway. 谁能告诉我-我已经尝试了很多阅读,但是每个星期五一次一次的概念太多了,我正在努力取得任何进展。

The line I'm having trouble with is: Dim dispatcherObject As DispatcherObject = CType (handler.Target, DispatcherObject ) 我遇到的问题是: Dim dispatcherObject As DispatcherObject = CType (handler.Target, DispatcherObject )

The exception is: Unable to cast object of type '_Closure$__2[SomeRecord_Viewer.SomeRecord]' to type 'System.Windows.Threading.DispatcherObject'. 例外是: Unable to cast object of type '_Closure$__2[SomeRecord_Viewer.SomeRecord]' to type 'System.Windows.Threading.DispatcherObject'.

I've managed to make a WPF listbox populate via the code below, however only by commenting out a part of the ObservableCollectionEx class. 我设法通过下面的代码填充了WPF列表框,但是只能注释掉ObservableCollectionEx类的一部分。 This causes synchronisation problems and a crash after a few hundred records are entered. 输入数百条记录后,这将导致同步问题和崩溃。

Class that builds the threaded list of entities - in this case, an ObservableCollectionEx(Of SomeRecord): 用于构建实体的线程列表的类-在这种情况下,为ObservableCollectionEx(Of SomeRecord):

Class SomeRecordSet
Inherits ObservableCollectionEx( Of  SomeRecord)

Private Shared Property _SomeRecordList As New ObservableCollectionEx(Of  SomeRecord )
Public Shared ReadOnly Property SomeRecordList As ObservableCollectionEx(Of  SomeRecord )
    Get
        If _SomeRecordList.Count = 0 Then BuildSomeRecordListAsync()
        Return _SomeRecordList
    End Get
End Property

Public Shared ReadOnly Property ReturnSingleSomeRecord(id As Integer) As SomeRecord
    Get
        Return ( From SomeRecord In _SomeRecordList Where SomeRecord.id = id Select        SomeRecord).First()
    End Get
End Property

Private Shared Async Sub BuildSomeRecordListAsync()
    Await Task.Run( Sub() BuildSomeRecordList())
    Return
End Sub

Private Shared Sub BuildSomeRecordList()
    Db.newcmd( "Select * from  RecordList ")
    While Db.read
        Dim SomeRecord As New SomeRecord
        With SomeRecord
            .id = Db.dbint( "ID")
            .type = Db.dbin( "type")
         End With
        _SomeRecordList.Add(SomeRecord)
    End While
End Sub`

Partial code for the SomeRecord class: SomeRecord类的部分代码:

Class SomeRecord
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements                   INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged( ByVal info As String)
    RaiseEvent PropertyChanged(Me , New PropertyChangedEventArgs (info))
End Sub

...'lots of simple properties.
End Class

The threaded collection class code - translated from another online source. 线程集合类代码-从另一个在线来源翻译而来。

'I use PostSharp for try catch stuff. ``我使用PostSharp尝试捕捉东西。 ` Public Class ObservableCollectionEx (Of T ) Inherits ObservableCollection( Of T) ' Override the event so this class can access it Public Shadows Event CollectionChanged As System.Collections.Specialized.NotifyCollectionChangedEventHandler `Public Class ObservableCollectionEx(Of T)继承ObservableCollection(Of T)'重写事件,以便此类可以访问它。Public Shadows Event CollectionChanged as System.Collections.Specialized.NotifyCollectionChangedEventHandler

Protected Overrides Sub OnCollectionChanged( ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs )

    Using BlockReentrancy()

        Dim eventHandler As System.Collections.Specialized.NotifyCollectionChangedEventHandler = Sub () RaiseEvent CollectionChanged(Me , e)
        If (eventHandler Is Nothing) Then Return

        Dim delegates() As [Delegate] = eventHandler.GetInvocationList
*******If I comment this out I can populate the Listbox via a CollectionView, however it dies with issues to do with the list not staying synchronised :).
        'Walk thru invocation list 
        For Each handler As System.Collections.Specialized.NotifyCollectionChangedEventHandler     In delegates
            Dim dispatcherObject As DispatcherObject = CType (handler.Target, DispatcherObject)
            ' If the subscriber is a DispatcherObject and different thread
            If (( Not (dispatcherObject) Is Nothing) AndAlso (dispatcherObject.CheckAccess = False )) Then
                ' Invoke handler in the target dispatcher's thread
                dispatcherObject.Dispatcher.Invoke(DispatcherPriority .DataBind, handler, Me, e)
            Else
                handler( Me, e)
            End If
        Next

*******End of stuff I comment out to get working partially***

    End Using
End Sub
End Class

From what I can see, you have two problems. 据我所知,您有两个问题。

  1. You're assigning the local variable eventHandler to an anonymous method, rather than the actual event handler. 您正在将局部变量eventHandler分配给匿名方法,而不是实际的事件处理程序。 It should be: 它应该是:

     Dim eventHandler As NotifyCollectionChangedEventHandler = CollectionChangedEvent 

    NB: You need to use CollectionChangedEvent in VB, not CollectionChanged . 注意:您需要在VB中使用CollectionChangedEvent ,而不是CollectionChanged

  2. You're using CType to cast the target to a DispatcherObject , which won't work if the target isn't a DispatcherObject . 您正在使用CType到目标转换为DispatcherObject ,如果目标不是将不起作用DispatcherObject Use TryCast instead: 改用TryCast

     Dim dispatcherObject As DispatcherObject = TryCast(handler.Target, DispatcherObject) 

You can also tidy up the test on the next line by using IsNot : 您还可以使用IsNot整理下一行的测试:

If dispatcherObject IsNot Nothing AndAlso Not dispatcherObject.CheckAccess Then

WARNING - The code below acts differently to the C# version. 警告-以下代码的行为与C#版本不同。 The key difference seems to be that in VB you can't Override an Event (Why on earth not?) yet in C# you can. 关键的区别似乎是,在VB中,您不能重写事件(为什么不是呢?),而在C#中,您可以重写。

The result is the Handler is Nothing in VB but not in C# :(. 结果是Handler在VB中什么都没有,但在C#中却没有:(。

So the syntax builds without error but the VB version doesn't ever do anything. 因此,语法的构建没有错误,但是VB版本从来没有做任何事情。

Redone with the updated answer in VB. 在VB中重做更新的答案。 Thank you! 谢谢!

Note I cannot make this work with Entity Framework, yet. 注意我还不能使用实体框架。 But I think that a me and EF issue, not the collection. 但是我认为这是我和EF的问题,而不是收藏。

The code itself is here for anyone interested. 该代码本身适用于任何有兴趣的人。 My list DOES populate perfectly fine now. 我的列表现在填充得很好。 However, I would take this answer of mine with a small pinch of salt until I update saying how I've extensively tested perhaps :) 但是,我会用一小撮盐来回答我的问题,直到我更新说我如何进行了广泛测试:)

However the omens are good - here is the original C# author's site: Original Site 但是,预兆很好-这是原始C#作者的网站: 原始网站

Public Class ObservableCollectionEx(Of T)
Inherits ObservableCollection(Of T)



'Override the event so this class can access it
Public Shadows Event CollectionChanged As NotifyCollectionChangedEventHandler

Protected Overrides Sub OnCollectionChanged(ByVal e As NotifyCollectionChangedEventArgs)
    Using BlockReentrancy()
        Dim eventHandler As System.Collections.Specialized.NotifyCollectionChangedEventHandler = CollectionChangedEvent
        If eventHandler Is Nothing Then
            Return
        End If


        Dim delegates() As [Delegate] = CollectionChangedEvent.GetInvocationList
        'Walk thru invocation list
        For Each handler As NotifyCollectionChangedEventHandler In delegates
            Dim dispatcherObject As DispatcherObject = TryCast(handler.Target, DispatcherObject)
            ' If the subscriber is a DispatcherObject and different thread
            If dispatcherObject IsNot Nothing AndAlso Not dispatcherObject.CheckAccess Then
                ' Invoke handler in the target dispatcher's thread
                dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, Me, e)
            Else
                handler(Me, e)
            End If
        Next
    End Using
End Sub

End Class 末级

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM