Building Report-enabled Applications with the New ReportViewer Controls (Part 1 of 2)

Building Report-enabled Applications with the New ReportViewer Controls (Part 1 of 2)

icrosoft SQL Server 2005 Reporting Services (SSRS) provides open protocols for integrating reporting clients regardless of the targeted programming language and platform. A noticeable missing piece in its integration strategy was a presentation layer to deliver the generated reports to the end users. The RS 2005 Windows Forms and Web Report Viewer controls fill in this gap.

To understand how the report viewers simplify integrating RS with your applications, recall that the Report Server provides two communication fa?ades (HTTP Handler and Web service) hosted by Internet Information Server (IIS).

HTTP Handler
The RS HTTP handler lets external clients request a report by submitting HTTP GET or POST requests that specify the report URL, a feature usually termed “URL addressability.” For example, here is how you can render the Customer Orders report used in this article in PDF format. The example below assumes that the report was uploaded to the Prologika folder and takes two parameters.

   http://localhost/ReportServer?/Prologika/Customer       Orders&Date=1/1/2003&CustomerID=14335      &rs:Command=Render&rs:Format=PDF

As you would probably agree, requesting reports with URL addressability is easy. In its simplest implementation, you can embed the report URL link in your application. On the downside, in some cases URL addressability may present security risks because it exposes the report parameters. Another limitation with URL addressability is that it supports requesting reports only. You cannot use this integration option to perform management tasks. Because of these limitations, URL addressability may be deprecated in the long run in favor of the RS Web service.

RS Web Service
One significant limitation of the RS 2000 Web service was that it didn’t support interactive report features such as drilldown, toggled visibility, document maps, etc. As a result, developers were forced to use URL addressability when business requirements called for rich report experiences. Fortunately, this limitation has been lifted in RS 2005 and the RS Web service now supports interactive features. In fact, due to the sheer number of new rendering APIs, the RS team has split the Web service into two endpoints: the ReportExecution2005 Web service (ReportExecution2005.asmx), which handles report rendering tasks only, and the ReportService2005 (ReportService2005.asmx) Web service, which provides report management APIs. The RS 2000 Web service (ReportService.asmx) is still supported for backward compatibility.

However, it’s inherently more difficult to integrate report clients with the Web service than via the URL addressability option, because developers need to know how to access the SSRS. Web service programmatically and take extra steps to handle report images, report sessions, and other features that come for free with URL addressability. Supporting interactive features (toggle, drilldown, etc) presents additional challenges. This is where the report viewers come in. They shield developers from the Web service technicalities so you could spend less time writing plumbing code to deliver your reports to the end user.

Introducing the Report Viewers
The Windows Forms WinReporter sample application that accompanies this article demonstrates various features of the Windows Forms ReportViewer control. You can find the source code of WinReporter and the sample reports demonstrated in article by opening the Report Viewers solution. Figure 1 shows the WinReporter in action.

Figure 1. WinReporter Application: The Windows Forms Report Viewer supports local and remote integration modes.

The WinReporter Application
End users can request the Customer Orders report either remotely, in which case the report is generated by the Report Server, or locally. In remote mode, they can set the Date parameter by picking a date from the interface (see Figure 1). This causes the CustomerID parameter to refresh because the application shows only the customers who have placed orders on the selected date. When a user clicks the Run Report button, the Report Viewer submits the report request to the Report Server. The Customer Orders report also supports interactive features. For example, users can expand the customer field to see all the orders submitted by that customer (toggled visibility) or click on an order number to launch a Sales Order Details report (report drillthrough). Behind the scenes, the Report Viewer carries out these actions via calls to the SSRS Web service, which has been enhanced to support interactive features.

In contrast, in local mode, the Report Viewer bypasses the Report Server. The application supplies the report with parameters and data. The Report Viewer processes and renders the report. To end users, there’s little difference; the local version of the Customer Orders supports the same interactive features as its server counterpart.

ReportViewer Features
The ReportViewer supports a cornucopia of features, including:

  • Automatically handling report images and sessions.
  • Interactive features such as toggled visibility, document map, bookmarks, interactive sorting, etc.
  • Printing and print preview.
  • Export to multiple formats.
  • In remote mode, integration with managed report server environment: security, caching, scheduling, delivery, etc.

The end-user features can be initiated from the toolbar. For example, the end user can page through the report or export the report in one of the supported export formats. In remote mode, the ReportViewer can optionally display a parameter area which generates a parameter control for each parameter defined in the report. The ReportViewer supports a certain degree of customization. For example, if the application supplies all the report parameters, the parameter area is redundant and can be hidden.

Installing the Report Viewer Controls
Although they’re related to Reporting Services, the report viewers are not shipped with either Reporting Services or Business Intelligence Studio. Instead, they are bundled with VS.NET 2005. Therefore, to use the report viewers in your applications, you need a copy of VS.NET 2005. The viewers are physically implemented in five .NET assemblies, as shown in Table 1.

Table 1. ReportViewer Assemblies: The table lists the five assemblies that implement the Web Forms and Windows Forms report viewers.

Assembly FileDescription
Microsoft.ReportViewer.WinForms.dllThe Windows Forms control implementation.
Microsoft.ReportViewer.WebForms.dllThe Web Forms control implementation.
Microsoft.ReportViewer.Common.dllFunctionality common to both controls.
Microsoft.ReportViewer.ProcessingObjectModel.dllThe Processing Object Model accessed by expressions in the report.
Microsoft.ReportViewer.Design.dllDesign-time functionality of both controls. Not required at run-time.

The report viewers are freely redistributable. To facilitate distributing the controls to end users, Microsoft has provided a Microsoft ReportViewer Redistributable 2005 package. The report viewers have a dependency on .NET 2.0, so prior to deploying your application you need to install the .NET Framework 2.0 on the client machine. Remote mode is supported only with RS 2005.

For the purposes of running the sample code accompanying this article, you also need to install the AdventureWorks sample SQL Server database, which is included in the SQL Server setup but is not installed by default. To so install it, run SQL Server setup, and in the Feature Selection step, click the Advanced button. Expand the Sample Databases folder and select the AdventureWorks database. In addition, before running the code samples, open the report viewer solution and deploy the Reports project to deploy the sample reports to the Report Server.

Figure 2. Adding a ReportViewer: You can drag-and-drop a ReportViewer control to a form and configure it at design time.

Configuring the ReportViewer
To embed the ReportViewer in your Windows Forms .NET applications, open Visual Studio.NET 2005 and create a Windows Forms project. Open your form in design mode, expand the Data tab of the VS.NET toolbox and select the ReportViewer control (see Figure 2). Then, drag and drop it to the design canvas of your form.

As with other Windows Forms controls, you can configure the ReportViewer during design time using the VS.NET 2005 Smarts Tags feature (Tasks panel) or the Properties Window. For example, you can set up the Tasks panel to render a specific server or local report. Or, you can disable the toolbar buttons or the entire toolbar by setting the ShowToolBar property in the Properties window.

The ReportViewer also exposes a comprehensive object model that you can control programmatically, as the WinReporter sample demonstrates. In remote mode, you interact with the ServerReport object (reportViewer.ServerObject), while in local mode you use the LocalReport object (reportViewer.LocalObject). These objects expose almost identical programming interfaces, so switching from one mode to another is easy.

Now that I’ve given you a high-level overview of the Windows Forms ReportViewer, let’s find how you can use it to request server and local reports.

Remote Mode
In remote mode, the Report Server renders the report. In this mode, you would typically use the Report Designer to author the report definition. When the report is ready, you deploy the report to the report catalog to make it available to report clients. The remote mode offers the following main advantages:

  • Centralized report management. You deploy and centrally manage all of your reports in a single repository?the report catalog.
  • Rich feature set. You can leverage all Report Server features, such as scheduling, security, session and snapshot execution, linked reports, My Reports, etc. Note that the actual feature set varies among the RS editions.
Figure 3. Remote Mode: In remote mode, the report is rendered by the Report Server.

When you configure the ReportViewer in remote mode, it essentially acts as a thin presentation layer to the Report Server. The Report Server does the heavy lifting to process the report; the ReportViewer renders the report (see Figure 3). Behind the scenes, the ReportViewer calls the Report Server APIs to retrieve report and parameter metadata, pass parameters, and carry out user-initiated commands, such as zooming, printing, searching, etc. The ReportViewer performs most of its inner workings by making SOAP calls to the RS Web service. The noticeable exception is that report preview and rendering use URL addressability for performance reasons, to avoid incurring additional overhead for serializing the report payload?because the SOAP protocol is less efficient for transferring large chunks of data. A special RGDI rendering extension (new to RS 2005) is used to render the report, while a previewed report is rendered in EMF format. By default, exceptions thrown during report processing are embedded in the ReportViewer report pane. If this is not acceptable, you can handle the ReportError event of the ReportViewer object programmatically. The Exception property of the ReportErrorEventArgs class gives you the error that occurred. Setting the Handled property to true will prevent the ReportViewer from displaying an error message.

Requesting a Server Report
Developers who have generated reports by integrating with the RS Web service will undoubtedly appreciate the simplicity the ReportViewer brings to the table. Requesting a server report that doesn’t require parameters takes only a few lines of code.

   reportViewer.ProcessingMode =    Microsoft.Reporting.WinForms.ProcessingMode.Remote;   // Get the Report Server endpoint from the application    // config file   reportViewer.ServerReport.ReportServerUrl = new       Uri(Settings.Default.ReportServerEndPoint);   reportViewer.ServerReport.ReportPath =       "/Prologika/Customer Orders";   reportViewer.RefreshReport();

The preceding code sets the ReportViewer ProcessingMode property to ProcessingMode.Remote because it’s requesting a server-based report. Next, it sets the ReportServerUrl property to the Report Server endpoint and report path retrieved from the config file, and finally, calls the RefreshReport method, which submits the report request and renders the report. It really can’t be simpler! No need to mess with WebMethod arguments and save the report payload to a physical file. No need to handle images and sessions.

Note that if you need to request a specific history run for a report configured for snapshot execution, set the reportViewer.ServerReport.HistoryId property to the desired history identifier.

Handling Report Parameters
The ReportViewer can detect the report parameters for parameterized reports automatically. When the ShowParameterPrompts property is set to True (the default), the ReportViewer displays parameter placeholders in the Parameter Area of the toolbar. This behavior is consistent with rendering reports in the Report Manager. Behind the scenes, the ReportViewer invokes the LoadReport method (new with RS 2005) which returns an ExecutionInfo object describing the report, including the report parameters. The ReportViewer uses this information to configure the parameter prompts, displaying appropriate controls. For example, it displays a calendar control for parameters with the DateTime data type, or a multi-select list for multi-valued parameters.

If the parameter prompts don’t meet your integration requirements, you can hide the parameters area and implement your own UI instead. You can call the ReportViewer GetParameters method to obtain the parameter metadata. After collecting the parameters, you can pass them to the ReportViewer as follows:

   ReportParameter[] parameters = new ReportParameter[2];   parameters[0] = new ReportParameter("Date", "1/1/2003");   parameters[1] = new ReportParameter("CustomerID",       new string[] { "14335", "15094" });   reportViewer.ServerReport.SetParameters(parameters);

In the code above, the Customer Orders report takes two parameters. Note that the CustomerID parameter is configured as a multi-value parameter (new with RS 2005). You can pass multiple parameter values by constructing a string array. The SetParameters method invokes the SetExecutionParameters method, which prepares the report execution object in anticipation of the Render call (submitted when the report is refreshed).

Author’s Note: In VB.NET arrays are zero-based. Thus, the code Dim ReportParameter(1) declares an array that holds two parameters.

Customizing the ReportViewer
Besides parameter handling, the ReportViewer object model exposes several other methods to meet more advanced integration requirements. For example, suppose that you need to export your report to an image format other than TIFF (the default image format). The ReportViewer UI doesn’t allow you to specify device settings to request a specific image format. Luckily, the ReportViewer object model allows you to render a report manually. To do so, call the reportViewer.ServerReport.Render method, which wraps the Render Web service API.

Another interesting pass-through method is LoadReportDefinition. The LoadReportDefinition method (new with RS 2005) lets you render a server report without requiring the report definition to be uploaded to the report catalog. This allows you to implement a “preview” feature similar to Report Builder. For example, suppose that you need to allow users to construct the report definition in an ad hoc fashion. Requiring them to save the report before viewing it could be time consuming and might require additional house-keeping programming logic. Instead, you can let users preview the report with code such as this:

   // serialize the report definition to a byte array   byte[] reportPayload = GetReportDefinition()   stream.Write(reportPayload, 0, reportPayload.Length);   stream.Position = 0;   StreamReader reader = new StreamReader(stream);   _serverReport.LoadReportDefinition(reader);
Author’s Note: Before you deploy code that uses the LoadDefinition method, grant the end users rights to execute the Execute Report Definitions system task. Be aware that the report is executed in the root (Home) folder of the report catalog. Therefore, you must adjust the location of the resources used by the report (images, data sources, etc) to be relative to the Home folder so they can be rendered properly.

You aren’t limited to the default ReportViewer toolbar, either; you can customize that to suit your needs. For example, the control may not blend with your application’s UI?or you may just want to have more control over its features. You can do so by hiding the toolbar areas you don’t want (or the entire toolbar), or implementing your own toolbar. If you do that, you’ll need to call the appropriate method programmatically when a user requests an action. For example, if you implement a custom Find toolbar to search the report body, you’ll need to hide the ReportViewer Find toolbar controls (set ShowFindControls to False) and call the Find method manually.

   report.Find(searchString, startPage);

The ReportViewer is a standard Windows Forms control and it is not sealed (NotInheritable in VB.NET). Therefore, there is nothing stopping you from subclassing it whenever that makes sense. For example, suppose that you need to implement a proprietary security model (e.g. impersonating the user) to secure the calls between the ReportViewer and the Report Server and package that with the control. One way to accomplish this is to create a custom class, perhaps called ReportViewerEx, that inherits from the ReportViewer control and handles the security requirements. Once ReportViewerEx is ready, you can compile it and distribute it to your developers.

Securing the ReportViewer in Remote Mode
Security plays an important role in all RS integration scenarios. In remote mode, the ReportViewer control allows you to control how report requests to the Report Server will be authenticated.

Windows Integrated Security
Out of the box, the Report Server is configured and works best with Windows Integrated security. This security mode is your best bet when you have Windows Active Directory in place and requests to the Report Server are submitted using the domain identity (domainusername) of the users. You don’t have to do anything special to configure the Report Viewer for Windows Integrated security. That’s because the ReportViewer passes the user identity to the ReportServer by default by setting the Credentials property of the Web service proxy to CredentialCache.DefaultCredentials.

Basic Authentication
Sometimes, Windows Integrated security is not an option. For example, a third-party vendor may not be able to control the security infrastructure of its customers. In this case, you can configure the Report Server virtual root in IIS to use Basic Authentication and create local user accounts for each end user. Assuming that you have a security service in place to return the user credentials, configuring the ReportViewer for Basic Authentication is easy:

   private void SetBasicSecurity()     {      CredentialCache cc = new CredentialCache();      cc.Add(new Uri(         Settings.Default.ReportServerEndPoint),"Basic",          new NetworkCredential("uid", "pwd"));      reportViewer.ServerReport.ReportServerCredentials.         NetworkCredentials = cc;   }

Custom Security
When security requirements rule out both Windows-based security and Basic Authentication, you can configure the Report Server to use a custom security scheme. I covered the RS custom security model in an earlier DevX article titled “Harden MS Reporting Services Using Custom Extensions.” For example, your application may support an application-based security model where the end user logs in to the application with credentials kept in a database. The simplest (and less secure) approach to integrate the ReportViewer with custom security is to pass the user name and password explicitly.

   reportViewer.ServerReport.ReportServerCredentials.      SetFormsCredentials(null, "uid", "pwd", null);

In this case, the ReportViewer will call the LogonUser method and cache the authentication ticket for the lifetime of the ReportViewer control instance. There is also a GetFormsCredentials method, which you can call at a later time if you want to retrieve the cached user credentials.

Unfortunately, as it stands, the GetFormsCredentials method doesn’t return the authentication ticket, which forces you to keep the user credentials for the lifetime of the application. For this reason, I recommend you adopt a slightly more involved approach where the application calls LogonUser and caches the authentication ticket. For example, upon application startup, the application can collect the user login credentials and authenticate the user against RS by invoking the LogonUser method manually. Then, the application can save the ticket and discard the user credentials.

Figure 4. Local Mode Reporting: In local mode, the Report Viewer processes and renders the report.
   private void SetCustomSecurity2()    {      ReportServerProxy rs = new ReportServerProxy();      rs.LogonUser("rs", "rs", null);      // Save the authentication ticket and discard user       // credentials      reportViewer.ServerReport.ReportServerCredentials.         SetFormsCredentials(ReportServerProxy.AuthCookie,          null, null, null);   }

Part II of this article will cover custom security in more detail, showing you how to integrate it with the Web ReportViewer. For now, it’s worth exploring the local mode (see Figure 4).

Local Mode
In local mode, the ReportViewer morphs itself into a mini-Report Server. In this configuration, the ReportViewer control handles the report processing and rendering, not the Report Server. In fact, if you plan to use local mode only, you don’t need Reporting Services at all (neither at design nor at runtime). That’s because the ReportViewer controls don’t have any dependencies to the Report Server. Local mode offers the following advantages:

  • Easy report distribution. You can package the report definitions with your application and distribute them without requiring your customers to have the Report Server installed.
  • Flexible data binding scenarios. The application can bind the local report to an ADO.NET dataset or a business object. As with RS 2000, binding datasets to server reports is not supported and may require a custom data extension.

Before you get too excited about not needing the Report Server (and a SQL Server 2005 license), take some time to compare and evaluate remote and local modes. You shouldn’t view the ReportViewer local mode as a replacement for Report Server; its features are limited to report processing and rendering only, which means no report catalog, no caching, no subscribed delivery, no security, etc. In addition, it limits export formats to PDF and Excel only. Therefore, the Report Server and the Report Viewer are not competing products, they’re complementary technologies. The Report Server gives you a server-based reporting platform; the Report Viewer makes it easy to distribute your reports with custom applications.

In the absence of Report Server, the ReportViewer must obtain its data from your application. In local mode, your application is responsible for providing the necessary input to the report. That’s why the ReportViewer doesn’t display the parameter prompt area with local reports. Parameters and data are external to ReportViewer.

Authoring a local report consists of three steps:

  • Configure the report data source
  • Design the report layout
  • Request the local report programmatically

I’ll explain each of these steps in more detail below.

Author’s Note: To differentiate remote from local reports, the RS team introduced a RDLC file extension where “C” stands for client-side processing—a logical difference only. You describe both server (RDL) and client-side reports (RDLC) in the same XML schema-Report Definition Language. The RDLC definitions are more flexible because the Report Server doesn’t validate them. For example, some values, such as dataset queries, may be missing. This means that in most cases, you can use the server-side report definition as a client-side report with no additional steps (see the Converting RDL and RDLC topic in the VS.NET documentation for more information). The Report Viewer doesn’t require a local report definition to have an RDLC extension. That said, I will stick to the Microsoft naming convention and use RDLC for local files.

Configuring the Report Data Source
If you have a server report that you need to convert to a local report, you can skip this step. That’s because your report definition already describes the dataset(s) that will feed the report. The only thing left is to bind the local report to an application dataset with an identical schema at runtime. If you create a local report from scratch, there are two ways to create a data source in Visual Studio.NET?inside the local report itself or as an external dataset.

Selecting a Report Data Source
To try this option, create a new local report (right-click on the project node in the Solution Explorer and choose Add–>New Item–>Report. Next, click on the Add New Data Source button inside the Data Source window. This starts the Data Source Configuration Wizard which allows you to create a data source from a database (table, view, or stored procedure), Web service, or an application object. However, as it stands, the Data Source Configuration Wizard does not support free-form SQL queries.

Project-level Dataset
A second (and recommended) way is to add a new dataset to the project. For example, here is how you can create an identical dataset definition to the one used by the Customer Orders server-side report.

  1. Copy the SQL SELECT statement that loads the dsOrders dataset in the Customer Orders.rdl report.
  2. Right-click on the WinReporter project and choose Add–>New Item–>Dataset. Name the dataset CustomerOrders.xsd.
  3. Right-click on the Dataset Designer canvas and choose Add–>TableAdapter. This launches the TableAdapter Configuration Wizard (the same wizard available in ASP.NET), which supports SELECT queries.
  4. ?
    Figure 5. Configuring the Dataset Schema: The figure shows the process of selecting a report data source.
  5. Create a connection to the AdventureWorks database or create a new one if it doesn’t already exist. Save the connection string in the application configuration file as AdventureWorksConnectionString.
  6. In the “Choose a Command Type” step, leave the “Use SQL Statements” options selected.
  7. In the “Enter a SQL Statement” step, paste the SQL SELECT statement you copied in step 1.
  8. In the “Choose Methods to Generate” step, accept the defaults and finish the wizard.
  9. Rename DataTable1 to CustomerOrders.

The end result of running the TableAdapter Configuration Wizard is a typed dataset (CustomerOrders.xsd), as shown in Figure 5. With either approach, all typed datasets defined in the project are made available to the report, so you can proceed to authoring the report from these datasets.

Designing a Local Report
With the report data source in place, you can proceed to author the actual report. Don’t worry if you don’t have Reporting Services installed and cannot use the Report Designer. VS.NET 2005 includes a scaled-down version of Report Designer (for the lack of better term, let’s call it Local Report Designer) which you can use to design local reports right inside your code project. To do so, just double-click on the report definition file that you’ve already added to your project. You will undoubtedly find the Local Report Designer similar to its bigger cousin (the RS Report Designer) but its features are more limited. For example, the Local Report Designer doesn’t have Data and Preview tabs because a local report doesn’t know where to get its data from. Remember, in local mode, the application supplies data and parameters at runtime.

Handling Images
You may need additional tweaks to migrate a server report to a local report. For example, if the server report uses an external image located in the same folder of the report catalog, you may need to re-adjust the image path. As it stands, the Report Viewer won’t attempt to load the image from the current application directory by default. As a workaround, you should use the file:// protocol to load external images. Embedding the image path in the report definition is something you will probably want to avoid. For this reason, the Customer Orders.rdlc report contains a simple VB.NET embedded function which returns the application path.

   Public Function GetAppPath() As String      Return System.IO.Directory.GetCurrentDirectory   End Function

Using this function, here’s how you can set up the Value property of the logo image to point to the external AWC.jpg file.

   =String.Format("file:///{0}{1}", Code.GetAppPath(),       "AWC.jpg")

You won’t see the image at design time but it should appear when the report is run. Of course, another approach to get away the external images limitation in local mode is to convert all external images to embedded images that are saved in the report definition.

When you are done authoring the report, you can remove the report definition file from the project. Upon deployment, the application setup program could copy the report definition to a known location where the Report Viewer can find it.

Here’s a useful tip: To automate copying the local report definition to the application runtime folder at design time, you can define a post-build event in the Build Events tab of the project properties.

Requesting a Local Report
The final step to implement local report processing is writing some code to request the report using the LocalReport object.

   private void RunLocal()   {      reportViewer.ProcessingMode = ProcessingMode.Local;      reportViewer.LocalReport.ReportPath =          "Customer Orders.rdlc";      // Enable hyperlinks to handle clicking on the       // Customer ID      reportViewer.LocalReport.EnableHyperlinks = true;      // Required since the logo image in Customer Orders       // report is external      reportViewer.LocalReport.EnableExternalImages = true;      reportViewer.LocalReport.         ExecuteReportInCurrentAppDomain(         System.Reflection.Assembly.GetExecutingAssembly().         Evidence);         // The application is responsible for collecting       // parameters      ReportParameterInfoCollection parameterInfo =            reportViewer.LocalReport.GetParameters();      if (parameterInfo.Count > 0)      {         ParameterDialog dialog = new             ParameterDialog(parameterInfo);         DialogResult result = dialog.ShowDialog();         if (result == DialogResult.Cancel) return;         // Add report parameters         reportViewer.LocalReport.SetParameters(            dialog.Parameters);         // Add report data sources         reportViewer.LocalReport.DataSources.Add(            GetCustomers(dialog.Parameters[0]));      }         reportViewer.RefreshReport();                   }

First, the code configures the ReportViewer for local processing. Next, the code sets LocalReport.ReportPath to the local report definition file (by default, it will try to find it from the executable folder). As a security measure, if the local report uses navigation actions (hyperlinks), you need to explicitly enable hyperlinks (more on this in a moment). Similarly, since the local report loads the AdventureWorks logo as an external image, you need to enable external images. In addition, you’ll need to elevate the CAS permissions of the local report by running the report in the current application domain. That’s because the GetAppPath() embedded function needs FileIOPermission to get the application path where the external image is located.

Next, you need to take care of the report parameters (if any). The WinReporter launches the ParameterDialog form to collect the report parameters. Next, the code passes the parameters to the local report. Finally, the code loads the application dataset and passes it to the report. The RefreshReport method renders the report locally.

Handling ReportViewer Events
The ReportViewer supports a number of events that your code can handle at runtime. For example, the Windows Forms ReportViewer always processes the report request on a background thread to keep the main application thread responsive. If the application needs to be notified when the repot is ready, it can sink the RenderingComplete event. It is important to note that these events are raised by the control, not by the report processor. Therefore, you cannot use these events to change the report definition. Instead, the ReportViewer raises events when the control state changes, giving your application a chance to do some pre- or post-processing of remote and local reports.

Although the ReportViewer raises events in both modes, you will probably find them more useful in local mode. That’s because in local mode, your application handles additional reporting tasks, e.g. passing data to a drillthrough report or subreport, collecting parameters, etc. WinReporter demonstrates how your application can handle two of the most useful events?Drillthrough and Hyperlink.

Implementing Report Drillthrough
As with its server counterpart, the Customer Orders local report allows the end user to click on an order number to drill through to the Sales Order Detail.rdlc report and see the order details. However, as with any local report, the application has to supply the data for the drillthrough report. This happens in the Drillthrough event.

      private void reportViewer_Drillthrough(object sender,       DrillthroughEventArgs e)   {       if (e.Report is ServerReport) return;          LocalReport localReport = (LocalReport)e.Report;       // Sales Order Detail local report        // takes two datasets       // Load the Sales Order dataset       OrderTableAdapter orderAdapter = new           Entities.SalesOrderTableAdapters.          OrderTableAdapter();       SalesOrder.OrderDataTable orderTable =           orderAdapter.GetData(localReport.          OriginalParametersToDrillthrough[0].Values[0]);       // Load the Sales Order Detail dataset       OrderDetailTableAdapter orderDetailAdapter = new           OrderDetailTableAdapter();       SalesOrderDetail.OrderDetailDataTable           orderDetailTable = orderDetailAdapter.GetData(          localReport.OriginalParametersToDrillthrough[0].          Values[0]);          localReport.DataSources.Add(new ReportDataSource(          "SalesOrder", orderTable));       localReport.DataSources.Add(new ReportDataSource(          "SalesOrderDetail", orderDetailTable));   }   

The ReportViewer passes the drillthrough target report in the DrillthroughEventArgs argument. First, the code checks the source of the drillthrough event. In this case, you need handle only drillthrough events, and only those from local reports. The application gets the selected order number from the OriginalParametersToDrillthrough property. Finally, the application passes the two datasets to the Sales Order Detail report (one for the order header and another for the order details).

Figure 6. Raising Events: Use a hyperlink event to raise an event back to the application.

Implementing a Custom Navigation Action
Sometimes, you may need to pass some information from the report to the application?in other words, implement update capabilities. In an example scenario, a user can click on the customer identifier inside the report. The application intercepts the event and displays the customer details. You might want to let the user update the customer record and save it back to the database. Then, the application would refresh the report to show the changed data.

You can use a Hyperlink event to implement this scenario. First, define a “Jump to URL” action on the textbox control that displays the customer identifier (see Figure 6). Next, sink the ReportViewer Hyperlink event.

   private void reportViewer_Hyperlink(object sender,       HyperlinkEventArgs e)   {       Uri uri = new Uri(e.Hyperlink);       if (uri.Scheme.ToLower() == "customerid")       {           e.Cancel = true;           // Load the customer details in another form         ((ReportViewer)sender).RefreshReport();        }   }

The HyperlinkEventArgs exposes the hyperlink itself. Assuming that the end user has clicked on customer 14335, the hyperlink will look like “customerid:14335.” Since there may be other hyperlink events raised by the same report or other reports, we check the hyperlink schema, which in this case will return “customerid.” Next, the application does whatever it needs to with this customer. Finally, the event handler refreshes the report to show the updated results.

Building a report-enabling custom application doesn’t have to be a tedious chore. If you are tasked to report-enable.NET 2.0 Windows Forms applications and you target RS 2005, do yourself a favor and use the Windows Forms ReportViewer. Configure the ReportViewer in remote mode when requesting server reports, and consider local mode, when you need to distribute reports with your application or bind the report to an application dataset.

From here, I suggest you review the resources available on the ReportViewer website, where you’ll see how to get the most out of the ReportViewer in local mode, including using custom assemblies, working with subreports, binding to object data sources, generating the report definition on the fly, and more. Make sure to check also ReportViewer newsgroup on MSDN where you can post questions and get feedback from the technical community.


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist