Finding the Current and Last Page Numbers of a Crystal Report in ASP.NET 1.1
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:
- Create properties on the Web Form to store the values in viewstate. (I called mine CurrentPageNumber and LastPageNumber, and I gave CurrentPageNumber the default value of 1.)
- Set the ReportSource property of the CrystalReportViewer in the viewer's Init event. (You'll need to do this whether you use my method or not.)
- During the page's initial load (Not IsPostBack), call ShowLastPage on the report viewer.
- Create an event handler for the Navigate event of the report viewer. In that event handler, execute the following code:
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


1 comments:
Hmm . . . this method works if you *really* want to know the last page number, but it does remove some of the efficency that Crystal gives you. Normally, Crystal Reports doesn't care to think about the last page until it's specifically requested. Since this code forces it to consider the last page, that consideration takes time, (a significant amount of time for a very large report.) I'm looking into alternatives, and if I come up with one, I'll post it here.
Post a Comment