Domanda

Sto cercando un metodo generico per implementare una schermata di attesa durante le lunghe operazioni. Ho usato il threading alcune volte prima, ma ho la sensazione di averlo implementato molto male o con troppa seccatura (e copia / incolla - l'orrore!).

Voglio mantenerlo il più generico e semplice possibile, quindi non dovrò implementare un sacco di BackgroundWorker che gestiscono tutti i tipi di schifezze, rendendo le cose difficili da mantenere.

Ecco cosa vorrei fare - tieni presente che potrebbe differire da ciò che è effettivamente possibile / best practice / qualunque cosa - utilizzando VB.NET, Framework 2.0 (quindi nessun metodo anonimo):

  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'applicazione ora è completamente a thread singolo, quindi tutto viene eseguito sul thread della GUI. Devo poter accedere agli oggetti in DoSomethingTakingALongTime () senza ottenere eccezioni cross-thread. Il thread della GUI attende il completamento di un metodo (che richiede molto tempo), mentre il modulo LoadingScreen dovrebbe rimanere reattivo (è animato / ha una barra di avanzamento / ecc.).

È un approccio fattibile / buono o sto vedendo in questo modo troppo semplicistico? Qual è la migliore pratica in materia? E, soprattutto: come potrei implementare un tale sistema? Come ho già detto, ho pochissima esperienza con il threading, quindi sii gentile per favore :-)

È stato utile?

Soluzione

Il tuo problema è che stai ricevendo un'eccezione cross thread quando provi a passare i dati del thread di Worker al tuo thread dell'interfaccia utente. quello che devi fare è controllare InvokeRequired e iniziare a invocare prima di impostare i controlli sull'interfaccia utente in modo da non ricevere l'errore in questo modo:

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

basta cambiare la parte New EventHandler nel gestore eventi che stai usando.

Inoltre penso che l'utilizzo di un lavoratore in background non sia un cattivo metodo per le tue classi di lavoratori, basta creare una classe per il tuo lavoro e utilizzare il lavoratore in background per fare le cose di thread un po 'come questo:

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 po 'arrugginito con vb ma dovresti avere l'idea.

Altri suggerimenti

Nel tuo thread, utilizza Application.Run (yourform) per ottenere ciò che desideri.

Nota che devi segnalare al modulo di chiudersi in qualche modo.

Spero che non lo trovi utile, ma mi chiedo PERCHÉ vorresti una schermata di attesa threaded? Il motivo dell'utilizzo del threading è in primo luogo perché l'interfaccia utente rimane reattiva e le operazioni lunghe vengono eseguite in background.

Altrimenti, potresti anche avere una ProgressBar sul tuo controllo FormLoading e avere DoSomethingTakingALongTime per aggiornarlo periodicamente. Questo non avrebbe bisogno di discussioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top