Back on the main report, select the Subreport control in the designer, right-click and select
Subreport Properties from the menu, and go to the Parameters tab. Click the Add button, specify
CustomerID as the parameter name, and enter [CustomerID] as its value. Now each time it renders
the subreport, it will pass it the current value of the customer ID field.
The final thing to do is retrieve the value of that parameter in your ProcessSubreport event
handler, and filter the results returned accordingly, like so:
Vb
Private Sub ProcessSubreport(ByVal sender As System.Object,
ByVal e As SubreportProcessingEventArgs)
Dim customerID As Integer =
Convert.ToInt32(e.Parameters("CustomerID").Values(0))
Dim fromDate As DateTime = DateTime.Today.AddMonths(-1)
Dim qry = From co In context.SalesOrderHeaders
Where co.CustomerID = customerID AndAlso co.OrderDate > fromDate
Select co
e.DataSources.Add(New ReportDataSource("OrderData", qry))
End Sub
c#
public void ProcessSubreport(object sender, SubreportProcessingEventArgs e)
{
int customerID = Convert.ToInt32(e.Parameters["CustomerID"].Values[0]);
DateTime fromDate = DateTime.Today.AddMonths(-1);
var qry = from co in context.SalesOrderHeaders
where co.CustomerID == customerID && co.OrderDate > fromDate
select co;
e.DataSources.Add(new ReportDataSource("OrderData", qry));
}
the report wizard
The easiest place to start when designing a report is to make use of the Report Wizard. The Report
Wizard leads you through all the main steps to generate a report, and based upon your input will
generate the report for you that you can then customize to your needs.
The Report Wizard takes you through the following steps:
.
Choosing/creating a data source: Enables you to select an existing data source or create a
new one as the source of data for the report. This step is exactly the same as was detailed
earlier in the “Defining Data Sources” section of this chapter.
.
Arranging fields: Drag fields into the Values list to create a simple table, add fields in the
Row Groups list to group the rows of the table by those fields, and add fields to the Column
Groups list to group the columns by those fields (which will turn it into a matrix).
rendering reports .
673
.
Choose the layout: Gives you the option to add subtotals and grand totals rows/columns.
.
Choose a style: Allows you to choose different colors and styles used in the output. If you
want to create your own color scheme you can do so by modifying the StyleTemplates
.xml file in the C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\
PrivateAssemblies\1033 folder on your machine (this path may differ on your machine
based upon where Visual Studio has been installed).
To start the Report Wizard you will need to create a new report file (you cannot use the Report
Wizard on an existing file or after it has already been run). Add a new item to your project, and
from the Reporting subsection add a new Report Wizard item.
The Report Wizard takes you through its series of steps to generate a basic report. Once you have
completed the steps, it generates the report and opens it in the report designer for you to modify as
required.
This is a great place to start when learning how to design reports, and when
you become more familiar and comfortable with the process and designing more
complicated reports you will find yourself using it less and less.
renderinG rePorts
Now that you have designed your report, it’s time to actually generate it by populating it with data.
This is where the Report Viewer control is used, because it contains the local engine for generating
the report from the report definition files and the data sources.
the report Viewer controls
There are two versions of the Report Viewer control: one for use in web applications and one for
use in Windows applications. However, the way you use them to generate and display reports is
virtually identical.
The Windows version of the control is shown in Figure 30-33 .
fiGure 30-33
674 .
chaPter 30 reporTing
The Report Viewer contains a toolbar with various functions (such as Refresh, Export, Print, and
so on), and a view of the report (page by page). Individual functions on this toolbar can be turned
off via properties on the Report Viewer control, and each raises an event when clicked (although the
corresponding behavior is performed by the Report Viewer control automatically unless cancelled in
the event handler).
To use the Report Viewer control in your Windows Forms project, simply drop it on your form from
the Toolbox.
The web version also looks quite similar (shown in Figure 30-34), but displays the report output in a
browser.
fiGure 30-34
To use the web version of the Report Viewer control, you can drop it on a page from the Toolbox
(in the Reporting tab). This adds a namespace prefix (rsweb) for the Microsoft.ReportViewer
.WebForms assembly/namespace, and the following tag to use the Report Viewer control:
<rsweb:ReportViewer ID=”reportViewer” runat="server" />
The web version of the Report Viewer control also requires a Script Manager to be on the page. If
you don’t have one on the page already, drag this from the Toolbox (under the AJAX Extensions
tab) and onto the page.
When you display a report in the web version of the Report Viewer control you will find that it
displays a Print button on the toolbar only in Internet Explorer (IE), and not in other browsers such
as Firefox. This is because, in order to print the report from the browser, the Report Viewer needs
an ActiveX control to do the printing and ActiveX controls only work in IE. Because printing can’t
be done from other browsers the Print button won’t be displayed. When you click the Print button
in IE the first time it will ask you for permission to install the ActiveX control.
Generating the report
The process of generating a report is essentially to tell the report engine which report definition file
to use, and pass it the data (objects, entities, data tables, and so on) to populate the report with.
By default the report definition file is embedded into the assembly, although it often is best to have
it as a separate file so it can be easily updated when necessary without having to recompile the
rendering reports .
675
assembly. However, embedding it into the assembly means that there are fewer files to distribute,
and it may in some circumstances be preferable that the report definition file cannot (easily) be
tampered with. Set the Build Action on the report definition file to Embedded Resource in order for
it to be embedded in the assembly (which is the default value), or otherwise set it to be Content.
The following code is what is required to generate a report from a file-based report definition file
and populate it with some data (the data variable contains a collection of entities from the Entity
Framework model, which is used to populate the CustomerData data source in the report):
Vb
Dim reportEngine As LocalReport = reportViewer.LocalReport
reportEngine.ReportPath = "CustomerReport.rdlc"
reportEngine.DataSources.Add(New ReportDataSource("CustomerData", data))
reportViewer.RefreshReport() 'Only for Windows Report Viewer
c#
LocalReport reportEngine = reportViewer.LocalReport;
reportEngine.ReportPath = "CustomerReport.rdlc";
reportEngine.DataSources.Add(new ReportDataSource("CustomerData", data));
reportViewer.RefreshReport(); // Only for Windows Report Viewer
Here you get the existing LocalReport object from the Report Viewer control, assign values to its
properties, and then use the RefreshReport function on the Report Viewer control to start the
report engine generating the report.
If you have chosen to embed the report in your assembly, then instead of setting the ReportPath
property on the LocalReport object you will need to set the ReportEmbeddedResource property
instead. This must be the qualified resource path (which is case sensitive), including the namespace
and the extension of the report like so:
Vb
reportEngine.ReportEmbeddedResource = "Chapter30Sample.CustomerReport.rdlc"
c#
reportEngine.ReportEmbeddedResource = "Chapter30Sample.CustomerReport.rdlc";
If you have one or more subreports in your report you will also have to handle the
SubreportProcessing event of the LocalReport object as was demonstrated when discussing
the Subreport control. If you are using custom assemblies, you will need to include the code to
specify that the custom assembly is trusted. In addition, you may need to set the properties on the
LocalReport object to enable the report to use external images, hyperlinks, and so on. However,
the code provided here is the core code required to generate a report and display it in the Report
Viewer control.
rendering reports to different formats
It’s not necessary to display a report in the Report Viewer control. In some instances you may want
to generate the report and e-mail it as a PDF without any user interaction, or return a PDF’d report
676 .
chaPter 30 reporTing
as a result of a web service call. The Report Viewer control enables you to export the report to
various formats (Excel, PDF, Word, and so on) as an option on its toolbar, and this can also be done
via code. This is possible by creating a LocalReport object, setting the required properties, and
then using the Render function on the LocalReport object to render it to a specified format (which
is output to a stream or byte array).
The Render function has a number of overloads, but the simplest one to use is to just pass it the
output format (in this case PDF) and it will return a byte array containing the report. For example:
Vb
Dim reportOutput As Byte() = reportEngine.Render("PDF")
c#
byte[] reportOutput = reportEngine.Render("PDF");
The report engine can generate the report in a number of formats. Valid values include:
.
PDF: Output to an Adobe Acrobat file
.
Word:- Output to a Microsoft Word document
.
Excel: Output to an Microsoft Excel spreadsheet
.
Image: Output to a TIFF image file
To output to a stream (such an HTTP Response stream or a file stream) you can turn the bytes into
a stream:
Vb
Dim stream As MemoryStream = New MemoryStream(reportOutput)
stream.Seek(0, SeekOrigin.Begin)
c#
MemoryStream stream = new MemoryStream(reportOutput);
stream.Seek(0, SeekOrigin.Begin);
Alternatively, for larger reports (where this may be too memory-intensive) you can write directly to
a stream from the Render function using one of its overloads, passing in a callback function that
creates and returns the stream to write to as the value for the createStream parameter:
Vb
Private Function CreateReportFileStream(ByVal fileName As String,
ByVal extension As String,
ByVal encoding As Encoding,
ByVal mimeType As String,
ByVal willSeek As Boolean) As Stream
Return New FileStream(fileName & "." & extension, FileMode.Create)
End Function
summary .
677
c#
private Stream CreateReportFileStream(string fileName, string extension,
Encoding encoding, string mimeType, bool willSeek)
{
return new FileStream(fileName + "." + extension, FileMode.Create);
}
Then you can call the render function like so:
Vb
Dim warnings As Warning() = Nothing
reportEngine.Render("PDF", Nothing, AddressOf CreateReportFileStream, warnings)
c#
Warning[] warnings;
reportEngine.Render("PDF", null, CreateReportFileStream, out warnings);
dePloyinG rePorts
Now that you’ve designed your report you can deploy it to users as a part of your application.
However, the Report Viewer control is not a part of the .NET Framework, and thus it needs to be
installed separately. A search for “Report Viewer redistributable” on the Web should help you find
the installer for the Report Viewer assemblies.
An alternative is to simply distribute the Report Viewer assemblies that you have referenced with
your application. Note, however, that this won’t include the .cab installer for the ActiveX control
that, when using the web report viewer control in web applications, will enable reports to be printed
(in IE only). If this is a feature you require in your application then it’s best to use the Report Viewer
redistributable installer instead.
suMMary
In this chapter you’ve seen how to use Visual Studio’s report designer to design a report, populate
it with data, and display the output to the user. Unfortunately, reporting is an incredibly complex
topic, and it is impossible to cover it completely and go through every option available in one
chapter. Hopefully this has been a good introduction to the topic, however, and will guide you in
the right direction for designing your own reports.
PART VII
application services
. chaPter 31: Windows Communication Foundation (WCF)
. chaPter 32: Windows Workflow Foundation (WF)
. chaPter 33: Client Application Services
. chaPter 34: Synchronization Services
. chaPter 35: WCF RIA Services
31
Windows Communication
foundation (WCf)
what’s in this chaPter?
.
Understanding WCF services
.
Creating a WCF service
.
Configuring WCF service endpoints
.
Hosting a WCF service
.
Consuming a WCF service
Most systems require a means to communicate between their various components — most
commonly between the server and the client. Many different technologies enable this sort
of communication, but Windows Communication Foundation (WCF) brings a unified
architecture to implementing them. This chapter takes you through the architecture of
WCF services and how to create, host, and consume WCF services in your system.
what is wcf?
Within the .NET Framework there are a variety of ways that you can communicate