Benutzer mit den meisten Antworten
control aus fremden thread ansprechen (mal wieder)

Frage
-
Ich hab nun verschiedene Möglichkeiten kennengelernt, das zu lösen.
Die einfachste finde ich:
Dim chkUpdate As MethodInvoker = Sub() FrmMain.chkRecord.Checked = False chkUpdate.Invoke()
Klappt aber nicht und ich weiß nicht wieso.
Der code ist aus einem Delegatenaufruf:
delg.BeginInvoke(rück, AktualSong, output, quality, AddressOf RecordIsReady, Nothing)
und steht in der function recordIsReady, die auch vorschriftsmäßig durchlaufen wird wenn der delegat fertig ist.
Lediglich meine checkbox reagiert nicht auf das setzen der .checked-Eigenschaft.
Liegt das daran, dass dieser ganze code in einem separaten modul liegt, außerhalb der Class Frmmain und generell von da nicht auf die frmmain-controls zugegriffen werden kann?
Oder stimmt was mit meinem code nicht?Gruß und Dank!
Thomas
Antworten
-
Hallo,
außerhalb einer Klasse auf ein Control zuzugreifen ist nicht der beste Stil. Besser wäre für die von außen Setzbaren Eigenschaften auch solche zu erstellen. Sonst könntest du von außen alles frei verändern, was nicht OOP-Gerecht wäre.Ich nehme mal an, das FrmMain die Instanz der Form ist. In diesem Fall funktioniert es bei mir:
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim t As New Thread(Sub() modul.XYZ(Me) 'Funktion vom Module aufrufen, Me übergeben um Zugriff auf die Form zu erhalten' End Sub) t.Start() 'Thread zum testen' End Sub End Class Module modul Public Sub XYZ(frm As Form1) frm.chkRecord.Invoke(Sub() frm.CheckBox1.Checked = False) 'Über Invoke zugreifen' End Sub End Module
Von wo der Aufruf kommt sollte eigentlich keine Rolle spielen, solange die Instanz der Form auch stimmt.
Wie gesagt, so wäre es besser:
Public Class Form1 Public Property IsCheckBoxRecordChecked() As Boolean Get If Me.chkRecord.InvokeRequired Then'Zugriff aus einem anderen Thread heraus?' Dim isChecked As Boolean Me.chkRecord.Invoke(Sub() isChecked = Me.chkRecord.Checked) Return isChecked Else Return Me.chkRecord.Checked End If End Get Set(value As Boolean) If Me.chkRecord.InvokeRequired Then Me.chkRecord.Invoke(Sub() Me.chkRecord.Checked = value) Else Me.chkRecord.Checked = value End If End Set End Property End Class Module modul Public Sub XYZ(frm As Form1) frm.IsCheckBoxRecordChecked = False'Kein Invoke mehr erforderlich, die Form kümmert sich darum' End Sub End Module
In diesem Fall ruft die Eigenschaft ggf. Invoke auf, wenn benötigt.
Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert Migration Sucks Sonntag, 24. November 2013 16:47
-
zu 1.
Die Form muss übergeben werden. Sonst weiß dein 2. Modul/Klasse nicht um was es geht. Siehe 4.
Als Beispiel: Du erzeugst die Form1 zwei mal und rufst dann Form1.Title = "Test" auf. Von welcher Form wird der Titel geändert?zu 2.
Siehe 4. Es liegt wieder am 2. Thread und das du die Forminstanz nicht übergeben hast.zu 3.
Siehe 1 und 4.zu 4.
Das ordne ich unter "Eigenheit von VB" ein. Durch die Benutzung einer nicht shared Methode als shared wird eine Instanz erstellt und dann der Code von dieser aufgerufen. Ich denke mal, das es am 2. Thread liegt, das nicht auf die bereits existierende Instanz zugegriffen wird. Solchen Code sollte man aber nicht mehr verwenden, da er nur noch zur Abwärtskompatibilität (VB 6 und früher) funktioniert. AlsoDim frm As New Form1() frm.ShowDialog()
Anstelle vonForm1.ShowDialog()
Gleiches gilt für den Zugriff auf deine CheckBox etc.
Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert Migration Sucks Sonntag, 24. November 2013 16:47
Alle Antworten
-
Hallo,
außerhalb einer Klasse auf ein Control zuzugreifen ist nicht der beste Stil. Besser wäre für die von außen Setzbaren Eigenschaften auch solche zu erstellen. Sonst könntest du von außen alles frei verändern, was nicht OOP-Gerecht wäre.Ich nehme mal an, das FrmMain die Instanz der Form ist. In diesem Fall funktioniert es bei mir:
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim t As New Thread(Sub() modul.XYZ(Me) 'Funktion vom Module aufrufen, Me übergeben um Zugriff auf die Form zu erhalten' End Sub) t.Start() 'Thread zum testen' End Sub End Class Module modul Public Sub XYZ(frm As Form1) frm.chkRecord.Invoke(Sub() frm.CheckBox1.Checked = False) 'Über Invoke zugreifen' End Sub End Module
Von wo der Aufruf kommt sollte eigentlich keine Rolle spielen, solange die Instanz der Form auch stimmt.
Wie gesagt, so wäre es besser:
Public Class Form1 Public Property IsCheckBoxRecordChecked() As Boolean Get If Me.chkRecord.InvokeRequired Then'Zugriff aus einem anderen Thread heraus?' Dim isChecked As Boolean Me.chkRecord.Invoke(Sub() isChecked = Me.chkRecord.Checked) Return isChecked Else Return Me.chkRecord.Checked End If End Get Set(value As Boolean) If Me.chkRecord.InvokeRequired Then Me.chkRecord.Invoke(Sub() Me.chkRecord.Checked = value) Else Me.chkRecord.Checked = value End If End Set End Property End Class Module modul Public Sub XYZ(frm As Form1) frm.IsCheckBoxRecordChecked = False'Kein Invoke mehr erforderlich, die Form kümmert sich darum' End Sub End Module
In diesem Fall ruft die Eigenschaft ggf. Invoke auf, wenn benötigt.
Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert Migration Sucks Sonntag, 24. November 2013 16:47
-
Vielen Dank Tom,
1. ich hatte die Form gar nicht an das sub übergeben. Hatte einfach den Namen der Form (aus den Dateieingenschaften) eingesetzt (siehe Pkt 4). Daher hatte Dein erster code bei mir zunächst nicht funktioniert.
2. Kannst Du sagen, warum das mit dem MethodInvoker nicht geklappt hat?
3. Deine 2. Methode klappt auch nur dann, wenn ich die Form als parameter übergebe sonst nicht.
4. Wenn das ganze nicht in einem anderen Thread abläuft, funktioniert dieser Aufruf aus einem Modul Form1.control.eigenschaft="soundso", auch wenn die Form nicht als Parameter übergeben wurde.
Da ich das nur als Amateur betreibe ist das für mich von der Theorie schwer nachzuvollziehen. Ich lerne by doing...
Herzlich
Thomas
-
zu 1.
Die Form muss übergeben werden. Sonst weiß dein 2. Modul/Klasse nicht um was es geht. Siehe 4.
Als Beispiel: Du erzeugst die Form1 zwei mal und rufst dann Form1.Title = "Test" auf. Von welcher Form wird der Titel geändert?zu 2.
Siehe 4. Es liegt wieder am 2. Thread und das du die Forminstanz nicht übergeben hast.zu 3.
Siehe 1 und 4.zu 4.
Das ordne ich unter "Eigenheit von VB" ein. Durch die Benutzung einer nicht shared Methode als shared wird eine Instanz erstellt und dann der Code von dieser aufgerufen. Ich denke mal, das es am 2. Thread liegt, das nicht auf die bereits existierende Instanz zugegriffen wird. Solchen Code sollte man aber nicht mehr verwenden, da er nur noch zur Abwärtskompatibilität (VB 6 und früher) funktioniert. AlsoDim frm As New Form1() frm.ShowDialog()
Anstelle vonForm1.ShowDialog()
Gleiches gilt für den Zugriff auf deine CheckBox etc.
Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert Migration Sucks Sonntag, 24. November 2013 16:47
-
Hallo Thomas und Tom,
ich möchte hier einen kleinen Einspruch anmelden ;)
Die Eigenschaften einer Windows Forms Klasse (vom Steuerlement bis zum Formular) sind nicht ohne Grund in der Vorgabe niemals thread-sicher. So verlockend es sein mag, sämtliche Eigenschaften thread-sicher zu machen, ist das übers Ziel hinausgeschossen und kann sogar behindern.
Denn i. a. greift man nicht über einen anderen Thread zu und wenn man das tut, so weiß der Entwickler das (oder sollte es zumindest ;) Control.[Begin]Invoke haben einigen Overhead, da sie zum einen das Formular finden müssen, den Ausführungskontext sichern (teuer!) und eine Nachricht über die Meldeschleife verschicken.
Schon um den zu minimieren, würde / sollte man bei mehreren Eigenschaften (und/oder Methodenzugriffen) diese zusammenfassen - was das "Doppelkodieren" überflüssig machen würde. Gefährlich ist es zudem, da man jedes Mal gucken muss, ob eine Eigenschaft thread-sicher implementiert wurde oder nicht und ein Dritter vom Standard, also nicht-thread-sicher ausgeht.
Deswegen sollte man dies als (Set-)Methoden bereitstellen, deren Namen ggf. einen Hinweis darauf enthalten - z. B. InvokeRecordChecked(...).
Nicht zum letzten kann der Zugriff über den SynchronzationContext günstiger sein, da i. a. effizienter bei komplexeren Zugriffen. Der BackgroundWorker tat es schon immer und in neueren .NET Versionen machen es die Task (Async) Klassen ebenso. Was aber auch gegen "zu schlaue Eigenschaften" spricht.
Gruß Elmar
P. S.: Das es anfangs nicht funktioniert hat, dürfte an 4. liegen, siehe Von My.Forms und My.WebServices bereitgestellte Standardobjektinstanzen (Visual Basic) - ein Feature, auf das man besser verzichtet hätte, da es wenig nützt aber solche Fehler provoziert und schwer erkennbar macht. Siehe http://stackoverflow.com/questions/4698538/there-is-a-default-instance-of-form-in-vb-net-but-not-in-c-why für eine Erläuterung (Hans Passant) wie es implementiert ist (und die Kommentare warum man besser die Finger davon lässt).
-
Macht Sinn ;)
Danke für die Erklärung.Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.