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);
|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 UIor 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
) and call the Find
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 (domain\username) 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.
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();
new NetworkCredential("uid", "pwd"));
NetworkCredentials = cc;
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.
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
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