Pergunta

Eu estou procurando um método genérico para implementar uma tela de espera durante as operações longas. Eu tenho usado enfiar algumas vezes antes, mas eu tenho a sensação de que eu implementei ele quer muito mal, ou com a maneira demasiado incómodo (e copiar / colar - o horror).

Eu quero manter isso como genérico e simples possível, por isso não vou ter que implementar cargas de BackgroundWorkers manipulação de todos os tipos de lixo, tornando as coisas difíceis de manter.

Aqui está o que eu gostaria de fazer - por favor, note que este pode diferir do que é realmente possível / melhores práticas / whatever - usando VB.NET, Framework 2.0 (métodos por isso não anônimos):

  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

A aplicação é agora completamente single-threaded, então tudo é executado no thread de GUI. Eu preciso ser capaz de acessar objetos em DoSomethingTakingALongTime() sem obter exceções cross-linha. O aguarda thread de GUI para algum método (que leva um longo tempo) para ser concluído, enquanto o Formulário LoadingScreen deve ficar sensível (é animado / tem um progressbar / etc.).

Esta é a / boa abordagem factível ou estou vendo desta forma demasiado simplista? Qual é a melhor prática sobre esta matéria? E o mais importante: como eu poderia implementar tal sistema um? Como já mencionei, eu tenho muito pouca experiência com threading, assim que seja gentil, por favor: -)

Foi útil?

Solução

O problema é que seu recebendo uma exceção fio cruz quando seu tentando passar seus dados segmento de trabalho para o seu segmento. o que você precisa fazer é verificar InvokeRequired e BeginInvoke antes de definir os controles em seu ui para que você não obter o erro assim:

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

apenas mudar a parte New EventHandler para o evento manipulador de sua utilização.

Também acho que usando um trabalho de fundo não é um método ruim para suas classes operárias, basta criar uma classe para o seu trabalho e usar o trabalho de fundo para fazer as coisas enfiar um pouco como este:

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

aviso .. pouco enferrujado com vb, mas você deve ficar com a ideia.

Outras dicas

Em seu segmento, o uso Application.Run (yourform) para obter o que deseja.

Note que você precisa para sinalizar o formulário para fechar em si de alguma forma.

Eu espero que você não encontrar este inútil - mas eu questionar por que você iria querer uma tela de espera de rosca? A razão para usar segmentação em primeiro lugar é para que a interface do usuário continua a responder, e as operações longas são feitas em segundo plano.

Caso contrário, você pode muito bem ter um ProgressBar no seu controle FormLoading, e têm DoSomethingTakingALongTime para atualizá-lo periodicamente. Este não seria necessário tópicos em tudo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top