Aus Thread eine TextBox füllen (?)

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Mad Andy.

    Aus Thread eine TextBox füllen (?)

    Ich habe in einem Thread einen Listener laufen und möchte gerne den String der von einem Client an diesen Listener geschickt wird in einer TextBox ausgeben.
    Das Problem ist das der Listener wie gesagt in einem Thread läuft und die TetxtBox sich in einem Form befindet.
    In dem Thread habe ich also einen Delegate erstellt über den ich auf eine Funktion im Form zugreifen kann.
    In dieser Funktion (im Form) habe ich dann die TextBox mit Text gefüllt.

    Leider erscheint der Text nicht in der TextBox wie geplant, eine MsgBox() Ausgabe in der Funktion klappt aber beim delegate (invoke) aufruf, nur die TxtBox bleibt leer.

    Hier Mein Code

    Im Thread:

    VB.NET-Quellcode

    1. Module Threads
    2. ' Delegate
    3. Public Delegate Sub BoxFüllens(ByVal text As String)
    4. Public Sub Thread()
    5. Dim d1 As New BoxFüllens(AddressOf Form1.FülleTextBox)
    6. d1.Invoke("hallo")
    7. End Sub
    8. End Module


    Im Form1:

    VB.NET-Quellcode

    1. Public Sub FülleTextBox(ByVal nachricht As String)
    2. 'MsgBox("test")
    3. Me.TextBox1.Text = nachricht
    4. End Sub


    Mach ich einen denkfehler oder woran liegt das ?


    grüße ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThinkPad“ ()

    Wirklich saubere Methode, Metal Freak :P

    Das Problem ist wahrscheinlich, dass dein Parameter nicht übergeben wird, es muss imho ein Object-Array an Invoke übergeben werden. C# oder Option Strict hätten da sicher schon gemeckert ^^

    EDIT: hab was übersehen. Das Problem liegt eher daran, dass das Invoke auch noch im falschen Thread ausgeführt wird, da ja d1 im anderen Thread deklatiert wird. Du musst das Invoke über Me (Form ist logischerweise im Form-Thread) aufrufen. Ich hab den Code unten angepasst.

    Hab jetzt länger kein vb.net geschrieben, ich hoffe das stimmt :S

    VB.NET-Quellcode

    1. Dim d1 As New BoxFüllens(AddressOf Form1.FülleTextBox)
    2. Dim params(0) as Object : params(0) = "hallo"
    3. Me.Invoke(d1, params)


    Du solltest übrigens keine Umlaufe in Namen verwenden!
    (Da das eher ungewöhnlich ist, : bewirkt bei VB das gleiche wie ein Zeilenumbruch)

    Edit: Achja, als Übersichtshilfe solltest du irgendwie anmerken, dass es sich bei BoxFüllens um ein delegate handelt. Ich verwende dazu die .net-Konvention mit einem Handler hinten dran -> BoxFüllenHandler

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Mad Andy“ ()

    ^^

    VB.NET-Quellcode

    1. Dim params(0) As String
    2. params(0) = "hallo"


    VB.NET-Quellcode

    1. Public Sub FülleTextBox(ByVal nachricht)
    2. 'MsgBox("test")
    3. 'MsgBox(nachricht(0))
    4. Me.TextBox1.Text = nachricht(0)
    5. End Sub


    Ohne (as string) ;)

    müsste es nicht eigentlich so funktionieren ?
    die msgbox gibt z.b den text auch aus
    Hab meinen Post oben upgedated. Hab nicht gesehen, dass du das Invoke vom Delegate aufrufst, du musst das Invoke von der Form aufrufen. (siehe EDIT: mittendrin)
    Die Parameter muss man nur als Object-Array übergeben, wenn man das Delegate über Control.Invoke aufruft, da Parameterzahl und -typen nicht bekannt sind.
    Ähhm ich steh gerade auf dem Schlauch ^^

    Also derzeit hab ich das Delegate in meinem Thread deklariert.
    Ich glaube ich hätte auch noch erwähnen sollen das die Funktion die im Thread läuft sich in einer anderen (Modul) Datei befindet, also nicht in der Form1 Klasse !

    EDIT: Also wenn ich die Funktion die im Thread läuft ins Form1 (-Klasse) schreibe funktioniert das mit Me.Invoke(d1, params)
    Aber wie ist das mit Funktionen aus anderen Dateien ?
    Module sind schonmal unsauber, solltest du vielleicht ändern.

    Naja.. woraufs ankommt ist, dass du nicht das Delegate direkt "invokest", sondern über die Zielform da das auf jeden Fall im gleichen Thread läuft wie das Steuerelement. Statt Me musst du hald aus einem Modul heraus den Formnamen schreiben.
    Form1.Invoke(...) oder gleich Form1.TextBox1.Invoke(...)
    Also erstmal vielen Dank für deine Hilfe! :thumbsup:

    In Zeiten von OOP sind Module wohl wirklich nicht mehr so zeitgemäß. Werde ich morgen mal ändern.

    Trotzdem ist jetzt ne neue Fehlermedung aufgetaucht. Weil ich ja nicht die ganze Funktion die im Thread laufen soll im Form haben möchte, möchte ich diese in einer anderen Datei auslagern (derzeit noch ein Modul wie gesagt).

    statt me.Invoke(d1, params) (wie ich es in dem Form selbst machen würde), mache ich es so:

    VB.NET-Quellcode

    1. Form1.Invoke(d1, params)

    ... gibt die Fehlermeldung:
    Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.

    Was denn für ein Fensterhandle aufeinmal ? ^^
    Also ich hab zwei Dateien:

    Form1.vb:
    Hier starte ich das Thread aus Form1_Load()

    VB.NET-Quellcode

    1. Dim thread As New Threading.Thread(AddressOf Threads.Thread)
    2. thread.Start()


    und hier befindet sich auch die Funktion "Public Sub FülleTextBox()" die ich im Thread über das Delegate aufrufen möchte.

    VB.NET-Quellcode

    1. Public Sub FülleTextBox(ByVal nachricht)
    2. 'MsgBox("test")
    3. 'MsgBox(nachricht(0))
    4. Me.TextBox1.Text = nachricht
    5. End Sub


    Threads.vb - In diesem Module befindet sich die Funktion "Public Sub Thread()" die ich im Form1 starte.

    Code in "Public Sub Thread()"

    VB.NET-Quellcode

    1. Module Threads
    2. ' Delegate
    3. Public Delegate Sub BoxFüllens(ByVal Text)
    4. Public Sub Thread()
    5. Dim d1 As New BoxFüllens(AddressOf Form1.FülleTextBox)
    6. Dim params(0) As String
    7. params(0) = "hallo"
    8. Form1.Invoke(d1, params)
    9. End Sub
    10. End Module
    Ich vermute, dass das erst funktioniert, wenn das Form_Load abgeschlossen ist. Pack mal nen Timer auf die Form mit Interval 1.

    VB.NET-Quellcode

    1. Private Sub Timer1_Tick(Sender as Object) Handles Timer1.Tick
    2. Timer1.Stop()
    3. Dim thread As New Threading.Thread(AddressOf Threads.Thread)
    4. thread.Start()
    5. End Sub
    und nimm das aus Form_Load raus.
    Folgendes funktioniert bei mir (C# 2.0):

    Quellcode

    1. public class Class1
    2. {
    3. protected object sender;
    4. public Class1(object Sender)
    5. {
    6. this.sender = Sender;
    7. }
    8. public delegate void updateControlHandler(string value);
    9. public void update()
    10. {
    11. MainForm form = (MainForm)sender;
    12. updateControlHandler updateControl = new updateControlHandler(form.UpdateControl);
    13. object[] param = {"some value"};
    14. form.Invoke(updateControl, param);
    15. }
    16. }

    Quellcode

    1. public void UpdateControl(string val)
    2. {
    3. this.Text = val;
    4. }
    5. void MainFormLoad(object sender, EventArgs e)
    6. {
    7. Class1 myInstance = new Class1(this);
    8. Thread myThread = new Thread(
    9. new ThreadStart( myInstance.update ));
    10. myThread.Start();
    11. }


    Das mit dem Sender hab ich drin, weil ich keine öffentliche Instanzvariable von dem Formular hab (Form1 sollte statt dem Sender klappen, ist hald nicht wirklich Objektorientiert)
    das newThreadStart(...) spart man sich in VB, wird automatisch umgewandelt.
    Statt AdressOf hat man in C# einfach die Funktionen ohne Funktionsnamen.
    Also so wie ich das sehe ist mein Code fast das gleiche wie deiner. Kannst du mal vergleichen?
    Vielen Dank für deine Mühe !!

    Dein C# code funktioniert bei mir auch, nur umgewandelt in VB bringts wieder den Fehler:
    Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.

    So siehts übrigens in VB aus, hoffe ich hab keinen Fehler eingebaut, denn bei deinem

    VB.NET-Quellcode

    1. MainForm form = (MainForm)sender;
    bin ich mir nicht ganz sicher ob das in VB (siehe unten ^^) so richtig ist wie ich es geschrieben habe.

    Hier mal der code in VB

    VB.NET-Quellcode

    1. 'FORM
    2. Public Class MainForm
    3. Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    4. Dim myInstance As New Class1(Me)
    5. Dim myThread As New Thread(AddressOf myInstance.update)
    6. myThread.Start()
    7. End Sub
    8. Public Sub UpdateControl(ByVal val As String)
    9. Me.TextBox1.Text = val
    10. End Sub
    11. End Class
    12. ' THREAD
    13. Public Class Class1
    14. Protected sender As Object
    15. Public Sub New(ByVal Sender As Object)
    16. Me.sender = Sender
    17. End Sub
    18. Public Delegate Sub updateControlHandler(ByVal value As String)
    19. Public Sub update()
    20. Dim form As New MainForm() 'MainForm form = (MainForm)sender;
    21. Dim updateControl As New updateControlHandler(AddressOf form.UpdateControl)
    22. Dim param(0) As Object
    23. param(0) = "some value"
    24. form.Invoke(updateControl, param)
    25. End Sub
    26. End Class