The code below pulls out a bunch of records from an Access 2010 database; 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. 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 )
The exception is: 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. 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):
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:
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. ` 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
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.
You're assigning the local variable eventHandler
to an anonymous method, rather than the actual event handler. It should be:
Dim eventHandler As NotifyCollectionChangedEventHandler = CollectionChangedEvent
NB: You need to use CollectionChangedEvent
in VB, not CollectionChanged
.
You're using CType
to cast the target to a DispatcherObject
, which won't work if the target isn't a DispatcherObject
. Use TryCast
instead:
Dim dispatcherObject As DispatcherObject = TryCast(handler.Target, DispatcherObject)
You can also tidy up the test on the next line by using IsNot
:
If dispatcherObject IsNot Nothing AndAlso Not dispatcherObject.CheckAccess Then
WARNING - The code below acts differently to the C# version. The key difference seems to be that in VB you can't Override an Event (Why on earth not?) yet in C# you can.
The result is the Handler is Nothing in VB but not in C# :(.
So the syntax builds without error but the VB version doesn't ever do anything.
Redone with the updated answer in 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.
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
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.