Moved Notice
I've moved this blog to http://community.hydrussoftware.com/blogs/john, so please go there for the latest and greatest content.
Primarily a place for me to record all of the technical gyrations that I have to go through in order to develop software, so that I don't forget them and others can benefit from them. Most of the content will focus on .NET and SQL Server development, but anything else that I find myself dealing with could find its way here as well.
I've been banging my head for about a week on the problem of determining the last page number of a Crystal Report viewed through the web ReportViewer class. You would think that the report viewer class, which has methods like ShowLastPage and ShowNextPage would have properties like CurrentPageNumber and LastPageNumber, but alas, you would be wrong. So I had to roll my own implementation of these, which I think you may benefit from.
Here is the basic outline of the process that I followed to keep track of the current and last page numbers of the report:
If Not Me.IsPostBack Then
' Store the last page number in viewstate.
Me.LastPageNumber = e.NewPageNumber
' Mark the event as handled, to prevent actual navigation.
e.Handled = True
Else
' Store the index of the page now being displayed.
Me.CurrentPageNumber = e.NewPageNumber
End If
That's the most efficient solution that I could think of to find these two values, assuming I need both of them through the entire lifecycle of the page. At some point, I'd like to create a class that extends CrystalReportViewer that includes these properties, but for right now, I'll settle for just keeping them on the page I'm working with.
UPDATE: 6/20/06
I can't really find an elegant solution that tells me when I am viewing the last page of a report. So after much hair-pulling, I opted for a dirty trick. An instance of the CrystalReportViewer class *knows* whether or not it is on the last page after its PreRender event fires. It doesn't expose this information, but that doesn't mean you can't get at it. Now I'll admit, this goes against every principle of OOP that I know, but sometimes, you gotta do what you gotta do.
You can call the QueryIsLastPageKnown method after the PreRender event of the report viewer fires. If you need to, you can write an event handler for the PreRender event of a control that follows the report viewer in your page. This will tell you whether or not your report viewer is displaying the last page in the report, while you still have time (just barely) to set values on controls that are based on this information.
(Copy and paste this code to your editor to view it w/o line breaks.)
Private Shared _reportAgentFieldInfo As System.Reflection.FieldInfo
Private Shared _isLastPageNumberKnownFieldInfo As System.Reflection.FieldInfo
Private Shared ReadOnly Property ReportAgentFieldInfo() As System.Reflection.FieldInfo
Get
If _reportAgentFieldInfo Is Nothing Then
Dim reportViewerType As Type = GetType(CrystalDecisions.Web.CrystalReportViewer)
Dim reportAgentType As Type = GetType(CrystalDecisions.Web.ReportAgent)
For Each fieldInfo As Reflection.FieldInfo In reportViewerType.GetFields(Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
If fieldInfo.FieldType.Equals(reportAgentType) Then
_reportAgentFieldInfo = fieldInfo
End If
Next fieldInfo
End If
Return _reportAgentFieldInfo
End Get
End Property
Private Shared ReadOnly Property IsLastPageNumberKnownFieldInfo() As System.Reflection.FieldInfo
Get
If _isLastPageNumberKnownFieldInfo Is Nothing Then
Dim reportAgentType As Type = GetType(CrystalDecisions.Web.ReportAgent)
_isLastPageNumberKnownFieldInfo = reportAgentType.GetField("m_bIsLastPageNumberKnown", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
End If
Return _isLastPageNumberKnownFieldInfo
End Get
End Property
Private Shared Function QueryIsLastPageKnown(ByVal reportViewer As CrystalDecisions.Web.CrystalReportViewer) As Boolean
'
' HACK: This entire implementation is a hack, violating encapsulation of the CrystalDecisions.Web assembly.
'
' The CrystalDecisions.Web assembly knows whether or not the last page of a report is known, but does not reveal that information
' in any way that is accessable to other code. Therefore, the information is extracted via reflection of non-public members.
'
' If the CrystalDecisions.Web assembly is changed, this method should not cause the application to break.
' Rather, the current and last page numbers of a report will simply be displayed incorrectly.
'
If Not ReportAgentFieldInfo Is Nothing Then
Dim reportAgent As CrystalDecisions.Web.ReportAgent
reportAgent = CType(ReportAgentFieldInfo.GetValue(reportViewer), CrystalDecisions.Web.ReportAgent)
If (Not reportAgent Is Nothing) _
AndAlso (Not IsLastPageNumberKnownFieldInfo Is Nothing) Then
Return CBool(IsLastPageNumberKnownFieldInfo.GetValue(reportAgent))
End If
End If
Return False
End Function