CrystalReportViewer.RefreshReport 在来自 BackgroundWorker 的 运行 时挂起
CrystalReportViewer.RefreshReport hangs when running from BackgroundWorker
我正在尝试通过在 Crystal 报告 prepared/loaded 期间添加加载屏幕来“增强”我的报告代码。在我开始尝试添加加载屏幕之前,我的所有报告都会正常显示,但光标的变化还不足以表明应用程序仍在拉取报告——其中一些可能需要一段时间 - 所以我想提供一个更“明显”的视觉提示。
为了实现这一点,我已将报告创建方法调用放入存在于加载屏幕本身的 BackgroundWorker
中(我还没有抽出时间学习如何使用 Async/Await
足够好,但可以放心使用 )。加载屏幕正确出现并且一切 看起来 都按预期工作,直到它实际尝试在屏幕上显示报告。此时,“正在处理文档,请稍候。”框出现(在 CrystalReportViewer
控件中,用于显示报告), 但它只是坐在那里,甚至没有旋转。最终,我的 IDE 抛出了关于接收 ContextSwitchDeadlock
的错误,我几乎只需要取消执行。
这是我的dlgReportLoading
“启动画面”,带有一个包含动画 GIF 的 PictureBox
控件:
Imports System.Windows.Forms
Public Class dlgReportLoading
Private DisplayReport As Common.CRReport
Private WithEvents LoadReportWorker As System.ComponentModel.BackgroundWorker
Public Sub New(ByRef Report As Common.CRReport)
InitializeComponent()
DisplayReport = Report
End Sub
Private Sub dlgReportLoading_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Cursor = Cursors.WaitCursor
Me.TopMost = True
Me.TopMost = False
LoadReportWorker = New System.ComponentModel.BackgroundWorker
LoadReportWorker.RunWorkerAsync()
End Sub
Private Sub dlgReportLoading_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
Me.Cursor = Cursors.Default
End Sub
Private Sub LoadReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles LoadReportWorker.DoWork
If Not DisplayReport.ReportOption = Common.CRReport.GenerateReportOption.None Then
Select Case DisplayReport.ReportOption
Case Common.CRReport.GenerateReportOption.DisplayOnScreen
'-- This is the method I'm currently testing
DisplayReport.ShowReport()
Case Common.CRReport.GenerateReportOption.SendToPrinter
DisplayReport.PrintReport()
Case Common.CRReport.GenerateReportOption.ExportToFile
DisplayReport.ExportReport()
End Select
End If
DisplayReport.ReportOption = Common.CRReport.GenerateReportOption.None
'--
'-- This code was in use before trying to generate the reports in the background
'If Not DisplayReport.CrystalReport Is Nothing Then
' DisplayReport.CrystalReport.Dispose()
'End If
'--
End Sub
Private Sub LoadReport_Complete(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LoadReportWorker.RunWorkerCompleted
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
End Class
如上面代码所述,我目前正在测试此处定义的 ShowReport()
方法:
Protected Friend Sub ShowReport()
Dim ReportViewer As frmReportPreview
Me.PrepareReport()
ReportViewer = New frmReportPreview(Me)
With ReportViewer
.WindowState = FormWindowState.Maximized
.Show()
End With
End Sub
而 frmReportPreview
是这样的:
Imports System.ComponentModel
Public Class frmReportPreview
Private DisplayReport As Common.CRReport
Private ReportToDisplay As CrystalDecisions.CrystalReports.Engine.ReportDocument
Public Sub New(ByRef Report As Common.CRReport)
InitializeComponent()
DisplayReport = Report
PrepareReportForDisplay()
Me.rptViewer.ReportSource = Nothing
Me.rptViewer.ReportSource = ReportToDisplay
' SET ZOOM LEVEL FOR DISPLAY:
' 1 = Page Width
' 2 = Whole Page
' 25-100 = zoom %
Me.rptViewer.Zoom(1)
Me.rptViewer.Show()
End Sub
Private Sub frmReportPreview_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'-- HANGS HERE
Me.rptViewer.RefreshReport()
End Sub
Private Sub frmReportPreview_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
ReportToDisplay.Dispose()
Me.rptViewer.ReportSource = Nothing
End Sub
'...CODE FOR PREPARING THE REPORT TO BE DISPLAYED
End Class
dlgReportLoading
表单正确弹出,动画播放直到 frmReportPreview
弹出在它前面(它不会关闭)。通常带有动画旋转圆圈的小框表示正在加载报告数据,但几乎立即冻结在原地。
在调用 ShowReport()
方法后,我的 dlgReportLoading
表单的 LoadReport_DoWork()
方法中有一个断点,但它永远不会到达那个点。我在那种形式的 LoadReport_Complete()
方法中也有一个,它永远不会命中任何一个,并且该对话框永远不会真正关闭。
我在 frmReportPreview_Shown
方法的末尾放置了另一个断点,就在 Me.rptViewer.RefreshReport()
调用之后,但它也没有命中,所以很明显这是卡住的地方, 但 仅当通过 BackgroundWorker
生成报告时 。如果我只是调用 ShowReport()
方法而不通过“启动画面”和 BackgroundWorker
发送它,一切都会正常生成和显示。
我已经尝试将 RefreshReport()
方法放入其 自己的 BackgroundWorker
中,但行为没有任何变化。我已经尝试使用 ShowDialog()
而不是仅 Show()
使 frmReportPreview
对象模态显示。 None 这似乎有助于解决问题。
我感觉有些东西在某个地方被过早地处理掉了,但我不知道那会是什么。如果需要,我可以提供来自 frmReportPreview
的其余报告准备代码,但据我所知,所有 似乎 都可以正常工作。我不反对尝试其他方法来实现在所有报告准备过程中向用户显示加载屏幕的目标 - 例如,Async/Await
或其他多线程方法 - 所以 欢迎任何的建议。如果需要任何其他说明,请告诉我。
环境
微软 Windows 10 Pro 21H1(OS 内部版本 19043.1348)
微软 Visual Studio 社区 2017 (v15.9.38)
Crystal .NET Framework v13.0.3500.0(运行时版本 2.0.50727)的报告
编辑: 我忘了提到整个混乱是从我的 CRReport
class 中的 GenerateReport()
方法调用的,定义为:
Public Sub GenerateReport(ByVal ReportGeneration As GenerateReportOption)
Me.ReportOption = ReportGeneration
If Me.ReportOption = GenerateReportOption.None Then
'...CODE FOR REQUESTING A GENERATION OPTION FROM THE USER
End If
Dim ReportLoadingScreen As New dlgReportLoading(Me)
ReportLoadingScreen.ShowDialog()
End Sub
反过来,从我的主窗体中调用它是这样的:
Private Sub PrintMyXMLReport(ByVal XMLFile As IO.FileInfo)
Dim MyXMLReport As New IO.FileInfo("\SERVER\Applications\Reports\MyXMLReport.rpt")
Dim Report As New Common.CRReport(MyXMLReport, XMLFile)
Report.GenerateReport(Common.CRReport.GenerateReportOption.DisplayOnScreen)
End Sub
您应该将繁重的工作和 UI 操作分离到不同的方法中,以便将它们放入适当的 BackgroundWorker 事件中:
Protected Friend Sub PrepareReport()
' perform long-running background work
End Sub
Protected Friend Sub ShowReport()
Dim ReportViewer = New frmReportPreview(Me) With {.WindowState = FormWindowState.Maximized}
ReportViewer.Show()
End Sub
Private DisplayReport As Common.CRReport
Private Sub LoadReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles LoadReportWorker.DoWork
DisplayReport.PrepareReport()
End Sub
Private Sub LoadReport_Complete(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LoadReportWorker.RunWorkerCompleted
DisplayReport.ShowReport()
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
因为LoadReport_DoWork
实际上运行在一个新的非UI线程上,而LoadReport_Complete
运行在调用者线程上,它是一个UI线程。只有在那里您才能与 UI 交互并显示表单等
我正在尝试通过在 Crystal 报告 prepared/loaded 期间添加加载屏幕来“增强”我的报告代码。在我开始尝试添加加载屏幕之前,我的所有报告都会正常显示,但光标的变化还不足以表明应用程序仍在拉取报告——其中一些可能需要一段时间 - 所以我想提供一个更“明显”的视觉提示。
为了实现这一点,我已将报告创建方法调用放入存在于加载屏幕本身的 BackgroundWorker
中(我还没有抽出时间学习如何使用 Async/Await
足够好,但可以放心使用 )。加载屏幕正确出现并且一切 看起来 都按预期工作,直到它实际尝试在屏幕上显示报告。此时,“正在处理文档,请稍候。”框出现(在 CrystalReportViewer
控件中,用于显示报告), 但它只是坐在那里,甚至没有旋转。最终,我的 IDE 抛出了关于接收 ContextSwitchDeadlock
的错误,我几乎只需要取消执行。
这是我的dlgReportLoading
“启动画面”,带有一个包含动画 GIF 的 PictureBox
控件:
Imports System.Windows.Forms
Public Class dlgReportLoading
Private DisplayReport As Common.CRReport
Private WithEvents LoadReportWorker As System.ComponentModel.BackgroundWorker
Public Sub New(ByRef Report As Common.CRReport)
InitializeComponent()
DisplayReport = Report
End Sub
Private Sub dlgReportLoading_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Cursor = Cursors.WaitCursor
Me.TopMost = True
Me.TopMost = False
LoadReportWorker = New System.ComponentModel.BackgroundWorker
LoadReportWorker.RunWorkerAsync()
End Sub
Private Sub dlgReportLoading_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
Me.Cursor = Cursors.Default
End Sub
Private Sub LoadReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles LoadReportWorker.DoWork
If Not DisplayReport.ReportOption = Common.CRReport.GenerateReportOption.None Then
Select Case DisplayReport.ReportOption
Case Common.CRReport.GenerateReportOption.DisplayOnScreen
'-- This is the method I'm currently testing
DisplayReport.ShowReport()
Case Common.CRReport.GenerateReportOption.SendToPrinter
DisplayReport.PrintReport()
Case Common.CRReport.GenerateReportOption.ExportToFile
DisplayReport.ExportReport()
End Select
End If
DisplayReport.ReportOption = Common.CRReport.GenerateReportOption.None
'--
'-- This code was in use before trying to generate the reports in the background
'If Not DisplayReport.CrystalReport Is Nothing Then
' DisplayReport.CrystalReport.Dispose()
'End If
'--
End Sub
Private Sub LoadReport_Complete(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LoadReportWorker.RunWorkerCompleted
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
End Class
如上面代码所述,我目前正在测试此处定义的 ShowReport()
方法:
Protected Friend Sub ShowReport()
Dim ReportViewer As frmReportPreview
Me.PrepareReport()
ReportViewer = New frmReportPreview(Me)
With ReportViewer
.WindowState = FormWindowState.Maximized
.Show()
End With
End Sub
而 frmReportPreview
是这样的:
Imports System.ComponentModel
Public Class frmReportPreview
Private DisplayReport As Common.CRReport
Private ReportToDisplay As CrystalDecisions.CrystalReports.Engine.ReportDocument
Public Sub New(ByRef Report As Common.CRReport)
InitializeComponent()
DisplayReport = Report
PrepareReportForDisplay()
Me.rptViewer.ReportSource = Nothing
Me.rptViewer.ReportSource = ReportToDisplay
' SET ZOOM LEVEL FOR DISPLAY:
' 1 = Page Width
' 2 = Whole Page
' 25-100 = zoom %
Me.rptViewer.Zoom(1)
Me.rptViewer.Show()
End Sub
Private Sub frmReportPreview_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'-- HANGS HERE
Me.rptViewer.RefreshReport()
End Sub
Private Sub frmReportPreview_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
ReportToDisplay.Dispose()
Me.rptViewer.ReportSource = Nothing
End Sub
'...CODE FOR PREPARING THE REPORT TO BE DISPLAYED
End Class
dlgReportLoading
表单正确弹出,动画播放直到 frmReportPreview
弹出在它前面(它不会关闭)。通常带有动画旋转圆圈的小框表示正在加载报告数据,但几乎立即冻结在原地。
在调用 ShowReport()
方法后,我的 dlgReportLoading
表单的 LoadReport_DoWork()
方法中有一个断点,但它永远不会到达那个点。我在那种形式的 LoadReport_Complete()
方法中也有一个,它永远不会命中任何一个,并且该对话框永远不会真正关闭。
我在 frmReportPreview_Shown
方法的末尾放置了另一个断点,就在 Me.rptViewer.RefreshReport()
调用之后,但它也没有命中,所以很明显这是卡住的地方, 但 仅当通过 BackgroundWorker
生成报告时 。如果我只是调用 ShowReport()
方法而不通过“启动画面”和 BackgroundWorker
发送它,一切都会正常生成和显示。
我已经尝试将 RefreshReport()
方法放入其 自己的 BackgroundWorker
中,但行为没有任何变化。我已经尝试使用 ShowDialog()
而不是仅 Show()
使 frmReportPreview
对象模态显示。 None 这似乎有助于解决问题。
我感觉有些东西在某个地方被过早地处理掉了,但我不知道那会是什么。如果需要,我可以提供来自 frmReportPreview
的其余报告准备代码,但据我所知,所有 似乎 都可以正常工作。我不反对尝试其他方法来实现在所有报告准备过程中向用户显示加载屏幕的目标 - 例如,Async/Await
或其他多线程方法 - 所以 欢迎任何的建议。如果需要任何其他说明,请告诉我。
环境
微软 Windows 10 Pro 21H1(OS 内部版本 19043.1348)
微软 Visual Studio 社区 2017 (v15.9.38)
Crystal .NET Framework v13.0.3500.0(运行时版本 2.0.50727)的报告
编辑: 我忘了提到整个混乱是从我的 CRReport
class 中的 GenerateReport()
方法调用的,定义为:
Public Sub GenerateReport(ByVal ReportGeneration As GenerateReportOption)
Me.ReportOption = ReportGeneration
If Me.ReportOption = GenerateReportOption.None Then
'...CODE FOR REQUESTING A GENERATION OPTION FROM THE USER
End If
Dim ReportLoadingScreen As New dlgReportLoading(Me)
ReportLoadingScreen.ShowDialog()
End Sub
反过来,从我的主窗体中调用它是这样的:
Private Sub PrintMyXMLReport(ByVal XMLFile As IO.FileInfo)
Dim MyXMLReport As New IO.FileInfo("\SERVER\Applications\Reports\MyXMLReport.rpt")
Dim Report As New Common.CRReport(MyXMLReport, XMLFile)
Report.GenerateReport(Common.CRReport.GenerateReportOption.DisplayOnScreen)
End Sub
您应该将繁重的工作和 UI 操作分离到不同的方法中,以便将它们放入适当的 BackgroundWorker 事件中:
Protected Friend Sub PrepareReport()
' perform long-running background work
End Sub
Protected Friend Sub ShowReport()
Dim ReportViewer = New frmReportPreview(Me) With {.WindowState = FormWindowState.Maximized}
ReportViewer.Show()
End Sub
Private DisplayReport As Common.CRReport
Private Sub LoadReport_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles LoadReportWorker.DoWork
DisplayReport.PrepareReport()
End Sub
Private Sub LoadReport_Complete(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LoadReportWorker.RunWorkerCompleted
DisplayReport.ShowReport()
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub
因为LoadReport_DoWork
实际上运行在一个新的非UI线程上,而LoadReport_Complete
运行在调用者线程上,它是一个UI线程。只有在那里您才能与 UI 交互并显示表单等