GUIHelper

    • VB.NET

    Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      EDIT: Mit dem Framework 4.0 gehts auch so schon sehr einfach. s. Post weiter unten. Bei älteren FW ist es mit dieser Methode etwas weniger Schreibarbeit.

      Vermutlich nicht ganz sauber, aber ...

      In diesem Thread :
      [VB 2010] Funktion per Thread starten.
      hatte ich mal ne Idee und FatFire hat sich dann in einen an sich recht simplen Code gegossen.

      Geht darum, dass immer wieder das Problem auftaucht, dass man aus einem Thread auf ein Control in einem anderen Thread zugreifen muss. Die Lösungen sind ja bekannt, aber je nachdem wie viele (verschiedene) Controls das betrifft, ist das manchmal ziemlich viel Schreibarbeit. Wenn man sich das schenken will und Performance nicht das Hauptkriterium ist, kann man es halt mit dem Code von FatFire lösen.
      Leider ist FF derzeit nicht mehr hier unterwegs, und so kann ich ihn nicht fragen.
      Da das ganze aber "auch" auf meinem Mist gewachsen ist hier noch mal der Code von FF, weil der sicherlich besser in DIESES Unterforum passt:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Module GUIHelper
      2. Private Delegate Sub InvokeDelegateWOReturn(ByVal Control As System.Windows.Forms.Control, _
      3. ByVal Method As String, ByVal Flags As System. _
      4. Reflection.BindingFlags, ByVal Values As Object())
      5. Private Delegate Function InvokeDelegateWReturn(ByVal Control As System.Windows.Forms.Control, _
      6. ByVal Method As String, ByVal Flags As System. _
      7. Reflection.BindingFlags, ByVal Values As Object()) As Object
      8. Private SetSomethingDelegateInstance As New InvokeDelegateWOReturn(AddressOf SetSomething)
      9. Private GetSomethingDelegateInstance As New InvokeDelegateWReturn(AddressOf GetSomething)
      10. Private Sub SetSomething(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      11. ByVal Flags As System.Reflection.BindingFlags, ByVal Values As Object())
      12. If Control.InvokeRequired Then
      13. Control.Invoke(SetSomethingDelegateInstance, Control, Method, Flags, Values)
      14. Else
      15. Control.GetType.InvokeMember(Method, Flags, System.Type.DefaultBinder, Control, Values)
      16. End If
      17. End Sub
      18. Private Function GetSomething(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      19. ByVal Flags As System.Reflection.BindingFlags, ByVal Values As Object()) As Object
      20. If Control.InvokeRequired Then
      21. Return Control.Invoke(GetSomethingDelegateInstance, Control, Method, Flags, Values)
      22. Else
      23. Return Control.GetType.InvokeMember(Method, Flags, Nothing, Control, Values)
      24. End If
      25. End Function
      26. Public Sub SetProperty(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      27. ByVal ParamArray Values As Object())
      28. SetSomething(Control, Method, Reflection.BindingFlags.SetProperty, Values)
      29. End Sub
      30. Public Function GetProperty(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      31. ByVal ParamArray Values As Object()) As Object
      32. Return GetSomething(Control, Method, Reflection.BindingFlags.GetProperty, Values)
      33. End Function
      34. Public Function InvokeMethod(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      35. ByVal ParamArray Values As Object()) As Object
      36. Return GetSomething(Control, Method, Reflection.BindingFlags.InvokeMethod, Values)
      37. End Function
      38. Public Sub SetField(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      39. ByVal ParamArray Values As Object())
      40. SetSomething(Control, Method, Reflection.BindingFlags.SetField, Values)
      41. End Sub
      42. Public Function GetField(ByVal Control As System.Windows.Forms.Control, ByVal Method As String, _
      43. ByVal ParamArray Values As Object()) As Object
      44. Return GetSomething(Control, Method, Reflection.BindingFlags.GetField, Values)
      45. End Function
      46. End Module


      EDIT: Auf Wunsch eines Herren ... ;)

      Kleines Beispiel:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Class Form1
      2. Private Sub foo()
      3. Try
      4. TextBox1.Text = "Hallo"
      5. TextBox1.AppendText(" Welt")
      6. Catch ex As Exception
      7. MessageBox.Show(ex.Message)
      8. End Try
      9. End Sub
      10. Private Sub bar()
      11. ' Kein Fehler, wenn von einem anderen Thread aufgerufen!
      12. GUIHelper.SetProperty(TextBox1, "Text", "Hallo")
      13. GUIHelper.InvokeMethod(TextBox1, "AppendText", " Welt")
      14. End Sub
      15. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      16. ' löst einen Fehler in foo aus!
      17. Dim t As New Threading.Thread(AddressOf foo)
      18. t.Start()
      19. ' löst KEINEN Fehler aus!
      20. Dim s As New Threading.Thread(AddressOf bar)
      21. s.Start()
      22. End Sub
      23. End Class

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „picoflop“ ()

      jop ist wirklich nicht das sauberste mit Reflection :P

      Nur, geht das jetzt seit neuestem nicht auch recht kurz mit den Invokes in einer Lambda Funktion, hab ich zwar noch nie genutzt und eine zu alte IDE dafür...aber irgendwie so, oder so ähnlich:

      VB.NET-Quellcode

      1. Me.Invoke(Sub() Me.Text = "der neue Text")

      da spart man evtl. sogar noch etwas, dasselbe mit dem GUIHelper dürfte ja in etwa so aussehen:

      VB.NET-Quellcode

      1. GUIHelper.SetProperty(Me,"Text","der neue Text")


      @Vorposter:
      Funktioniert über Reflection :P
      durch die BindingFlags wird abgerufen, nach welchem Type geguckt werden soll(Eigenschaft, Variable, Methode...)..
      im Endeffekt wird alles auf SetSomething bzw. GetSomething weitergeleitet, dort wird überprüft, ob ein Invoke benötigt wird, ist dies der Fall, wird dieselbe Methode nocheinmal über einen Invoke + Delegaten aufgerufen, wodurch diese im GUI Thread ausgeführt wird, über Reflection wird dann mit GetType das entsprechende Ereigniss ausgeführt, über den Namen der Variable/Property/...

      Die Erklärung kann noch stark ausgeweitet werden :P
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---
      @jvbsl: thx ;)

      Mit FW 4.0 gehts mit Bordmitteln schon einfach genug:

      VB.NET-Quellcode

      1. Public Class Form1
      2. Private Sub foo()
      3. Me.Invoke(Sub() TextBox1.Text = "Hallo ")
      4. Me.Invoke(Sub() TextBox1.AppendText("Welt"))
      5. Dim s As String = Me.Invoke(Function() As String
      6. Return TextBox1.Text
      7. End Function)
      8. MessageBox.Show(s)
      9. End Sub
      10. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      11. Dim t As New Threading.Thread(AddressOf foo)
      12. t.Start()
      13. End Sub
      14. End Class
      meines Wissens geht es bereits mit der 2008er Version der IDE und somit sollte es auch mit 3.5 und evtl. auch 3.0 funktionieren(vielleicht ist es auch nur IDE bedingt und nicht Framework bedingt...)
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---
      Deine Deklaration

      VB.NET-Quellcode

      1. Function() As String
      musst Du ändern in

      VB.NET-Quellcode

      1. Function() As Object
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!
      Das macht möglicherweise doch Sinn, da Du den Typ des Return-Wertes nicht überschreiben kannst:
      Bilder
      • Invoke.jpg

        12,78 kB, 644×62, 273 mal angesehen
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!
      nein - picoflop proggt da Strict Off.
      Strict On wäre:

      VB.NET-Quellcode

      1. Dim s As String = DirectCast(Me.Invoke(Function() As String
      2. Return TextBox1.Text
      3. End Function), String)
      4. 'oder als Einzeiler
      5. Dim s As String = DirectCast(Me.Invoke( Function() TextBox1.Text), String)

      Interessanterweise finden bei lesenden Zugriffen auf Controls keine ungültigen threadübergreifenden Zugriffe statt - jdfs. nicht immer.
      Try this

      VB.NET-Quellcode

      1. Dim t As New Thread(Sub() MsgBox(TextBox1.Text))
      2. t.Start()
      3. 'oder:
      4. Dim gettext As action = Sub() MsgBox(TextBox1.Text)
      5. gettext.BeginInvoke(AddressOf gettext.EndInvoke, Nothing)
      (is in dieser Weise lustig, weil zeigt 2 Messageboxen gleichzeitig an, die originellerweise unmodal sind, da im Nebenthread laufend ;))

      In diesem Fall ist Control.Invoke also unnötig (und mit Treenode.Text habichs auch getestet).

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ErfinderDesRades“ ()