ecrã (espera) carregamento roscado
-
11-07-2019 - |
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 BackgroundWorker
s 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: -)
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.