2008-11-25 6 views
4

Je recherche une méthode générique pour implémenter un écran d'attente lors d'opérations longues. J'ai utilisé le threading quelques fois auparavant, mais j'ai le sentiment que je l'ai implémenté soit très mal, soit avec beaucoup trop de tracas (et copier/coller - l'horreur!). Je veux garder cela aussi générique et simple que possible, donc je n'aurai pas à mettre en œuvre des charges de BackgroundWorker s manipulant toutes sortes de merdes, ce qui rend les choses difficiles à maintenir.Ecran de chargement fileté (en attente)

Voici ce que je voudrais faire - s'il vous plaît noter que ce pourrait différer de ce qui est en fait possible/meilleures pratiques/whatever - en utilisant VB.NET, Framework 2.0 (donc pas de méthodes anonymes):

Private Sub HandleBtnClick(sender as Object, e as EventArgs) Handles Button.Click 
     LoadingScreen.Show() 

     'Do stuff here, this takes a while!' 
     Dim Result as Object = DoSomethingTakingALongTime(SomeControl.SelectedObject) 

     LoadingScreen.Hide() 

     ProcessResults(Result) 
    End Sub 

L'application est maintenant complètement mono-thread, donc tout fonctionne sur le thread graphique. Je dois être en mesure d'accéder aux objets dans DoSomethingTakingALongTime() sans avoir d'exceptions de thread-thread. Le thread graphique attend une méthode (qui prend beaucoup de temps) pour terminer, tandis que le formulaire LoadingScreen doit rester réactif (il est animé/a une barre de progression/etc.).

Est-ce une approche réalisable/bonne ou est-ce que je vois cela de manière trop simpliste? Quelle est la meilleure pratique en la matière? Et le plus important: comment pourrais-je mettre en place un tel système? Comme je l'ai déjà mentionné, j'ai très peu d'expérience avec le filetage, alors soyez gentil s'il vous plaît :-)

Répondre

4

Votre problème est que vous obtenez une exception de thread croisé lorsque vous essayez de transmettre vos données de thread de travail à votre thread ui. ce que vous devez faire est de vérifier InvokeRequired et BeginInvoke avant de régler les commandes de votre interface afin de ne pas l'erreur comme ceci:

Private Sub work_CrossThreadEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles work.CrossThreadEvent 

     If Me.InvokeRequired Then 
      Me.BeginInvoke(New EventHandler(AddressOf work_CrossThreadEvent), New Object() {sender, e}) 
      Return 
     End If 

     Me.Text = "Cross Thread" 

End Sub 

changer seulement la partie New EventHandler au gestionnaire d'événements à l'aide de votre.

Aussi je pense que l'aide d'un travailleur de fond n'est pas une mauvaise méthode pour vos classes de travailleurs, il suffit de créer une classe pour votre travail et utilisez le travailleur de fond pour faire les choses de filetage un peu comme ceci:

Public MustInherit Class Worker 

    Protected WithEvents worker As BackgroundWorker 

    Public Sub New() 

     worker = New BackgroundWorker() 
     worker.WorkerReportsProgress = True 
     worker.WorkerSupportsCancellation = True 

    End Sub 

    Public Sub Start() 

     If (Not worker.IsBusy AndAlso Not worker.CancellationPending) Then 
      worker.RunWorkerAsync() 
     End If 

    End Sub 

    Public Sub Cancel() 
     If (worker.IsBusy AndAlso Not worker.CancellationPending) Then 
      worker.CancelAsync() 
     End If 
    End Sub 

    Protected MustOverride Sub Work() 

    Private Sub OnDoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork 
     Work() 
    End Sub 

    Public Event WorkCompelted As RunWorkerCompletedEventHandler 
    Private Sub OnRunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted 
     OnRunWorkerCompleted(e) 
    End Sub 
    Protected Overridable Sub OnRunWorkerCompleted(ByVal e As RunWorkerCompletedEventArgs) 
     RaiseEvent WorkCompelted(Me, e) 
    End Sub 

    Public Event ProgressChanged As ProgressChangedEventHandler 
    Private Sub OnProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles worker.ProgressChanged 
     OnProgressChanged(e) 
    End Sub 
    Protected Overridable Sub OnProgressChanged(ByVal e As ProgressChangedEventArgs) 
     RaiseEvent ProgressChanged(Me, e) 
    End Sub 

End Class 

Public Class ActualWork 
    Inherits Worker 

    Public Event CrossThreadEvent As EventHandler 

    Protected Overrides Sub Work() 

     'do work here' 
     WorkABit() 
     worker.ReportProgress(25) 

     WorkABit() 
     worker.ReportProgress(50) 

     WorkABit() 
     worker.ReportProgress(75) 

     WorkABit() 
     worker.ReportProgress(100) 

    End Sub 

    Private Sub WorkABit() 

     If worker.CancellationPending Then Return 
     Thread.Sleep(1000) 
     RaiseEvent CrossThreadEvent(Me, EventArgs.Empty) 

    End Sub 

End Class 

avertissement .. peu rouillé avec vb mais vous devriez avoir l'idée.

+0

Merci! Je vais regarder dans cela et revenir à vous :-) –

+0

Très, très utile. Une bonne explication en anglais. Je vous remercie! –

0

Dans votre fil, utilisez Application.Run (yourform) pour obtenir ce que vous voulez.

Notez que vous devez indiquer au formulaire de se fermer d'une manière ou d'une autre.

+0

Quel fil? L'application est construite à un seul thread (= thread graphique), qui utilise déjà Application.Run pour afficher le formulaire principal. –

0

J'espère que vous ne trouvez pas cela inutile - mais je demanderais pourquoi vous voulez un écran d'attente fileté? La raison de l'utilisation de l'enfilage en premier lieu est que l'interface utilisateur reste réactive et que de longues opérations sont effectuées en arrière-plan. Dans le cas contraire, vous pourriez tout aussi bien avoir un ProgressBar sur votre contrôle FormLoading, et avoir DoSomethingTakingALongTime pour le mettre à jour périodiquement. Cela n'aurait pas besoin de threads du tout.

+0

.NET est très pointilleux sur quand et comment il met à jour l'interface graphique. Parfois, il fonctionne bien jusqu'à ce que vous cliquez quelque part, et il cesse complètement la mise à jour. Je sais que les longues opérations doivent être exécutées dans un thread à la place de l'interface graphique, mais je ne l'ai pas mentionné car je veux savoir si cette solution est faisable. –

+0

Je ne me soucie pas vraiment de la forme principale, seul l'écran de chargement devrait être réactif. Je ne me soucie pas non plus de la logique des threads qui est exécutée, mais je n'arrive pas à bien faire les choses, je cours toujours dans des exceptions cross-thread ... –

Questions connexes