The CrystalReportViewer class contains all of the properties, methods, and events that relate to the viewer itself, its appearance, the methods that are used to make the viewer perform certain actions (like refresh or print a report) and events that can be used to determine when a particular event (such as drill-down or refresh) has occurred. To start learning how to work with the viewer, we are going to start with the basic properties and move on from there.
When previewing your report, you may notice that there is a standard set of icons and layout that appears on your viewer by default, but you can control most of the aspects of the viewer by setting a few simple properties for the Crystal Report Viewer in the Properties window, as shown opposite.
The area at the top of the viewer is the Toolbar, which can be shown or hidden as an entire object, or you can choose to only show certain icons. On the left-hand side is a Group Tree, generated by the grouping that you have inserted into your report. The properties that control these general properties are Boolean and are listed below:
Property
Description
DisplayBackgroundEdge For showing the off-set edge around your report when previewing
DisplayGroupTree For showing the group tree on the left-hand side of the viewer
DisplayToolbar For showing the entire toolbar at the top of the viewer All of these properties default to True and you cannot change the position of any of these elements - they are fixed in place on the viewer. You can, however, hide the default toolbar and create your own buttons for printing, page navigation, and other functions, and we''ll look at that a little later in the chapter.
For the icons within the toolbar, you can also set simple Boolean properties to show or hide a particular icon, as shown below:
ShowCloseButton
ShowExportButton
ShowGotoPageButton
ShowGroupTreeButton
ShowPageNavigateButtons
ShowRefreshButton
ShowTextSearchButton
ShowZoomButton
ShowPrintButton
So a typical use of these properties is where you want to give users a view-only preview, with no printing or exporting options and no option to refresh the report. Going back to our original report (ch3_worldsales) we could easily set a few properties before you set your ReportSource property to make this happen.
Double-click anywhere you your Form to open the code view and in the Form''s Load method, enter the following:
... End With myTableLogonInfo.ConnectionInfo = myConnectionInfo myTableLogonInfo.TableName = "customers" myTableLogonInfos.Add(myTableLogonInfo) CrystalReportViewer1.LogOnInfo = myTableLogonInfos CrystalReportViewer1.ReportSource = myReport CrystalReportViewer1.DisplayGroupTree = False CrystalReportViewer1.ShowExportButton = False CrystalReportViewer1.ShowRefreshButton = False CrystalReportViewer1.ShowPrintButton = False CrystalReportViewer1.ReportSource = New ch3_worldsales_northwind() End Sub
When the report is previewed, it will appear as shown:
Keep in mind that you can set these properties any time prior to the report preview. You could store individual user or security settings in your application data and then set the appropriate properties prior to viewing the report. This is just one example of where we can customize how a report is presented to the user - the next section on the methods available within the viewer, takes that discussion one step further.
When working with the Crystal Report viewer, we have a number of methods available to us, which will allow us to integrate specific viewer functions into our application. As we move through this section, keep in mind that these methods can be used to create your own "look and feel" for the report preview window, as shown below:
During the course of this section, we will actually be looking at the code behind the custom viewer shown here, so it is probably not a bad idea to start a new project within this chapter''s solution file. To create a new project from within Visual Studio select File | New | Project and from Visual Basic Applications, select Windows Applications and specify a name (in the sample code, we have called this project viewer_methods) and location for your project files. Remember to set this project as your startup project.
Once your sample project has been created, add the Crystal Report Viewer to the default Form that is created and copy or add the ch3_worldsales.rpt to your project. Set the ReportSource property to point to this report. We are now ready to get started.
The first thing we need to do to emulate the custom viewer shown earlier is to set the DisplayToolbar and DisplayGroupTree properties to False.
Next, we need to add additional buttons for some of the functions normally associated with the buttons shown on the Viewer Toolbar - the buttons for the next page, previous page, print, export, and so on, using the custom viewer shown above as a guide. We shall walk through each of these in turn below.
The tangible benefit of using the methods described below and your own Form design is that you have more flexibility in how the report appears when viewed and you can match the viewer''s user interface to your own application.
To print a report, there is a simple PrintReport method that will invoke a standard Windows printer dialog to select where you would like to print your report, how many copies, and other functions.
To add this code to your custom viewer, drag and drop a button onto your Form, and name it Print_Button. Change the Text property to Print. Double-click the Print button you have dropped onto your Form and enter the following code in its Click event:
CrystalReportViewer1.PrintReport
This will open a standard Windows print dialog that will allow you to print your report. If you need to access advanced print options (like printing two pages to separate paper trays) or if you want to control the print process manually, you will need to use the Report Engine to do so, which is covered in Chapter 8.
When a report is refreshed, it goes back to the database for the most current set of data available and runs again. Drag and drop a button onto the Form, and call it Refresh_Button. Change the text to Refresh. To refresh from the Crystal Report viewer, you can add the RefreshReport method to the Refresh button you have created on your custom viewer Form:
CrystalReportViewer1.RefreshReport
If your report takes a while to run, or if you are concerned about database traffic and load, you may want to consider removing this as an option from your viewer, or even changing the properties of the standard viewer so that the Refresh icon does not appear at all, using the syntax CrystalReportViewer1.ShowRefreshButton = False.
Crystal Reports.NET features a rich export functionality, which is partially exposed in the Crystal Report Viewer. From the viewer, we can call the ExportReport method to open a Save as... dialog and export your report into one of four formats:
Adobe Acrobat (PDF)
Microsoft Excel (XLS)
Microsoft Word (DOC)
Rich-Text Format (RTF)
Important
In compatibility testing, the export formats for Microsoft Word and Excel work well with Office 97+ and Rich Text Format can be used by just about any word-processing application (including Word, WordPad, WordPerfect, etc.). For Adobe Acrobat, a version 3.0 or above reader is recommended and the output is consistent across version 3.0 – 5.0.
So, let''s put this functionality into our custom viewer. Drag and drop a button onto the Form once more, this time calling it Export_Button and setting the text to Export. Once again, click on the button to open its code event. Insert the following:
CrystalReportViewer1.ExportReport()
When the ExportReport method is used, the dialog below will appear and allow you to select an export format from a drop-down list and select where the file is to be saved.
Once the file has been saved, a message box will appear, advising you that the export is complete. You can then use the associated application to open the exported file.
To start with, you probably will want to know what page you''re are on at some point. Luckily for us, the Crystal Report Viewer has a simple method called GetCurrentPageNumber that allows us to get the page number of the page we are currently viewing.
In the custom viewer we are working with, we are going to place a label on the Form (we''ll call it PageNo_Label) that contains the page number. Initially, we''ll set this to Page: 1 using the Text property, but after that, this can be set dynamically using:
PageNo_Label.Text = "Page: " & CrystalReportViewer1.GetCurrentPageNumber.ToString
This method should be called after moving through the report pages, so it should be placed after the code for each of the following buttons (and the Refresh button, of course - do this now).
So, we''ll create the following four buttons, and place them on our Form.
Button Name
Button Text Property Value
FirstPage_Button First Page
BackPage_Button Back
NextPage_Button Forward
LastPage_Button Last Page In order to navigate through the pages or our report, we have a number of methods that can be called without any additional parameters, as shown below:
ShowFirstPage
ShowLastPage
ShowNextPage
ShowPreviousPage
So to put code behind our navigation buttons on our custom Form, (in this case, the Forward button) we could use the ShowNextPage method.
CrystalReportViewer1.ShowNextPage() PageNo_Label.Text = "Page: " & CrystalReportViewer1.GetCurrentPageNumber.ToString
Compile and run this. You should be on page two of the report, and the label should inform you of this. Now populate the remaining buttons with the code, remembering to set the correct method for each button.
These methods do not return a result, so to determine what page you are currently on, we would have to use the GetCurrentPageNumber method immediately after calling the first method, which will return the page you are currently viewing. Unfortunately, we don''t have a way to get the total page count, unless you were to use ShowLastPage to go to the last page, use the GetCurrentPageNumber method, and then store the number of the last page in a variable somewhere in your code, but that is a lot of work for one little number.
For navigating to a specific page, ShowNthPage allows us to pass a specific page number to the method, as shown here emulating the functionality of the ShowNextPage method.
Dim CurrentPage CurrentPage = GetCurrentPageNumber CrystalReportViewer1.ShowNthPage(CurrentPage + 1) '' This will take you to the next page
In the custom viewer we are working with, draw a textbox onto the Form, naming it PageNo_TextBox. The point of this textbox is to allow the user to enter a page number and then click the Go To button to go to a specific page. Drag and drop a button on the Form next to the textbox, naming the button GoTo_Button and labelling it Go To.
Assuming that the textbox you have drawn on your Form is called PageNo_TextBox, the following code, placed behind the Go To button, checks to see if a page number has been entered. If something has been entered, the ShowNthPage method is then called to jump to a specific page.
If PageNo_TextBox.Text <> " " Then CrystalReportViewer1.ShowNthPage(PageNo_TextBox.Text) PageNo_Label.Text = "Page: " & CrystalReportViewer1.GetCurrentPageNumber.ToString PageNo_TextBox.Text = " " Else MsgBox("Please enter a page number to jump to", MsgBoxStyle.Exclamation, "Please enter a page number") PageNo_TextBox.Text = " " End If
Compile and run, and you should see that this functionality is now implemented.
In addition to page navigation, you also have the ability to choose the zoom factor that is applied to your report. By default, the zoom is set to 100% of the report size unless you specify otherwise. In the example below, we will add a combo box to the Form to allow the user to select a particular zoom factor for viewing.
The name of the combo box is ComboBox_Zoom. Assign the Text property with the value 100%, and click on the Items property. The String Collection Editor should now open. Enter the following strings, one per line:
25%
50%
Full Size
200%
Now, we move on to the business of selecting and setting a zoom factor based on the index of the item that has been selected. Double-click on the combo box and enter the following code:
With CrystalReportViewer1 Select Case ComboBox_Zoom.SelectedIndex Case 0 .Zoom(25) Case 1 .Zoom(50) Case 2 .Zoom(100) Case 3 .Zoom(200) End Select
You also may want to consider adding the option to let the user select their own Zoom factor. Keep in mind that 50% is about the lowest resolution at which a report can be read legibly with a 12-point font used in the report itself - if you are concerned about how the report will appear when viewed, you may also set the "minimum zoom" required to view the report as it should appear.
Another powerful navigation feature can be found in the SearchForText method within Crystal Reports.NET, which will allow you to search for a specific string that appears in your report. In our custom viewer, we will add a textbox (SearchString_TextBox) for the user to enter a search string, as well as a Search button (Search_Button) to kick off this method. Drag both of these items onto the Form, and set their properties appropriately.
The code behind the search button looks like this:
If SearchString_TextBox.Text <> " " Then CrystalReportViewer1.SearchForText(SearchString_TextBox.Text) PageNo_Label.Text = "Page: " & CrystalReportViewer1.GetCurrentPageNumber.ToString SearchString_TextBox.Text = " " Else MsgBox("Please enter a search string to search for", MsgBoxStyle.Exclamation, "Please enter a string to search for...") SearchString_TextBox.Text = " " End If
We first check to see if a value is entered and if so, the SearchForText method is called, passing the search string that was entered. The Crystal Report Viewer will search the entire report and when the value is found, go directly to the page on which it appears and highlight the value. This method can be called repeatedly to find all of the occurrences of a particular string - each time it finds the string in your report, it will jump to that page and highlight where the value appears.
Using our World Sales report and searching on Hong Kong should jump to the first company with a region of Hong Kong and highlight the value, as shown below:
Viewer events provide the ability to track the firing of different events - for instance, when the user navigates through the pages of the report, or when they refresh the report. These events can then be used to fire other code from within your application.
While all of the different events have their own unique properties and methods, they all inherit a common property called Handled. This is a Boolean value that is used to determine whether the event was fired and subsequently handled.
In the following section, we will be looking at all of the available events associated with the viewer and their common uses. Again, since this is a new set of functionality contained within the viewer, we are going to create another project to hold all of the code and Forms related to this section.
To create a new project from within Visual Studio select File | New | Project and from Visual Basic Applications, select Windows Applications and specify a name (in the sample code, we have called this project viewer_events) for your project files. Set this as your startup project within the solution.
Once your sample project has been created, add the Crystal Report Viewer to the default Form that is created and copy or add the ch3_worldsales.rpt to your project. Set the ReportSource property to point to this report and let''s get coding.
For all of these events, we are going to place the code behind our Form and when a particular event is fired, the code will be run.
For page navigation, the NavigateEventArgs class provides the properties we need to work with the Navigate event, including:
Property
Description
CurrentPageNumber Returns the current page number
NewPageNumber Gets or sets the new page number In the example below, the Navigate event would fire when a user changed the page within the viewer, resulting in a message box that would show the page they are currently on, and the page they are navigating to.
Insert the following subroutine into your Form:
Private Sub CrystalReportViewer1_Navigate(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.NavigateEventArgs) Handles CrystalReportViewer1.Navigate If MyEvent.NewPageNumber <> 1 Then MsgBox ("Current page: " & MyEvent.CurrentPageNumber & " New Page: " & Event.NewPageNumber) End If End Sub
Compile and run with this code. When the Form opens with the report, click on the last page icon. You should see a message box similar to the one below:
This event could be used to determine when the first page was viewed, and pop up another Form with an explanation of the report and its contents, or used to perform a task in the background (like logging page views) while the user is viewing the report.
The ReportRefresh event has no arguments other than the inherited Handled property. It can be used to build metrics on how often a report is run or refreshed, and to pass information to users about the report before they launch a refresh, as shown below:
Private Sub CrystalReportViewer1_ReportRefresh(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.ViewerEventArgs) Handles CrystalReportViewer1.ReportRefresh MsgBox ("Please be advised this report takes up to 2 minutes to run.") End Sub
Refresh events are also key to improving application and data performance - if your database is only updated once a day (or once a month) you can keep track of how many times a user attempts to hit the database, and simply remind them with an information box that the data will remain the same during the day, regardless of how many times they hit the refresh button!
When a user searches for a report value, either through the standard icon on the toolbar or through your own method call, the Search event is fired. The arguments for the Search event are:
Property
Description
Direction Gets or sets the direction in which to search. This can be either Backward or Forward.
PageNumberToBeginSearch Gets or sets the page number to start searching at.
TextToSearch Gets or sets the text to search for in the report. So by using these event arguments, you could keep a record of what values users searched for. An example of getting the text that is being used in the search is included below:
Private Sub CrystalReportViewer1_Search(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.SearchEventArgs) Handles CrystalReportViewer1.Search MsgBox ("You searched for " & event.TextToSearch ) End Sub
The Load event is fired whenever the report viewer is initialized from a Windows Form and has no other arguments other than Handled. You can use this event to fire other sections of code or launch additional windows for help, or a description of the report, as shown here:
Private Sub CrystalReportViewer1_Load(ByVal sender As System.Object, ByVal MyEvent As System.EventArgs) Handles CrystalReportViewer1.Load MsgBox ("This report shows monthly sales broken down by region") End Sub
Again, you could also use this event for logging as well.
For those times that the user changes the zoom factor for a particular report, the ViewZoom event fires, and has only one argument in ZoomEventArgs. The NewZoomFactor property will get or set the magnification factor for the viewer, as shown here:
Private Sub CrystalReportViewer1_ViewZoom(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.ZoomEventArgs) Handles CrystalReportViewer1.ViewZoom Select Case MyEvent.NewZoomFactor Case "25" MsgBox ("You have selected 25%") Case "50" MsgBox ("You have selected 50%") Case "100" MsgBox ("You have selected full size") End Select End Sub
If you are working with a report that has groups inserted, you can drill down within the viewer to show the detailed records that make up that group. By default, these details are visible anyway, as shown below:
When you drill down into a group, a separate tab is opened within the preview window, showing only the group you have selected. For summary reports, you may want to hide the details and allow users to drill down if they need more information.
This provides an easy way to cut down on report development - instead of multiple reports for different regions, for example, you could create one report and then let the users drill into it and print the section they wanted to see - in the example below, the user has drilled down into Australia, which opens another tab in the viewer and allows them to see the regions within Australia
When you double-click a group or summary and drill down into a report, the Drill event is fired and can be used to return the name of the group, the level, or other information. There are a number of properties associated with DrillEventArgs, including:
Property
Description
CurrentGroupLevel Returns the group level that was drilled into
CurrentGroupName Returns the name of the group that was drilled into
CurrentGroupPath Returns the group number and group level that was drilled into
NewGroupLevel Returns the target group level that is being drilled into
NewGroupName Returns the target group name that is being drilled into
NewGroupPath Returns the target group number and group level that is being drilled into Note
CurrentGroupNamePath and NewGroupNamePath are included within DrillEventArgs but are reserved for future use.
To see the Drill event in action, you will need to have a report that has at least one group inserted and a section where the details are hidden (not suppressed). In addition, in the Crystal Report Viewer, the EnableDrillDown property must be True. The Drill event will fire whenever you drill down into one of the groups on your report and can be used to determine the group name, and what level has been drilled into, among other things.
Drill events can be used to launch other Forms or processes - for example, when a user drills down on a Country group, you could display a Form giving a background to the country, its currency, and other pertinent information.
The code below demonstrates the Drill event being used to display an information box, containing information on where the user was drilling from and the target they were drilling to.
Private Sub CrystalReportViewer1_Drill(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.DrillEventArgs) Handles CrystalReportViewer1.Drill MsgBox("You drilled into " & MyEvent.NewGroupName() & (Chr(13) & Chr(10)), MsgBoxStyle.Information, "Drill Down Event") End Sub
Multiple subreports can be inserted into a main report and provide a way to combing disparate information on a single report. A subreport within a Crystal Report is actually a report in its own right, with its own page numbering, sections, and other information.
There are a number of subreport events that can be used as users drill through a report with subreports, including:
Property
Description
CurrentSubreportName Returns the name of the subreport that was drilled into
CurrentSubreportPageNumber Returns the page number that the subreport is on
CurrentSubreportPosition Returns the location in the viewer where the subreport is
NewSubreportName Returns the name of subreport that is being drilled into
NewSubreportPageNumber Returns the page number to drill into the subreport
NewSubreportPosition Returns the location in the viewer where the subreport is to drill into Using the properties above, you could determine the name of a report that had been drilled into and then use the same for logging and launching other Forms. Our report does not contain a subreport, but the methods remain the same.
For changing elements of a subreport, we would need to use functionality from the Crystal Report Engine, covered in Chapter 8.
The HandleException event fires whenever you are viewing a report and the viewer encounters any errors or exceptions. This could be caused by a data source not being available, the report file itself being moved to a different location (if external to your application) or any other error that may occur.
There are a number of arguments that are associated with this event, including:
Property
Description
Exception Returns the exception data for the exception that has occurred
UserData Returns or sets any type of data that can be used to over-ride what is done in the handling of an exception The UserData property is a generic object that can be used to override the error handling that the viewer would normally do. For example, if your were using an Access database and it had been moved and was no longer available, you could set the UserData property to a string containing the location to the UserData, and that particular database location would be used.
So, to trap these and other types of errors, you can set up an error handler event and then use the exception to return the error message:
Public Sub CrystalReportViewer1_HandleException(ByVal source As Object, ByVal MyEvent As CrystalDecisions.Windows.Forms.ExceptionEventArgs) Handles CrystalReportViewerl.HandleException Dim err As Exception err = myEvent.Exception MsgBox("An error has occurred with your report:" & (Chr(13) & Chr(10)) & err.ToString, MsgBoxStyle.Critical, "Exception Event") End Sub
Note
You also may want to consider tying context-sensitive help (where the help topic directly relates to the error message produced) to the error as well, to give the user a more complete description of what the error really means.