Pour reformuler clairement le problème, vous devez fournir un composant visuel à un client qui l'utilisera dans son propre programme. Le programme du client, hors de votre contrôle, bloque son thread principal (c'est-à-dire l'interface utilisateur) et vous devez faire en sorte que votre composant visuel continue de fonctionner pendant que le programme du client est gelé.
Soyons clairs ici - le fait que le programme du client soit gelé est leur et il est le résultat direct de défauts dans les domaines suivants leur code d'application. Ce qui suit est un hack, et un hack horrible que personne ne devrait jamais utiliser. Donc, pendant que vous CAN pour les raisons suivantes, il ne devrait presque jamais être considéré comme une solution recommandée pour quoi que ce soit. Elle va à l'encontre de toutes les meilleures pratiques.
Cela dit, vous devez créer un deuxième contexte d'application et une nouvelle boucle de messages qui peut continuer à fonctionner à côté du thread principal de l'interface utilisateur. Une classe comme celle-ci fonctionnerait :
Imports System.Threading
Public Class SecondUIClass
Private appCtx As ApplicationContext
Private formStep As Form
Private trd As Thread
Private pgBar As ProgressBar
Delegate Sub dlgStepIt()
Public Sub New()
trd = New Thread(AddressOf NewUIThread)
trd.SetApartmentState(ApartmentState.STA)
trd.IsBackground = True
trd.Start()
End Sub
Private Sub NewUIThread()
formStep = New Form()
pgBar = New ProgressBar()
formStep.Controls.Add(pgBar)
appCtx = New ApplicationContext(formStep)
Application.Run(appCtx)
End Sub
Public Sub StepTheBar()
formStep.Invoke(New dlgStepIt(AddressOf tStepIt))
End Sub
Private Sub tStepIt()
pgBar.PerformStep()
End Sub
End Class
Essentiellement, ce que vous faites avec la classe ci-dessus est de créer un nouveau contexte d'application dans un nouveau thread STA (en donnant à ce thread une boucle de messages). Ce contexte contient un formulaire principal (donnant au thread la propriété de celui-ci et la responsabilité du traitement de ses messages) qui peut continuer à fonctionner en dehors du thread principal de l'interface utilisateur. Cela revient à avoir un programme dans un programme - deux threads d'interface utilisateur, chacun avec son propre ensemble de contrôles mutuellement exclusifs.
Les appels qui interagissent avec l'un des contrôles appartenant au nouveau thread d'interface utilisateur (ou à son formulaire) doivent être marshallés avec la fonction Control.Invoke
du fil d'exécution primaire de l'interface utilisateur (ou d'autres) pour s'assurer que votre nouveau fil d'exécution de l'interface utilisateur est celui qui effectue l'interaction. Vous pouvez également utiliser BeginInvoke
ici.
Cette classe n'a AUCUN code de nettoyage, aucun contrôle de sécurité, etc. (soyez prévenus) et je ne suis même pas sûr qu'elle se terminerait de manière élégante - je vous laisse cette tâche. Ceci illustre simplement une façon de commencer. Dans le formulaire principal, vous feriez quelque chose comme :
Public Class Form1
Private pgClass As New SecondUIClass
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
For i = 1 To 10
System.Threading.Thread.Sleep(1000)
pgClass.StepTheBar()
Next
End Sub
End Class
L'exécution de l'application ci-dessus créerait Form1
ainsi qu'un second formulaire créé par pgClass
. En cliquant sur Button1
sur Form1
bloquerait le formulaire 1 pendant qu'il effectue sa boucle, mais le second formulaire resterait toujours vivant et réactif, mettant à jour sa barre de progression chaque fois que Form1
appelé .StepTheBar()
.
La meilleure solution dans ce cas est de demander au "client" d'apprendre à programmer correctement et d'éviter de se retrouver dans cette situation. Dans le cas où cela est totalement impossible et que vous DEVEZ créer pour lui un composant qui restera vivant malgré son mauvais code, l'approche ci-dessus est probablement votre seul recours.