跑马灯进度条在使用BackgroundWorker时无响应

4

在我的代码中,当点击按钮时,进度条被设置为跑马灯,然后调用BackgroundWorker,但是当调用BackgroundWorker时,进度条会冻结或消失。我使用BackgroundWorker将ReportViewer的RefreshReport方法与UI线程分离。感谢任何帮助。

    Private Sub btnOtherReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOtherReport.Click
        rvReport.ProcessingMode = ProcessingMode.Remote
        rvReport.ShowParameterPrompts = False
        rvReport.ServerReport.ReportServerUrl = New Uri("REPORT_SERVER_URL")
        rvReport.ServerReport.ReportPath = "REPORT_PATH"
        rvReport.BackColor = Color.White

        BackgroundWorker1.RunWorkerAsync()
    End Sub


    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        RefreshReport()
    End Sub


    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        pbReports.Style = ProgressBarStyle.Blocks
        pbReports.Value = 100
    End Sub


    Public Sub RefreshReport()
        rvReport.BeginInvoke(New MethodInvoker(AddressOf rvReport.RefreshReport))
    End Sub
2个回答

3
问题出在你在RefreshReport()方法中调用.BeginInvoke()BackgroundWorker.DoWork()方法已经在另一个线程中被触发,所以你只需要调用rvReport.RefreshReport()即可。代码应该如下所示:
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    rvReport.RefreshReport()
End Sub

这很简单,可能需要使用 Monitor 来锁定报告对象并防止重入。
当您调用.BeginInvoke()时,报告过程就开始了,但它根本不会阻塞,因此DoWork()方法没有剩下任何任务可做,它立即返回。此时,BackgroundWorker认为它已完成工作,因此调用.RunWorkerCompleted()方法,这会停止进度条。
基于评论,rvReport是一个可视化控件,而不是组件或简单的数据访问类。在这种情况下,您应该知道,.Net中的可视化控件不是线程安全的,因此绝不能直接执行需要超过几分钟的任务。 RefreshReport()方法中的.BeginInvoke()所经历的困难实际上是在主UI线程中调用长时间运行的函数。
要解决此问题,您需要关闭跨线程检查,以使异常不被抛出(简单但不推荐),或更改控件的使用方式,使主要工作发生在其他地方,并在准备好时控件仅引发事件。如果您无法将控件修改到这个程度,则控件存在设计缺陷。

当我从DoWork()方法调用rvReport.RefreshReport()时,会出现异常:跨线程操作无效:控件“winRSviewer”是在其创建的线程之外的线程上访问的。 - Bryan Roth
我想问题出在当我调用ReportViewer的RefreshReport()方法时,生成报表需要很长时间并使表单无响应。我已经进行了测试,发现与报表无关,实际上是ReportViewer的问题。 - Bryan Roth

-1
问题在于进度条没有机会重绘,因为后台线程占用了处理器。所以,在处理过程中,您需要从主等待线程调用System.Windows.Forms.Application.DoEvents()。这将获取控制并导致进度条重新绘制。
因此,在BackgroundWorker1.RunWorkerAsync调用之后,您可以添加一个循环来调用DoEvents,直到引发事件,然后在RunWorkerCompleted中引发事件,以让调用代码退出。由于这不允许主代码继续运行,您可以将调用放在后台调用或另一个线程中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接