2013-03-13 2 views
0

J'ai créé un formulaire de fenêtre simple (collé ci-dessous). Maintenant, je l'utilise pour afficher un dailog d'attente à l'utilisateur lors de l'exécution d'une opération complexe qui consomme du temps.Le formulaire Windows ne se ferme pas lorsqu'il est ouvert à partir d'un autre thread

L'appel du HerculesWaitForm.Show("Some Title","Some Caption...) affiche le formulaire comme d'habitude mais lorsque vous appelez HerculesWaitForm.Close affiche toujours le formulaire. Que puis-je faire pour surmonter le problème?

Imports System.Threading 
Public Class HerculesWaitForm 
    Inherits DevExpress.Utils.WaitDialogForm 
    Private Shared form As HerculesWaitForm 
    Private Shared _thread As Thread 
    Private Shared _caption As String, _title As String 
    Private Sub New(ByVal caption As String, ByVal title As String) 
     MyBase.New(caption, title) 
    End Sub 
    Private Shared Sub CreateInstance() 
     form = New HerculesWaitForm(_caption, _title) 
     Application.Run(form) 
     Application.DoEvents() 
    End Sub 
    Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String) 
     _caption = caption 
     _title = title 
     If form Is Nothing Then 
      _thread = New Thread(AddressOf CreateInstance) 
      _thread.IsBackground = True 
      _thread.Start() 
     End If 
    End Sub 
    Public Overloads Shared Sub SetCaption(ByVal caption As String) 
     If form IsNot Nothing Then 
      _caption = caption 
      form.SetFormCaption() 
     Else 
      Show(caption, "") 
     End If 
    End Sub 
    Public Overloads Shared Sub Close() 
     If form IsNot Nothing Then 
      form.CloseForm() 
      form = Nothing 
     End If 
    End Sub 
    Private Sub CloseForm() 
     If Me.InvokeRequired Then 
      Invoke(New MethodInvoker(AddressOf CloseForm)) 
      Return 
     End If 
     Application.ExitThread() 
    End Sub 
    Private Sub SetFormCaption() 
     If Me.InvokeRequired Then 
      Invoke(New MethodInvoker(AddressOf SetFormCaption)) 
      Return 
     End If 
     MyBase.SetCaption(_caption) 
    End Sub 
End Class 
+0

Avez-vous des exceptions à ce sujet? Consultez cette question, de toute façon: http://stackoverflow.com/questions/947476/calling-a-windows-form-from-another-thread-net –

+0

Si l'opération se termine très vite, le problème persiste mais si le prend un certain temps puis il fonctionne comme prévu. –

+0

Bunch de code, n'a pas réellement un appel Form.Close(). HerculesWaitForm est un * nom de type *, n'utilisez jamais HerculesWaitForm.Close(). Cela utilise le mauvais objet lorsque vous faites cela dans un fil. –

Répondre

2

Si vous appelez cela comme:

Private Sub doSomethingLong() 
    HerculesWaitForm.Show("hi", "there") 
    Sleep(someAmount) 'Random long operation ' 
    HerculesWaitForm.Close() 
End Sub 

alors vous pouvez avoir des problèmes, comme vous le notez, si le temps entre les appels ouverture et de fermeture est court. C'est parce que l'appel à Show() démarre un deuxième thread travaillant à la création du nouvel objet hercules tandis que vous effectuez votre opération. La longue opération commence immédiatement et en même temps que le fil commence sur la tâche de se mettre en marche.

Si l'appel à Close() vient avant que le thread a terminé l'initialisation lui-même et en complétant l'instanciation de l'objet form l'appel à Close() constatera que form = Nothing et il retournera tout simplement sans faire quoi que ce soit.

Changer, puis, le code Show() comme ceci:

Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String) 
    _caption = caption 
    _title = title 
    If form Is Nothing Then 
     _thread = New Thread(AddressOf CreateInstance) 
     _thread.SetApartmentState(ApartmentState.STA) 'should do this ' 
     _thread.IsBackground = True 
     _thread.Start() 
    End If 
    While form Is Nothing ' add ' 
     Thread.Sleep(1)  ' this ' 
    End While    ' here ' 
End Sub 

forcera la méthode Show() pour bloquer jusqu'à ce que le thread de travail a créé l'objet form - ce qui garantit que tous les appels futurs à Close ne seront pas simplement sauté (parce que form is nothing). Pas joli, mais sans refactoriser toute cette classe, c'est probablement la solution la plus rapide.

Il s'agit en fait d'une très mauvaise façon de gérer une longue opération. Plutôt que de placer le travail dans un thread de travail, vous créez un nouveau thread d'interface utilisateur pour exécuter une animation stupide tout en bloquant le thread d'interface utilisateur réel pour effectuer une opération fastidieuse. En plus d'être une pratique de conception terrible, cela a également pour effet que votre forme de widget DevExpress planera au-dessus de toutes les autres applications (puisque c'est une sorte d'application quand elle est appelée), refusant à vos utilisateurs la possibilité d'effectuer plusieurs tâches simultanément. d'autres applications parce que votre application vole à l'avant et au centre pour montrer qu'elle travaille sur quelque chose.

Vous devriez vraiment utiliser le ThreadPool ou un BackgroundWorker ou un autre type de thread de travail pour effectuer l'opération complexe à la place. Cela laisse votre thread principal de l'interface utilisateur libre de fournir des mises à jour d'état sur la longue opération et évite ce désordre de thread double-interface utilisateur ci-dessus.

+0

Oui, il y a fondamentalement une règle stricte dans le développement d'applications Windows selon laquelle ** vous n'avez jamais plus d'un thread d'interface utilisateur **. Spin off un thread de travail pour faire le travail et garder toute l'interface utilisateur sur le même fil. Et repentez-vous du mal qui est "Thread.Sleep". –

+0

@CodyGray - mais vous pouvez avoir plus d'un thread d'interface utilisateur (mutuellement indépendant et exclusif) si vous venez de faire un autre (second appel à 'Application.Run()', comme cela a été fait ici). Cela fait juste un énorme gâchis et ne devrait vraiment être qu'une solution de dernier recours. Le seul endroit où je peux le voir valide est une solution temporaire à un grand nombre de codes hérités mal écrits qui ne peuvent pas être rapidement refactorisés ... une sorte de «make-do» jusqu'à ce que vous puissiez contourner les problèmes structurels du application. –

+0

@CodyGray - plutôt de dire, peut-être, que la règle, quoique pas dure et rapide, est que vous ** devriez ** avoir seulement un fil de l'interface utilisateur. Si vous devez en faire un autre, vous faites probablement quelque chose de mal! –

2

Vous devez appeler _thread.SetApartmentState (ApartmentState.STA) avant de démarrer le thread.

Questions connexes