Vous êtes sur la page 1sur 13

9/17/2015

PrintingReportsin.NETCodeProject

Articles Desktop Development Printing General

Printing Reports in .NET


Mike Mayer, 27 Aug 2008

CPOL

315.9K

12K

256

4.84 66 votes
Using the library presented, you can print reports from C# and other .NET languages

Download source ver 0.7 282.6 KB

http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

1/13

9/17/2015

PrintingReportsin.NETCodeProject

Introduction
Printing a document programmatically can be quite involved. Using the ReportPrinting library presented here, you'll be able to print reports with multiple sections with very
little code.
The reports are comprised of plain text sections such as the title "Birthdays", and the other paragraphs and grids of data from a database more specifically, from a DataView
object. Lines and images using the .NET Image class and boxes like the CSS box model are also supported. The framework presented can easily be extended to handle many
other primitives.
I do not plan to keep this page uptodate with the latest code and documentation. This will just be a quick presentation of what you can do and the classes involved. For the latest
version, see here. However, this project is not actively being developed, so use at your own risk. This was all written for the .NET 1.1 Framework, and I know a lot of new namespaces
were added in .NET 2.0 that may make a lot of this obsolete. It solved a problem I had at the time.

Step-by-Step
This section will take you through the process of using the ReportPrinting library, step by step.

Create a DataView
The first step is to create a DataTable and DataView that serves as the source of data. The following code will create a table of some famous birthdays:
publicstaticDataViewGetDataView()
{
DataTabledt=newDataTable("People");
dt.Columns.Add("FirstName",typeof(string));
dt.Columns.Add("LastName",typeof(string));
dt.Columns.Add("Birthdate",typeof(DateTime));
dt.Rows.Add(newObject[]{"Theodore","Roosevelt",
newDateTime(1858,11,27)});
dt.Rows.Add(newObject[]{"Winston","Churchill",
newDateTime(1874,11,30)});
dt.Rows.Add(newObject[]{"Pablo","Picasso",
newDateTime(1881,10,25)});
dt.Rows.Add(newObject[]{"Charlie","Chaplin",
newDateTime(1889,4,16)});
dt.Rows.Add(newObject[]{"Steven","Spielberg",
newDateTime(1946,12,18)});
dt.Rows.Add(newObject[]{"Bart","Simpson",
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

2/13

9/17/2015

PrintingReportsin.NETCodeProject

newDateTime(1987,4,19)});
dt.Rows.Add(newObject[]{"Louis","Armstrong",
newDateTime(1901,8,4)});
dt.Rows.Add(newObject[]{"Igor","Stravinski",
newDateTime(1882,6,17)});
dt.Rows.Add(newObject[]{"Bill","Gates",
newDateTime(1955,10,28)});
dt.Rows.Add(newObject[]{"Albert","Einstein",
newDateTime(1879,3,14)});
dt.Rows.Add(newObject[]{"Marilyn","Monroe",
newDateTime(1927,6,1)});
dt.Rows.Add(newObject[]{"Mother","Teresa",
newDateTime(1910,8,27)});
DataViewdv=dt.DefaultView;
returndv;
}
This function will return a DataView for a table of a dozen famous individuals and their birthdays.

Create a ReportMaker
The ReportPrinting.IReportMaker interface is used to create objects that setup a ReportDocument. That is, an object that implements IReportMaker will contain
all the code necessary to make a report. For this example, it is a class called SampleReportMaker1. It has one method to implement:
publicvoidMakeDocument(ReportDocumentreportDocument)
{
Let's take a look at the implementation of this method stepbystep. First, it is a good idea to reset the TextStyle class. TextStyle provides global styles for the formatting of
text such as heading, normal paragraphs, headers, footers, etc. Since the scope of this class is application wide, it should be reset to a known state at the beginning of report
generation.
TextStyle.ResetStyles();
Next, setup the default margins for the document, if desired.
//Setupdefaultmarginsforthedocument(unitsof1/100inch)
reportDocument.DefaultPageSettings.Margins.Top=50;
reportDocument.DefaultPageSettings.Margins.Bottom=50;
reportDocument.DefaultPageSettings.Margins.Left=75;
reportDocument.DefaultPageSettings.Margins.Right=75;
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

3/13

9/17/2015

PrintingReportsin.NETCodeProject

As mentioned, the TextStyle class has several static, global styles that can be applied to different text blocks. These styles can each be customized. We'll change some fonts and
colors just to show what's possible.
//SetuptheglobalTextStyles
TextStyle.Heading1.FontFamily=newFontFamily("ComicSansMS");
TextStyle.Heading1.Brush=Brushes.DarkBlue;
TextStyle.Heading1.SizeDelta=5.0f;
TextStyle.TableHeader.Brush=Brushes.White;
TextStyle.TableHeader.BackgroundBrush=Brushes.DarkBlue;
TextStyle.TableRow.BackgroundBrush=Brushes.AntiqueWhite;
TextStyle.Normal.Size=12.0f;
//Addsomewhitespacetothepage.Byaddinga1/10inchmargin
//tothebottomofeveryline,quiteabitofwhitespacewillbeadded
TextStyle.Normal.MarginBottom=0.1f;
Using our method defined earlier, we'll get a dataview and set the default sort based on the values setup in a GUI. Note, this is a hack. Better encapsulation should be used to
isolate the dialog, defined later, from this class.
//createadatatableandadefaultviewfromit.
DataViewdv=GetDataView();
//setthesortonthedataview
if(myPrintDialog.cmbOrderBy.SelectedItem!=null)
{
stringstr=myPrintDialog.cmbOrderBy.SelectedItem.ToString();
if(myPrintDialog.chkReverse.Checked)
{
str+="DESC";
}
dv.Sort=str;
}
The next step is creating an instance of the ReportPrinting.ReportBuilder class. This object is used to simplify the task of piecing together text, data, and the container
sections that they go into these classes for text, data and sections are described in more detail later in this article.
//createabuildertohelpwithputtingthetabletogether.
ReportBuilderbuilder=newReportBuilder(reportDocument);
Creating a header and footer are quite easy with the builder class's five overloaded functions. The one below creates a simple header with text on the left side Birthdays Report and
on the right side page #. The footer has the date centered.
//Addasimplepageheaderandfooterthatisthesameonallpages.
builder.AddPageHeader("BirthdaysReport",String.Empty,"page%p");
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

4/13

9/17/2015

PrintingReportsin.NETCodeProject

builder.AddPageFooter(String.Empty,DateTime.Now.ToLongDateString(),
String.Empty);
Now the real fun begins: we start a vertical, linear layout because every section from here should be added below the preceding section.
builder.StartLinearLayout(Direction.Vertical);
Now add two text sections. The first section added will be a heading as defined by TextStyle.Heading1. The second section is just normal text as defined by the
TextStyle.Normal.
//Addtextsections
builder.AddTextSection("Birthdays",TextStyle.Heading1);
builder.AddTextSection("Thefollowingarevariousbirthdaysofpeople"+
"whoareconsideredimportantinhistory.");
Next, we add a section of a data table. The first line adds a data section with a visible header row. Then three column descriptors are added. These are added in the order that the
columns are displayed. That is, LastName will be the first column, followed by FirstName, followed by Birthdate.
The first parameter passed to AddColumn is the name of the column in the underlying DataTable. The second parameter is the string as printed in the header row. The last
three parameters describe the widths used. A maxwidth can be specified in inches. Optionally, the width can be autosized based on the header row and/or the data rows. In this
case, with falsebeing passed, no autosizing is performed.
//Addadatasection,thenaddcolumns
builder.AddDataSection(dv,true);
builder.AddColumn("LastName","LastName",1.5f,false,false);
builder.AddColumn("FirstName","FirstName",1.5f,false,false);
builder.AddColumn("Birthdate","Birthdate",3.0f,false,false);
We set a format expression for the last column added the date column. These format expressions are identical to those used by String.Format. This makes the date show up in
long format.
//Settheformatexpressiontothisstring.
builder.CurrentColumn.FormatExpression="{0:D}";
And the very last thing is to finish the LinearLayout that was started earlier.
builder.FinishLinearLayout();
}

http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

5/13

9/17/2015

PrintingReportsin.NETCodeProject

Create a Form for Printing


There are only a handful of controls on the following form: a label, a combo box, a check box, and a usercontrol from ReportPrinting namespace called PrintControl. This
control has the four buttons you see at the bottom of the form.

This form also has an instance of the ReportPrinting.ReportDocument class. This is a subclass of System.Drawing.Printing.PrintDocument. If you create the
above form in a designer, here is the constructor required to create a new ReportDocument object.
privateReportDocumentreportDocument;
publicReportPrinting.PrintControlPrintControls;
publicSystem.Windows.Forms.ComboBoxcmbOrderBy;
publicSystem.Windows.Forms.CheckBoxchkReverse;
publicSamplePrintDialog1()
{
InitializeComponent();
this.reportDocument=newReportDocument();
this.PrintControls.Document=reportDocument;
SampleReportMaker1reportMaker=newSampleReportMaker1(this);
this.reportDocument.ReportMaker=reportMaker;
this.cmbOrderBy.Items.Clear();
this.cmbOrderBy.Items.Add("FirstName");
this.cmbOrderBy.Items.Add("LastName");
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

6/13

9/17/2015

PrintingReportsin.NETCodeProject

this.cmbOrderBy.Items.Add("Birthdate");
}
In this constructor, an instance of ReportDocument is created. This instance is assigned to the PrintControls.Document property. A SampleReportMaker1 object
defined above is then instantiated and assigned to the ReportDocument's ReportMaker property. The final bit of the constructor simply sets up the ComboBox.

You're Finished
The above code prints a fairly simple document. Just to note, you can use the standard PrintDialog, PrintPreview, and PageSettings dialogs without using the
PrintControls usercontrol.
This entire sample can be found in the download of the ReportPrinting library, along with many other tests that I have created most are boring to look at, but test various
random settings.

Report Document Classes


There are several classes introduced into the ReportPrinting namespace. They work together for the printing of the above report in addition to all the .NET Framework base
classes that are used. Here is a quasiUML diagram that shows the relationship between these classes. An open triangle is generalization i.e. it points to the superclass in the
inheritance chain. The black diamonds are composite i.e. show that one class instantiates members of another class. The dashedlines are dependency i.e. it uses the class.

http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

7/13

9/17/2015

PrintingReportsin.NETCodeProject

ReportDocument
ReportDocument extends from PrintDocument and is customized for printing reports from one or more tables of data. A ReportDocument object is the toplevel
container for all the sections that make up the report. This consists of a header, body, and footer.
The ReportDocument's main job is printing, which occurs when the Print() method is called of the base class. The Print() method iterates through all the
ReportSections making up the document, printing each one.
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

8/13

9/17/2015

PrintingReportsin.NETCodeProject

The strategy design pattern is employed for formatting the report. An object implementing IReportMaker may be associated with the ReportDocument. This
IReportMaker object is application specific and knows how to create a report based on application state and user settings. This object would be responsible for creating sections,
associating DataViews, and applying any required styles through use of the TextStyle class. It will generally use the ReportBuilder class to assist with the complexity of
building a report.

ReportSection
ReportSection is an abstract class that represents a printable section of a report. There are several subclasses of ReportSection, including ReportSectionText which
represents a string of text and ReportSectionData which represents a printable DataView. There are also container sections which derive from SectionContainer
class, which in turn derives from ReportSection. These containers hold child ReportSection objects also known as subsections to be printed. Lets take a quick look at
how this might work with an example.
In the sample report shown at the top of this article, there is a paragraph of text followed by a table of data. There are actually two paragraphs of text, one of which is a heading. Plus
there is a page header, but we'll ignore all that for now. We would create a ReportSectionText object to print the paragraph of text and a ReportSectionData object to
print the table of data. To add both of these ReportSections to the ReportDocument, we must create a container. We would create a LinearSections container to hold
these two sections. This container is then made the body of the ReportDocument. When the document is printed, the section container will first print the
ReportSectionText, and then below that, it will print the ReportSectionData. Simply printing each section below the preceding one will result in the finished report. But
there are many other ways to set up these classes.

SectionContainer
This abstract class defines a container of sections. There are two types provided with the framework: LinearSections and LayeredSections.

LinearSections
The LinearSections class is a subclass of SectionContainer, which is a subclass of ReportSection. Therefore, the LinearSections can be thought of as "a
printable section of a report." However, it is also a container of one or more sections.
As its name implies, it lays sections out linearly that is, in a row or in a column. A property named Direction specifies if this container will layout sections going down the page
typical or across the page not as typical.

LayeredSections
The LayeredSections class is also a subclass of SectionContainer, which is a subclass of ReportSection. Therefore, the LayeredSections can be thought of as
"a printable section of a report." It is also a container of one or more sections.
The child sections of a LayeredSections object are all painted on top of one another creating layers. The first section added to a LayeredSections object is the bottom
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

9/13

9/17/2015

PrintingReportsin.NETCodeProject

layer. Subsequent ReportSection objects added to the LayeredSections object will be shown on top of each other.

ReportSectionText
The ReportSectionText prints a string to the page. Two public properties are used to setup this section. Text is used to specify the string to print. TextStyle,
described later, sets the font, color, alignment and other properties for how the text is printed.
It is interesting to note that the string specified for this section can be just one word, or many paragraphs of text.

ReportSectionData
The ReportSectionData prints a table of data. It uses a DataView object from the .NET System.Data namespace as the source of data. It then uses a series of
ReportDataColumns to provide the formatting details. These ReportDataColumns are similar to the DataGridColumnStyle class.

ReportDataColumn
The ReportDataColumn provides the necessary information for formatting data for a column of a report. For every column to be presented within a section of data, a new
ReportDataColumn object is instantiated and added to the ReportSection. At a minimum, each column describes a source field from the DataSource that is, a column
name from the DataView and a maximum width on the page.
The ReportDataColumn can be setup with its own unique TextStyle for both header and normal rows. Therefore, each column's data can be formatted differently e.g. an
important column could be bold and red. The TextStyle is also used to set the horizontal alignment justification.

TextStyle
The TextStyle class allows styles and fonts to be added to text selectively, allowing default styles to be used when not explicitly set. All styles except for the static
TextStyle.Normal have another style as their "default" style. Until a property is set like bold, underline, size, font family, etc, a TextStyle object always uses the
corresponding value from its default or parent style.
For example, a new style can be defined using Normal as its default, but setting bold.
TextStyleparagraphStyle=newTextStyle(TextStyle.Normal);
paragraphStyle.Bold=true;
It will have all the same properties as TextStyle.Normal, except it will be bold. A later change to Normal such as below will have the effect of increasing the size of both
styles Normal and paragraphStyle.

http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

10/13

9/17/2015

PrintingReportsin.NETCodeProject

TextStyle.Normal.Size+=1.0f

ReportBuilder
ReportBuilder assists with the building of a report. This class is the main interface between your code and the ReportPrinting library. In many cases, you will never
explicitly create any of the above objects. Instead, the ReportBuilder will create them for you.
To instantiate a ReportBuilder, you must provide the ReportDocument to be built. Then you can call its various Add methods to sequentially add pieces to a report
document.
We've already seen an example of using ReportBuilder above.

IReportMaker
IReportMaker is an interface used to implement the strategy design pattern. An object that implements IReportMaker can be added to a ReportDocument. When the
document is about to be printed, it automatically calls the single method MakeDocument(). The above example shows an implementation of that method to print a onepage
report.
For example, you could have an application that can print either detailed reports or a shorter overview. The logic to make each of these reports would be located in separate classes.
Each class would implement the IReportMaker interface. Your print dialog could have a "Print What" combo box to allow the user to select the type of report, and use the
selection in the combo box to associate the correct implementation of IReportMaker with the ReportDocument.

Conclusion
That summarizes the ReportPrinting library.

Revision History
06Sep2003
Initial version of article posted with version 0.32 of the ReportPrinting library
01Sep2003
An updated version of code
24Aug2008
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

11/13

9/17/2015

PrintingReportsin.NETCodeProject

Removed two dead links


Updated code to the latest known good code from Sourceforge
Added a little bit of text that this is no longer being actively worked on
Ran through Visual Studio's reformat document to clean up some HTML

License
This article, along with any associated source code and files, is licensed under The Code Project Open License CPOL

Share
About the Author
Mike Mayer
United States
No Biography provided

You may also be interested in...


Easily turn a ListView into a nicely printed report,
complete with print preview
http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

Common Cassandra Data Modeling Traps

12/13

9/17/2015

PrintingReportsin.NETCodeProject

Printing support functions

Red Hat JBoss BPM Suite 6 Compared to Pegasystems


Pega 7 BPM

Printing a Form in a report fashion (release 2.2)

Red Hat JBoss BPM Suite 6 compared to Appian 7


BPM Suite

Comments and Discussions


91 messages have been posted for this article Visit http://www.codeproject.com/Articles/4934/PrintingReportsinNET to post and view comments on this article, or click
here to get a print view with messages.
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1509010.1 | Last Updated 27 Aug 2008

Select Language

http://www.codeproject.com/Articles/4934/PrintingReportsinNET?display=Print

Article Copyright 2003 by Mike Mayer


Everything else Copyright CodeProject, 19992015

13/13

Vous aimerez peut-être aussi