[B .nET 2005] Lettura properties ListBox da thread esterno

venerdì 06 giugno 2008 - 12.33

Mollella27 Profilo | Newbie

Ciao a tutti,
Sto provando a leggere delle properties di vari oggetti di una form da trhead esterno mediante delegate. Ho creato
una classe a cui passo l'oggetto e la proprietà da leggere e lei mi restituisce il valore della proprietà.
Ad esempio, dal thread riesco a leggere il testo di una label passando alla classe l'oggetto label1 e la proprietà Text
e la classe restituisce il valore della proprietà text. Funziona con quasi tutti gli oggetti tranne quando vado a prendere
il valore della proprietà SelectedItem della listbox. Li la classe mi dà una eccezione di object not found.
Ecco il codice:

Protected Function GetObjectProperty(ByVal obj As Object, _
ByVal propertyName As String) As String

Dim propInfoArray() As System.Reflection.PropertyInfo
Dim result As New System.Text.StringBuilder
'get array of propertyinfo objects for our targets Type
propInfoArray = obj.GetType.GetProperties
Dim prop As Object

'iterate through these propertyinfo objects
For Each propInfo As System.Reflection.PropertyInfo In propInfoArray
Dim s As String
Try
s = propInfo.GetValue(obj, Nothing).ToString()

Catch ex As Exception
s = ""
End Try
If propInfo.Name = propertyName Then
Return s
End If
Next
Return ""

End Function

Alla funzione passo obj = Listbox5 e PropertyName = "SelectedItem". In s mi aspetto di vedere il valore del selectedItem
ossia un testo che ho precedentemente selezionato nella listbox5, ma non va, mi dà eccezione.
Qualcuno può darmi una mano?

grazie

Mike

0v3rCl0ck Profilo | Guru

>Ciao a tutti,

Ciao

>Sto provando a leggere delle properties di vari oggetti di una
>form da trhead esterno mediante delegate. Ho creato
>una classe a cui passo l'oggetto e la proprietà da leggere e
>lei mi restituisce il valore della proprietà.
>Ad esempio, dal thread riesco a leggere il testo di una label
>passando alla classe l'oggetto label1 e la proprietà Text
>e la classe restituisce il valore della proprietà text. Funziona
>con quasi tutti gli oggetti tranne quando vado a prendere
>il valore della proprietà SelectedItem della listbox. Li la classe
>mi dà una eccezione di object not found.
>Ecco il codice:
>
>Protected Function GetObjectProperty(ByVal obj As Object, _
> ByVal propertyName As String) As String
>
>Dim propInfoArray() As System.Reflection.PropertyInfo
> Dim result As New System.Text.StringBuilder
>'get array of propertyinfo objects for our targets Type
> propInfoArray = obj.GetType.GetProperties
> Dim prop As Object
>
> 'iterate through these propertyinfo objects
>For Each propInfo As System.Reflection.PropertyInfo In propInfoArray
> Dim s As String
> Try
>s = propInfo.GetValue(obj, Nothing).ToString()
>
> Catch ex As Exception
> s = ""
> End Try
> If propInfo.Name = propertyName Then
> Return s
> End If
> Next
> Return ""
>
> End Function
>
>Alla funzione passo obj = Listbox5 e PropertyName = "SelectedItem".
>In s mi aspetto di vedere il valore del selectedItem
>ossia un testo che ho precedentemente selezionato nella listbox5,
>ma non va, mi dà eccezione.
>Qualcuno può darmi una mano?

Come popoli la listbox? Con datasource o items.add? Con che tipo dato (stringa, collezione, ...)?
Sei sicuro che nel contesto in cui vai a leggere la listbox, quest'ultimo esisti ancora?

>
>grazie
>
>Mike


- Michael -
http://blogs.dotnethell.it/Regulator/

Mollella27 Profilo | Newbie

Ciao,
La listbox è popolata tramite items.add, contiene stringhe e nel contesto in cui la vado a leggere esiste ancora ed è popolata, e , l'item su cui vado a leggere è selezionato.

Mike

0v3rCl0ck Profilo | Guru

Da dentro la funzione definita dal delegate riesci ad accedere al controllo? Riesci a farmi vedere come hai sviluppato il delegate, il suo istanziamento ed il suo utilizzo (chiamata).

- Michael -
http://blogs.dotnethell.it/Regulator/

Mollella27 Profilo | Newbie

Alla fine sono riuscito a disabilitare il controllo threadsafe di visualstudio 2005 con l'istruzione:

Control.CheckForIllegalCrossThreadCalls = False

Con il controllo disabilitato sono riuscito ad accedere liberamente a tutti gli oggetti della form dal thread esterno.

Grazie a tutti.

Mike


0v3rCl0ck Profilo | Guru

Però così vuol dire che stai tentando di leggere l'oggetto non dal thread principale, e disabilitando quel check, puoi rischiare che l'applicativo vada in crash. Le prime applicazioni che avevo fatto senza threadsafe, con un utilizzo massiccio di thread, succedeva spessissimo che l'applicativo andava in errore! Quindi ti consiglio di risolvere il tuo problema, lasciando il threadsafe attivato e controllando per bene che thread sta invocando il codice che accede all'ogetto grafico, leggendo la proprietà Threading.Thread.CurrentThread.Name


Ciao
- Michael -
http://blogs.dotnethell.it/Regulator/

Mollella27 Profilo | Newbie

Siccome si tratta di un applicativo per l'impianto di dati che utilizzerò una volta sola non mi interessa tanto la sua instabilità, anche perchè fino ad ora non mi ha dato problemi. Comunque voglio seguire il tuo consiglio ed ecco il codice che uso per eseguire il delegate

Public Class ThreadSafePropertySetter

Delegate Sub SetCtrlPropertyDelegate(ByVal ctrl As Object, _
ByVal propName As String, ByVal propvalue As Object)
Delegate Sub getCtrlPropertyDelegate(ByVal ctrl As Object, _
ByVal propName As String)

Public Sub New(ByVal syncInvokeObject As ISynchronizeInvoke)
Me._syncInvokeObject = syncInvokeObject
End Sub

Public Sub SetCtrlProperty(Of T)(ByVal ctrl As Object, _
ByVal propName As String, ByVal propValue As T)
SetObjectProperty(ctrl, propName, propValue)
End Sub
Public Function GetCtrlProperty(ByVal ctrl As Object, _
ByVal propName As String) As Object
Return GetObjectProperty(ctrl, propName)
End Function

Protected Sub SetObjectProperty(ByVal obj As Object, _
ByVal propertyName As String, ByVal propertyValue As Object)
If _syncInvokeObject.InvokeRequired Then
_syncInvokeObject.Invoke(New SetCtrlPropertyDelegate(AddressOf _
SetCtrlProperty), New Object() {obj, propertyName, propertyValue})
Return
End If
Dim propInfo As PropertyInfo = obj.GetType.GetProperty(propertyName)
If propInfo IsNot Nothing Then
If propertyValue Is Nothing Then
propInfo.SetValue(obj, Nothing, Nothing)
ElseIf propInfo.PropertyType.IsAssignableFrom(propertyValue.GetType) Then
propInfo.SetValue(obj, propertyValue, Nothing)
End If
End If
End Sub
Protected Function GetObjectProperty(ByVal obj As Object, _
ByVal propertyName As String) As String

Dim propInfoArray() As System.Reflection.PropertyInfo
Dim result As New System.Text.StringBuilder
'get array of propertyinfo objects for our targets Type
propInfoArray = obj.GetType.GetProperties
Dim prop As Object

'iterate through these propertyinfo objects
For Each propInfo As System.Reflection.PropertyInfo In propInfoArray
Dim s As String
Try
's = Convert.ToString(propInfo.GetValue(obj, Nothing))
s = propInfo.GetValue(obj, Nothing).ToString()

Catch ex As Exception
s = ""
End Try
If propInfo.Name = propertyName Then
Return s
End If
Next
Return ""

End Function


Private _syncInvokeObject As ISynchronizeInvoke
End Class


La classe serve a settare la proprietà dell'oggetto oltre che leggerla. Ecco, proprio in lettura sul parametro selectedItem della listbox mi dà il problema di cui sopra. Non riesco a sapere questo benedetto selecteditem dal thread esterno.

Mike

0v3rCl0ck Profilo | Guru

Il problema è che la funzione GetObjectProperty dovrebbe essere implentata così perchè fosse thread safe:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Ma rimane comunque il problema che l'ultima cosa che ritorna quella funzione è l'ultimo return "" perchè cmq è una chiamata ricorsiva.

Ecco una possibile soluzione:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Uno dei modi è quello di passare da una variabile privata membro della classe, in questo modo il risultato, durante la chiamata ricorsiva, viene salvato nel membro della classe. La chiamata ricorsiva ritorna _PropertyValue e di quel valore non ce se ne fa niente, mentre l'ultimo return, cioè quello riferito alla prima chiamata, quando ancora l'InvokeRequired era a true, ritorna anch'esso _PropertyValue con il valore voluto. Oppure è possibile gestire il tutto anche con BeginInvoke e EndInvoke passando a quest'ultimo il risultato della funzione eseguita, ma comunque ritengo che la soluzione che ti ho proposto sia semplice e indolore (prestazionalmente parlando).

Spero di essere stato chiaro e utile

Ciaooo

- Michael -
http://blogs.dotnethell.it/Regulator/
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2025
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5