Hallo,
in den folgenden Zeilen möchte ich kurz erklären, wie man für seine eigenen Applicationen Controls Threadsicher und wiederverwendbar macht.
Jeder weiß, wie er zur Laufzeit der Application einen bestimmten Property Wert ändert, jedoch sobald er sich nicht mehr in dem GUI Thread befindet, wird das ganze schon wieder etwas schwerer und ist mit etwas Gehirnschmalz verbunden.
z.B. Das Control Label
Warum kommt es zum Fehler, wenn ich Me.Label1.Text = "Background Thread Label" aufrufe?
Das ist recht einfach zu erklären. Me.Label1 gehört zum Haupt-/GUI Thread und ist entsprechend von diesem verwaltet, wenn ich nun über einen Background Thread oder Background Worker diesen Wert ändern will, muss ich dazu natürlich gewisse Richtlinien einhalten. Somit müssen wir eine Möglichkeit schaffen um von einem Nebenthread den Hauptthread anzusprechen und diesem die Änderung mitzuteilen. Damit dieser dann unsere Änderung an seinem verwaltetenen Objekt durchführen kann.
Wie man in meinem Beispiel sehen kann, verwende ich zum einen das Standard Label Control und zum anderen MyLabel Control. Da MyLabel von Label erbt, besitze ich bereits alle Funktionalitäten, die auch Label bietet, diese kann ich nun mit eigenen Funktionen und Methoden erweitern bzw. überschreiben. Für das oben abgebildete Beispiel bieten sich nun 2 mögliche Wege an wie ich mein Control gestalten kann, zum einen über einen Delegaten und zum anderen über einen Lambda Ausdruck.
Beispiel Möglichkeit 1 - Sehr viel Schreibaufwand!
Spoiler anzeigen
Möglichkeit 2 - Etwas komplizierter, aber deutlich Pflegeleichter!
Spoiler anzeigen
Ich hoffe ich konnte damit einige Anregungen schaffen.
in den folgenden Zeilen möchte ich kurz erklären, wie man für seine eigenen Applicationen Controls Threadsicher und wiederverwendbar macht.
Jeder weiß, wie er zur Laufzeit der Application einen bestimmten Property Wert ändert, jedoch sobald er sich nicht mehr in dem GUI Thread befindet, wird das ganze schon wieder etwas schwerer und ist mit etwas Gehirnschmalz verbunden.
z.B. Das Control Label
VB.NET-Quellcode
- Imports System.Threading
- Public Class Form1
- Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- 'ERFOLGREICH
- Me.Label1.Text = "GUI Thread Label"
- 'ERFOLGREICH
- Me.MyLabel1.Text = "GUI Thread MyLabel"
- 'FEHLSCHLAG
- Dim newThread1 As New Thread(AddressOf DoWorkLabel)
- newThread1.Start()
- 'ERFOLGREICH
- Dim newThread2 As New Thread(AddressOf DoWorkMyLabel)
- newThread2.Start()
- End Sub
- Private Sub DoWorkLabel()
- 'ERROR
- 'FEHLER: Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement Label1
- 'erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
- Me.Label1.Text = "Background Thread Label"
- End Sub
- Private Sub DoWorkMyLabel()
- 'ERFOLGREICH
- Me.MyLabel1.Text = "Background Thread MyLabel"
- End Sub
- End Class
Warum kommt es zum Fehler, wenn ich Me.Label1.Text = "Background Thread Label" aufrufe?
Das ist recht einfach zu erklären. Me.Label1 gehört zum Haupt-/GUI Thread und ist entsprechend von diesem verwaltet, wenn ich nun über einen Background Thread oder Background Worker diesen Wert ändern will, muss ich dazu natürlich gewisse Richtlinien einhalten. Somit müssen wir eine Möglichkeit schaffen um von einem Nebenthread den Hauptthread anzusprechen und diesem die Änderung mitzuteilen. Damit dieser dann unsere Änderung an seinem verwaltetenen Objekt durchführen kann.
Wie man in meinem Beispiel sehen kann, verwende ich zum einen das Standard Label Control und zum anderen MyLabel Control. Da MyLabel von Label erbt, besitze ich bereits alle Funktionalitäten, die auch Label bietet, diese kann ich nun mit eigenen Funktionen und Methoden erweitern bzw. überschreiben. Für das oben abgebildete Beispiel bieten sich nun 2 mögliche Wege an wie ich mein Control gestalten kann, zum einen über einen Delegaten und zum anderen über einen Lambda Ausdruck.
Beispiel Möglichkeit 1 - Sehr viel Schreibaufwand!
VB.NET-Quellcode
- 'Bei dieser Möglichkeit wird der Schreibaufwand stark ansteigen und die Übersichtlichkeit wird darunter stark leiden.
- Public Class MyLabel
- Inherits Label
- 'Wird benötigt, für den Aufruf von diversen Methoden innerhalb des Controls MyLabel
- Private Delegate Sub ControlStringInvoke(ByVal value As String)
- 'Überschreibt die Eigenschaft von Label, welches hier als Basisklasse fungiert.
- Public Overrides Property Text As String
- Get
- Return MyBase.Text
- End Get
- Set(ByVal value As String)
- 'Benötigen wir einen Invoke, da wir uns in einem Background Thread befinden?
- If Me.InvokeRequired Then
- Me.Invoke(New ControlStringInvoke(AddressOf SetText), value)
- Else
- MyBase.Text = value
- End If
- End Set
- End Property
- 'Für jede Eigenschaft, die wir Threadsicher machen wollen, benötigen wir eine solche Methode um den Aufruf erneut zu senden.
- Private Sub SetText(ByVal value As String)
- Me.Text = value
- End Sub
- End Class
Möglichkeit 2 - Etwas komplizierter, aber deutlich Pflegeleichter!
VB.NET-Quellcode
- 'Bei dieser Möglichkeit verzichten wir ganz auf Delegates und gewinnen wieder an Übersichtlichkeit, jedoch müssen wir uns mit Lambda Ausdrücken auseinander setzen.
- Public Class MyLabel
- Inherits Label
- 'Überschreibt die Eigenschaft von Label, welches hier als Basisklasse fungiert.
- Public Overrides Property Text As String
- Get
- Return MyBase.Text
- End Get
- Set(ByVal value As String)
- 'Benötigen wir einen Invoke, da wir uns in einem Background Thread befinden?
- If Me.InvokeRequired Then
- 'Bei diesem Befehl, erhält man von Visual Studio folgende Warnung:
- 'Der Ausdruck ruft rekursiv die enthaltene Text-Eigenschaft auf.
- 'Was ja im Prinzip richtig ist, sogar gewünscht. :)
- Me.Invoke(Sub(x As String) Me.Text = x, value)
- Else
- MyBase.Text = value
- End If
- End Set
- End Property
- End Class
Ich hoffe ich konnte damit einige Anregungen schaffen.
Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Samtrion“ () aus folgendem Grund: Korrekturen