Local Mode Reporting in Desktop Applications
1. Architetcure
2. Abstract
Report Viewer control can be configured to process reports locally so that they use the built-in processing provided by the control. When we configure the Report Viewer control for local processing, all report processing is performed on the computer that hosts the application. All data used by the report must be retrieved from data that the client application provides.
Data sources that are used in the reports must be defined in advance, during report design. At run time, application must generate the data table or retrieve the data that is used in the report. Locally processed reports can get data from data tables and Business objects. The steps for configuring a data source for a locally processed report vary depending on whether we are using the Web server control or the Windows Forms control.
2.1 Local Mode
In local mode, the Report Viewer morphs itself into a mini-Report Server. In this configuration, the Report Viewer control handles the report processing and rendering, not the Report Server. In fact, if we plan to use local mode only, we don't need Reporting Services at all (neither at design nor at runtime). That's because the Report Viewer controls don't have any dependencies to the Report Server. Local mode offers the following advantages:
• Easy report distribution. Can package the report definitions with the application and distribute them without requiring the 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 we 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. We shouldn't view the Report Viewer 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 a server-based reporting platform; the Report Viewer makes it easy to distribute the reports with custom applications.
In the absence of Report Server, the Report Viewer must obtain its data from the application. In local mode, the application is responsible for providing the necessary input to the report. That's why the Report Viewer doesn't display the parameter prompt area with local reports. Parameters and data are external to Report Viewer.
2.2 Steps
Authoring a local report consists of three steps:
• Configure the report data source
• Design the report layout
• Request the local report programmatically
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. We 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.
2.2.1 Configuring the Report Data Source
If we have a server report that we need to convert to a local report, we can skip this step. That's because the 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 we 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 creating 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 we can create an identical dataset definition to the one used by the Customer Orders server-side report.
1. Right-click on the project and choose Add-->New Item-->Dataset.
2. Right-click on the Dataset Designer canvas and choose Add-->TableAdapter. This launches the TableAdapter Configuration Wizard
3. Create a connection to the database or create a new one if it doesn't already exist. Save the connection string in the application configuration file if required.
4. In the "Choose a Command Type" step, select appropriate type and proceed further.
5. In the "Choose Methods to Generate" step, accept the defaults and finish the wizard.
6. Rename Data Table and datasets as required.
The end result of running the Table Adapter Configuration Wizard is a typed dataset as shown in Figure.
With either approach, all typed datasets defined in the project are made available to the report, so we can proceed to authoring the report from these datasets.
2.2.2 Designing a Local Report
With the report data source in place, we can proceed to author the actual report. (Don't need to worry even if the Reporting Services aren’t 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 we can use to design local reports right inside the code project. To do so, just double-click on the report definition file that we've already added to the project. We will undoubtedly find the Local Report Designer as shown in the figure. However Local Report Designer 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.
When we are done authoring the report, we 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.
2.2.3 Requesting a Local Report
The final step to implement local report processing is writing some code to request the report using the Local Report object.
private void GenerateReport(enumReportType reportType)
{
rptVwr.ProcessingMode = Microsoft.Reporting.WinForms.ProcessingMode.Local;
rptVwr.LocalReport.EnableHyperlinks = true;
localReport = rptVwr.LocalReport;
LoadDataSets(reportType);
localReport.ReportPath = reportPath;
this.rptVwr.RefreshReport();
}
private void LoadDataSets(Guid paramGuid)
{
reportDataSource = new ReportDataSource();
switch (reportType)
{
case (ReportTypes.EntityDetailsReport):
Report.GetEntityDetails(dataBase, ref dataSet);
this.reportPath = Properties.Resources.REPORT_ENTITYDETAILS_RDLC_PATH;
reportDataSource.Name = "EntityDetailsReportDataSet_EntityDetails";
reportDataSource.Value =dataSet.Tables["EntityDetails"];
localReport.DataSources.Add(reportDataSource);
break;
case (ReportTypes.EntityPropertyReport):
Report.GetPropertySheetInfo(dataBase, ref dataSet);
this.reportPath = Properties.Resources.REPORT_ENTITYPROPERTYSHEET_RDLC_PATH;
reportDataSource.Name = "EntityDetailsReportDataSet_PropertySheet";
reportDataSource.Value = dataSet.Tables["PropertySheet"];
localReport.DataSources.Add(reportDataSource);
break;
}
}
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), we need to explicitly enable hyperlinks (more on this in a moment). In addition, we'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.
2.3 Handling ReportViewer Events
The ReportViewer supports a number of events that the 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 report 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, we cannot use these events to change the report definition. Instead, the ReportViewer raises events when the control state changes, giving the application a chance to do some pre- or post-processing of remote and local reports.
Although the ReportViewer raises events in both modes, we will probably find them more useful in local mode. That's because in local mode, the application handles additional reporting tasks, e.g. passing data to a drillthrough report or subreport, collecting parameters, etc. The example demonstrates how the application can handle two of the most useful events—Drillthrough and Hyperlink.
2.4 Implementing Report Drillthrough
As with its server counterpart, the EntityDetails local report allows the end user to click on an Entity to drill through to the EntityDetails.rdlc report and see the Entity 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 rptVwr_Drillthrough(object sender, DrillthroughEventArgs e)
{
localReport = (LocalReport)e.Report;
IList<ReportParameter> paramList = localReport.OriginalParametersToDrillthrough;
if (paramList.Count > 0)
{
if (Properties.Resources.REPORT_ENTITYPROPERTYSHEET_RDLC_PATH.Contains(e.ReportPath))
{
if (string.Compare(paramList[0].Name, PARAM_ENTITY_ID, true) == 0)
{
if (paramList[0].Values.Count > 0)
{
Guid entityId = new Guid(paramList[0].Values[0]);
if (entityId != Guid.Empty)
{
reportType = ReportTypes.EntityPropertyReport;
LoadDatasetAndReportPath(entityId);
}
}
}
}
}
}
The ReportViewer passes the drillthrough target report in the DrillthroughEventArgs argument. First, the code checks the source of the drillthrough event. In this case, we need handle only drillthrough events, and only those from local reports. The application gets the selected Entity Id from the OriginalParametersToDrillthrough property. Finally, the application passes the two datasets to the Entity Details report.
Building a report-enabling custom application doesn't have to be a tedious chore. If we are tasked to report-enable.NET 2.0 Windows Forms applications and we 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 we need to distribute reports with the application or bind the report to an application dataset.
Comments
- Anonymous
September 14, 2008
Local Mode Reporting in Desktop Applications