Question

Je cherche une méthode générique pour implémenter un écran d'attente lors d'opérations longues. J'ai déjà utilisé le threading plusieurs fois auparavant, mais j'ai le sentiment de le mettre en œuvre de manière très médiocre ou avec trop de tracas (et de copier / coller - l'horreur!).

Je veux que cela reste aussi générique et simple que possible, de sorte que je n'aurai pas à implémenter des charges de BackgroundWorker traitant toutes sortes de conneries, ce qui rend les choses difficiles à maintenir.

Voici ce que je voudrais faire - veuillez noter que cela peut différer de ce qui est réellement possible / meilleure pratique / peu importe - avec VB.NET, Framework 2.0 (sans 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ée, donc tout s'exécute sur le thread d'interface graphique. Je dois pouvoir accéder aux objets dans DoSomethingTakingALongTime () sans obtenir d'exceptions transversales. Le thread de l'interface graphique attend qu'une méthode (qui prend beaucoup de temps) soit terminée, tandis que le formulaire LoadingScreen doit rester réactif (il est animé / a une barre de progression / etc.).

Est-ce une approche faisable / bonne ou est-ce que je vois cela de façon trop simpliste? Quelle est la meilleure pratique à ce sujet? Et surtout: 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, soyez donc doux s'il vous plaît: -)

Était-ce utile?

La solution

Votre problème est que vous obtenez une exception de thread croisé lorsque vous essayez de transmettre vos données de thread Worker à votre thread ui. Ce que vous devez faire est de vérifier InvokeRequired et begininvoke avant de régler les contrôles sur votre interface utilisateur afin que vous n'obteniez pas l'erreur comme suit:

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

remplacez simplement la partie New EventHandler par le gestionnaire d'événements que vous utilisez.

De plus, je pense que l’utilisation d’un travailleur en arrière-plan n’est pas une mauvaise méthode pour vos classes créez simplement une classe pour votre travail et utilisez l’agent d’arrière-plan pour effectuer les tâches de threading 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

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

Autres conseils

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

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

J'espère que vous ne trouvez pas cela inutile - mais j'aimerais savoir POURQUOI vous voulez un écran d'attente fileté? La première raison d'utiliser le threading est que l'interface utilisateur reste sensible et que les opérations longues soient effectuées en arrière-plan.

Sinon, vous pouvez également disposer d'une barre de progression sur votre contrôle FormLoading et laisser DoSomethingTakingALongTime le mettre à jour périodiquement. Cela n'aurait pas besoin de threads du tout.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top