Tipp-Upload: VB.NET 0017: Threadübergreifende Events
von Spatzenkanonier
Über den Tipp
Dieser Tippvorschlag ist noch unbewertet.
Der Vorschlag ist in den folgenden Kategorien zu finden:
- Fenster
- Sonstiges
- Sprachmerkmale
- Timer und Zeiten
Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
Threading, generisch, Ereignis, Event
Damit er übernommen werden kann, müssen noch Änderungen daran vorgenommen werden. Sofern Sie der Autor sind, können Sie sich anmelden, um die Liste einzusehen.
Der Vorschlag wurde erstellt am: 29.07.2007 19:30.
Die letzte Aktualisierung erfolgte am 04.02.2009 21:59.
Beschreibung
Will man von einem NebenThread aus benutzerdefinierte Ereignisse auslösen, deren Verarbeitung schließlich angezeigt wird, so empfiehlt es sich, schon das Auslösen selbst mittels Control.Invoke() in den Hauptthread zu "überführen", ansonsten eine Fehlermeldung wegen ungültigem threadübergreifendem Aufruf erfolgt.
Es handelt sich um einen Anwendungsfall der in Tippvorschlag 278 (Threading) vorgestellten Vorgehensweise. Vorraussetzung ist natürlich, daß das Ereignis konform zu den Framework-Konventionen implementiert ist, siehe auch Tippvorschlag 283 (Ereignisse implementieren)
Schwierigkeitsgrad |
Verwendete API-Aufrufe: |
Download: |
' Dieser Source stammt von http://www.activevb.de ' und kann frei verwendet werden. Für eventuelle Schäden ' wird nicht gehaftet. ' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. ' Ansonsten viel Spaß und Erfolg mit diesem Source! ' ' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird. ' In den Zip-Dateien ist er jedoch zu finden. ' ------------ Anfang Projektgruppe CountDown.sln ------------ ' ----------- Anfang Projektdatei CountDown.vbproj ----------- ' ---------------- Anfang Datei CountDown.vb ---------------- ' IDE-Voreinstellungen: ' Option Explicit On ' Option Strict On ' "My Project"-Einstellungen: ' Imports Microsoft.VisualBasic.ControlChars ' Imports System.Windows.Forms ' Imports System Imports System.Threading Public Class CountDown #Region "benutzerdefinierte Events" Public Class TickEventArgs Inherits EventArgs Public ReadOnly Counter As Integer Public Sub New(ByVal Counter As Integer) Me.Counter = Counter End Sub End Class ' TickEventArgs ' Tick verwendet einen generischen Eventhandler für TickEventArgs Public Event Tick As EventHandler(Of TickEventArgs) Protected Overridable Sub OnTick(ByVal e As TickEventArgs) RaiseEvent Tick(Me, e) End Sub ' Finished verwendet den Standard-Eventhandler für EventArgs.Empty Public Event Finished As EventHandler Protected Overridable Sub OnFinished() RaiseEvent Finished(Me, EventArgs.Empty) End Sub #End Region ' benutzerdefinierte Events ' System.Threading.Timer läuft im NebenThread Private _AsyncTimer As New System.Threading.Timer(AddressOf AsyncTimer_Callback, Nothing, _ Timeout.Infinite, Timeout.Infinite) Private _Counter As Integer Public Sub Start(ByVal InitValue As Integer) _Counter = InitValue _AsyncTimer.Change(dueTime:=0, period:=1000) ' dueTime=0: sofort ticken End Sub Private Sub AsyncTimer_Callback(ByVal state As Object) ' Der Fehler ist reproduzierbar, indem die folgende Zeile statt der übernächsten ausgeführt ' wird. Auch gut erkennbar, wie ein beliebiger synchroner Aufruf umzuschreiben ist ' OnTick(New TickEventArgs(_Counter)) CrossThread.RunGui(AddressOf OnTick, New TickEventArgs(_Counter)) ' Tick raisen If _Counter = 0 Then _AsyncTimer.Change(Timeout.Infinite, Timeout.Infinite) ' stop timer CrossThread.RunGui(AddressOf OnFinished) ' Finished raisen End If _Counter -= 1 ' "down-counten" ;) End Sub End Class ' ----------------- Ende Datei CountDown.vb ----------------- ' --------------- Anfang Datei CrossThread.vb --------------- ''' <summary> ''' Stellt Methoden bereit, mit denen ein beliebiger Methoden-Aufruf mit bis zu 3 Argumenten ''' in einen Nebenthread verlegt werden kann, bzw. aus einem Nebenthread in den Hauptthread ''' </summary> Public Class CrossThread Public Shared Sub RunAsync(Of T1, T2, T3)(ByVal Action As Action(Of T1, T2, T3), ByVal _ Arg1 As T1, ByVal Arg2 As T2, ByVal Arg3 As T3) ' Aufruf von Action.EndInvoke() gewährleisten, indem er als Callback-Argument ' mitgegeben wird Action.BeginInvoke(Arg1, Arg2, Arg3, AddressOf Action.EndInvoke, Nothing) End Sub Public Shared Sub RunAsync(Of T1, T2)(ByVal Action As Action(Of T1, T2), ByVal Arg1 As _ T1, ByVal Arg2 As T2) Action.BeginInvoke(Arg1, Arg2, AddressOf Action.EndInvoke, Nothing) End Sub Public Shared Sub RunAsync(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1) Action.BeginInvoke(Arg1, AddressOf Action.EndInvoke, Nothing) End Sub Public Shared Sub RunAsync(ByVal Action As Action) Action.BeginInvoke(AddressOf Action.EndInvoke, Nothing) End Sub Private Shared Function GuiCrossInvoke(ByVal Action As [Delegate], ByVal ParamArray Args( _ ) As Object) As Boolean If Application.OpenForms.Count = 0 Then ' wenn kein Form mehr da ist, so tun, als ob das Invoking ausgeführt wäre Return True End If If Application.OpenForms(0).InvokeRequired Then Application.OpenForms(0).BeginInvoke(Action, Args) Return True End If End Function Public Shared Sub RunGui(Of T1, T2, T3)(ByVal Action As Action(Of T1, T2, T3), ByVal Arg1 _ As T1, ByVal Arg2 As T2, ByVal Arg3 As T3) ' falls Invoking nicht erforderlich, die Action direkt ausführen If Not GuiCrossInvoke(Action, Arg1, Arg2, Arg3) Then Action(Arg1, Arg2, Arg3) End Sub Public Shared Sub RunGui(Of T1, T2)(ByVal Action As Action(Of T1, T2), ByVal Arg1 As T1, _ ByVal Arg2 As T2) If Not GuiCrossInvoke(Action, Arg1, Arg2) Then Action(Arg1, Arg2) End Sub Public Shared Sub RunGui(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1) If Not GuiCrossInvoke(Action, Arg1) Then Action(Arg1) End Sub Public Shared Sub RunGui(ByVal Action As Action) If Not GuiCrossInvoke(Action) Then Action() End Sub End Class ' ---------------- Ende Datei CrossThread.vb ---------------- ' --------------- Anfang Datei frmCountDown.vb --------------- Public Class frmCountDown Private WithEvents _CountDown As New CountDown Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click _CountDown.Start(10) End Sub Private Sub _CountDown_Tick( _ ByVal sender As Object, _ ByVal e As CountDown.TickEventArgs) Handles _CountDown.Tick Me.Label1.Text = e.Counter.ToString End Sub Private Sub _CountDown_Finished(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles _CountDown.Finished MsgBox("Go!") End Sub End Class ' ---------------- Ende Datei frmCountDown.vb ---------------- ' ------------- Anfang Datei UsefulDelegates.vb ------------- ' Compiler-Direktive: Code nur aktivieren, wenn die VB-Compiler-Version < 9.0 ist #If VBC_VER < 9.0 Then ' diese 7 generischen Delegaten hat Framework 3 dem Framework 2 vorraus. ' Bis diese Datei eingebunden wird ;) ' Sie decken **jedes** Delegaten-Einsatzgebiet ab (Ausnahme: Delegaten mit ByRef-Parametern). ' ( Im FW 2 bereits bekannt: Public Delegate Sub Action(Of T)(ByVal arg As T) ) Public Delegate Sub Action() Public Delegate Sub Action(Of T1, T2)(ByVal arg1 As T1, ByVal arg2 As T2) Public Delegate Sub Action(Of T1, T2, T3)(ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3) Public Delegate Function Func(Of T)() As T Public Delegate Function Func(Of T1, T2)(ByVal arg1 As T1) As T2 Public Delegate Function Func(Of T1, T2, T3)(ByVal arg1 As T1, ByVal arg2 As T2) As T3 Public Delegate Function Func(Of T1, T2, T3, T4)(ByVal arg1 As T1, ByVal arg2 As T2, ByVal _ arg3 As T3) As T4 #End If ' -------------- Ende Datei UsefulDelegates.vb -------------- ' ------------ Ende Projektdatei CountDown.vbproj ------------ ' ------------- Ende Projektgruppe CountDown.sln -------------
Diskussion
Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.
Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.