您的位置:首页 > 其它

优秀开源项目介绍:Printing Reports in .NET

2007-07-20 14:22 639 查看
PrintingReportsin.NET-StepbyStepIntroduction
作者:mag37



入门:
通常报表打印是一件很棘手的事情,在这里我们用ReportPrintinglibrary,你可以看到用少量的代码实现复杂的报表。
报表由文本片段(例如上图中Birthdays)或者由数据库(具体的所说是从DataView对象)生成的表格组成。同时也得支持图片线条等。框架体现处了能够满足基本得扩展需求。
如果你想快速了解它,看这个文档就够了,如果你想了解最新信息,请登陆http://www.mag37.com/projects/Printing/.。

这一节将一步步带你了解ReportPrintinglibrary的使用过程。

CreateaDataView:建立数据视图

首先建一个DataView或者DataTable,下面我们将建一个关于一些名人的出生年月信息的DataTable。
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",

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;

}

这个函数将会建一个DataTable,包含了一打的名人信息和他们的出生日期

CreateaReportMaker:建一个ReportMaker

接口
ReportPrinting.IreportMaker
被用来建一个对象来启动一个
ReportDocument
。一个继承了
IreportMaker
的对象包含一个报表所有的代码。在例子中,这个类叫做
SampleReportMaker1
它有一个继承的方法:
publicvoidMakeDocument(ReportDocumentreportDocument)

{

我们一步步的看看这个继承的方法,首先,他有一个好的创意,重设文本的样式的类
TextStyle
,TextStyle提供了一个全局样式和字体格式。例如标题,页头页脚,主体等等。这个类的应用是非常广泛的,它在报表创建时显式的重设。
TextStyle.ResetStyles();

下一步将设置页面间距:
//
设置一个默认的文档间距
(
单位是
1/100
英寸
)

reportDocument.DefaultPageSettings.Margins.Top=50;

reportDocument.DefaultPageSettings.Margins.Bottom=50;

reportDocument.DefaultPageSettings.Margins.Left=75;

reportDocument.DefaultPageSettings.Margins.Right=75;

如上可以看出,TextStyle提供了几个静态方法,全局的样式会应用的所有的文本快上,当然也可以为某一特定快单独的设置样式,就像代码中的显示,我们会改变一些字体和颜色。
//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;

//Addsomewhite-spacetothepage.Byaddinga1/10inchmargin

//tothebottomofeveryline,quiteabitofwhitespacewillbeadded

TextStyle.Normal.MarginBottom=0.1f;

用此方法非常简单,我们将得到一个dataview,在一个GUI中设定默认的排序(注意:这里是一个hack,好的封装应该是低耦合的对话框,后期设定)
//createadatatableandadefaultviewfromit.

DataViewdv=GetDataView();

//setthesortonthedataview

if(myPrintDialog.cmbOrderBy.SelectedItem!=null)

{

stringstr=myPrintDialog.cmbOrderBy.SelectedItem.ToString();

if(myPrintDialog.chkReverse.Checked)

{

str+="DESC";

}

dv.Sort=str;

}


下一步将建一个
ReportPrinting.ReportBuilder
的实例,这个对象的任务就是组合文本,数据和包含的段落。
//createabuildertohelpwithputtingthetabletogether.

ReportBuilderbuilder=newReportBuilder(reportDocument);

用五个重载的函数建页眉页脚是非常容易的。下面建了一个简单的页眉,BrithdaysReport居左,page#居右。页脚局中。
//Addasimplepageheaderandfooterthatisthesameonallpages.

builder.AddPageHeader("BirthdaysReport",String.Empty,"page%p");

builder.AddPageFooter(String.Empty,DateTime.Now.ToLongDateString(),

String.Empty);

现在,真正有意思的才开始:我们开启垂直行布局,因为所有的后加的元素都依次载先前元素的后面。
builder.StartLinearLayout(Direction.Vertical);


现在:加上两个文本节,第一个元素被Header1所控制,第二个被Nomal控制。这两个样式在前面已经定义过了。
//Addtextsections

builder.AddTextSection("Birthdays",TextStyle.Heading1);

builder.AddTextSection("Thefollowingarevariousbirthdaysofpeople"+

"whoareconsideredimportantinhistory.");


接下来:我们加一个DataTable的元素,第一行是可见的标题行,设定三列,如下所示一次是LastName,FirstName,Birthdate,
AddColumna
方法的第一个参数是是绑定DataTable的列名,第二个参数是显示的标题,第三个参数是宽,最大宽度在inches中规定,默认的她的尺寸将遵循表头或者是数据行。在这个case中,false被传递,不会呈现自动尺寸。
//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);

我们设置最后一行的格式必须是日期,这里的格式表达是语String.Format是一样的。这里我们定义长日期。
//Settheformatexpressiontothisstring.
builder.CurrentColumn.FormatExpression="{0:D}";
最最后的事情就是完成布局设置:
builder.FinishLinearLayout();

Createaformforprinting:为打印建一个窗体。

这里只有简单的几个控件:标签,combox,复选框,和一个从
ReportPrinting
命名控件调用的用户控件
PrintControl





这个窗体也包含一个
ReportPrinting.ReportDocumentSystem.Drawing.Printing.PrintDocument
的实例,它是[code]System.Drawing.Printing.PrintDocument的子类,如果你是在设计是做这些事情,在构造函数中需要新建一个对象。
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");
this.cmbOrderBy.Items.Add("Birthdate");
}
在这个构造函数中,一个ReportDocument被建立,它的实例指派给PrintControl.Document属性。上面的SampleMaker1对象实例化后指派给reportDocument.ReportMaker。最后用了一些代码设置了一些Combobox。
大功告成
上面的代码就可以打印一份简单清晰的报表了,当然,你也可以用
PrintDialog
,
PrintPreview
,和
PageSettings
对话框
注意没有用PrintControls用户控件。

这个简单的例子可以在ReportPrint下载的代码中看到,连同一起的还有其他的一些例子。

ReportDocumentClasses

ReportPrinting
命名空间中引入了几个类,他们一起共同完成了报表的工作。下面的uml图说明了他们之间的关系:三角形表示扩展,黑色菱形表示组合,使用。

ReportDocument

ReportDocument
继承自
PrintDocument
并且自定义了报表的数据,从一个或多个表的数据。
ReportDocument
对象是一个报表中顶级的容器,包含了报表中的所有元素。

ReportDocument
's的主要工作是打印,靠调用基类的Print()方法实现。
Print()
方法迭代组成文档的所有
ReportSections
,并打印。

Thestrategydesignpatternisemployedforformattingthereport.一个继承IreportMaker的对象可以关联到
ReportDocument
.
IReportMaker
对象是个专门的应用并且已知的在它上面建立了应用状态和客户设定。这个对象负责建立的节点sections,关联dataView,应用在TextStyles中定义的样式。它一般指派给
ReportBuilder
用于构建负责的报表.

ReportSection

ReportSection
是一个代表报表的可打印节点的抽象类,它有几个子类,包括
ReportSectionText
(代表文本字符串)和
ReportSectionData
(代表
DataView
).也包括容器类节点(衍生自
SectionContainer
类,继承自
ReportSection
).容器类可以包括子报表节点.我们快速的看一个例子来看看他是怎么工作的。

在示例中,这个文章的顶部,又一段文本在表数据的后面(事实上有两处文字,一处在头里嵌入了一个页眉,但是我们现在忽略它)。我们建一个
ReportSectionText
对象打印一段文本,并且
ReportSectionData
对象打印表数据。在添加的这些
ReportSections
ReportDocument
时,我们必须建一个容器。我们将会建一个
LinearSections
容器,容纳者两个节点,

这个容器然后建立到ReportDocument中,在文档打印时,这个节点容器首先打印ReportSectionText,然后是ReportSectionData。简单的一个一个的打印节点在上面的报表程序中,但是有许多其他的方法设定这些类。

SectionContainer

这个抽象类定义了节点的容器。有两种类型:
LinearSections
LayeredSections
.

LinearSections

LinearSections
类是
SectionContainer
的子类。也是
ReportSection
子类
.所以,
LinearSections
能够被作为可打印的节点。同时,它也是一个或多个节点的容器。

象它的名字所暗示的,他是一个直线布局的节点。也就是说,在行或列中。一个名叫
Direction
的属性指定了容器是横行或纵向布局。

LayeredSections

LayeredSections
类也是的
SectionContainer
子类,又是一个
ReportSection
子类。因此它既可以被打印,有是其他元素的容器。

她的子对象都被画到它上面,第一个加上去的位于低层,后来的在前面的上面依次排队。

ReportSectionText

ReportSectionText
打印一个字符串在页面上。两个公共的殊相用来开始一个节点。Text用来指定打印的字符.
TextStyle
,描述了字体,颜色,布局,以及文本的其他属性。

ReportSectionData

ReportSectionData
打印表的数据。它用一个
DataView
对象作为数据源(fromthe.NET
System.Data
namespace)。然后用一些
ReportDataColumns
来表示详细内容。这些
ReportDataColumns
DataGridColumnStyle
类似。

ReportDataColumn

ReportDataColumn
提供了报表行数据格式化的信息。对每一行,都有一个新的
ReportDataColumn
被实例并被附加到ReportSection中。立刻,每一行的通过提取数据源描述最宽的显示在页面上。

ReportDataColumn
能够为头和记本行启用一个单独的
TextStyle
。因此,每一行数据,都可以被不同的格式化。

TextStyle

TextStyle
允许文本选择样式和字体,允许默认的样式,所有的样式都有他们默认的值(除了静态的
TextStyle.Normal
)。如果属性不被指定,它会一直用默认的值。

例如一个新的样式能够定义用默认,但是它可以设定加粗。

TextStyleparagraphStyle=newTextStyle(TextStyle.Normal);

paragraphStyle.Bold=true;

TextStyle.Normal.Size+=1.0f

ReportBuilder

ReportBuilder
指定了建立一个报表。这个类是你的代码和库的主要接口。在许多cases中,你没有显示的声明许多对象,,通过
ReportBuilder
可以为你建立他们。

对于一个
ReportBuilder
实例,你必须显式的建立
ReportDocument
。让后你可以自由的使用它Add某某方法为报告文档添加片段。

我们已经在上面的实例中看到了这些。

Example:

ThefollowingexampleshowsthecreationofareportusingtheReportBuilder.Thefollowingmethodswouldbepartofaclassthatimplements
IReportMaker
(thisisfromexample1inthesampleproject).

privateDataViewGetDataView()

{

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",newDateTime(1987,4,19)});

returndt.DefaultView;

}

publicvoidMakeDocument(ReportDocumentreportDocument)

{

//Clearthedocument

reportDocument.ClearSections();

//createadatatableandadefaultviewfromit.

DataViewdataView=this.GetDataView();

//createabuildertohelpwithputtingthetabletogether.

ReportBuilderbuilder=newReportBuilder(reportDocument);


//Addasimplepageheaderandfooterthatisthesameonallpages.

builder.AddPageHeader("BirthdaysReport",String.Empty,"page%p");

builder.AddPageFooter(String.Empty,DateTime.Now.ToLongDateString(),String.Empty);

builder.StartLinearLayout(Direction.Vertical);

//Addtextsections

builder.AddTextSection("Birthdays",TextStyle.Heading1);

builder.AddTextSection("Thefollowingarevariousbirthdaysofpeoplewho"

+"areconsideredimportantinhistory.");

//Addadatasection,thenaddcolumns

builder.AddDataSection(dataView,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);

//Settheformatexpressiontothisstring.

builder.CurrentColumn.FormatExpression="{0:D}";

builder.FinishLinearLayout();


}


IReportMaker

IreportMaker
在设计上被所为一个接口来被使用。一个继承它的对象可以添加R
eportDocument
.当一个文档要被打印,它自动的调用单例方法
MakeDocument()
.在上面的例子中显示一个继承的方法打印一个报告。

例如,你可以有个应用打印详细和预览。报告的逻辑被分离在单独的类中。每一个都继承
IReportMaker
的类。你的打印对话框有个打印什么的momboxbox允许用户选择报告类型,用它来选择指定的扩展自的
ReportDocument


PrintDialogs

打印对话框引导用户使用打印过程。许多应用有许多设置来影响打印什么和怎么打印,许多的windows应用自定义标准的打印对话框,附加一些按钮来做各种各样的操作。有一片文章说怎么用mfc扩展打印对话框,但是我仍然有.net.如果有人做的.net控件看起来象标准的打印对话框,并且很容易的附加到窗体上,或者知道其他的一些办法,请你告诉我。

PrintControl

Tomakeprintingeasyformyapplications,Icreatedthisverybasiccontrolthatcanbedroppedontoanyform.Itgivestheuseroptionstosetup,preview,submit(ok)orcancel.Providingapreviewbuttonandapagesetupbuttononaprintdialogarenotstandardinthewindowsinterface,butIwishtheywere.Sothiscontrolprovidesthatfunctionalitytoyourprintdialog.Note,youcanstillprovideaccessviaaFilemenu(File|PrintPreview,File|PageSetup).





Figure3-PrintControlusercontrol
Thecontrolusesthefollowingdialogsassociatedwithprinting:

PrintPreviewDialog


PageSetupDialog


PrintDialog


Tousetheprintcontrol,placeitonaform.Setthe
Document
propertytoavalid
PrintDocument
.(itdoesnthavetojustbethe
ReportDocument
describedearlier).Thatsit!

Youcancustomizeafewthingswiththefollowingproperties:

ShowStatusDialog
-Theprogressoftheprintjobisshowninastatusdialog.Defaultistrue.

PrintInBackground
-Indicatesthatprintingshouldbedoneinthebackground.Defaultistrue.

Printing
-Thiseventisraisedpriortoprinting.Itallowsusercodetosetupforprinting.(ThisisusefulfordumpingdatafromtheGUItoahelperclass,forinstance).

PrintDialogwithPrintControl

AsampleformwithaPrintControlisshownbelow.ThisdialogallowsausertoselecttablestoprintfromtheNorthwindsampledatabase.



Figure4-Adialogtopromptuserforprintsettingsandgivethemachancetopreviewandsetupthepage.

布局

Linearlayout:流布局

The
LinearSections
classisusedtoprintavarietyofsections,onebelow(ornextto)another.Initssimplestform,thelinearlayoutlookslikethefiguretotheright.

Eachsectionmayconsumeadifferentamountofreal-estate.Somemaybeaswideasthepage,othersmaynot.The
LinearSections
container(shownincolor)isarectangleaswideasthewidestsectionandastallasallsub-sectionscombined.

Thisisthesimplestclasstouseforstructuringareport.Simplystarta
LinearLayout
withthe
ReportBuilder
classandthenadd
ReportSections
fortextanddataasneeded.



Layeredlayout

Thelayeredlayoutisforcombingsectionstogetherintooneregion.Thisisoftenusedforplacingvarying
ReportSectionText
objectstogetherinone
PageHeader
or
PageFooter
.Itcouldalsobeusedtoaddawatermarktothebodyofa
ReportDocument
.



Columnlayout

Asthenamesuggests,thecolumnlayoutisusedforcreatingcolumnsonapage.Apictureofadocumentusingcolumnscanbefoundonthesamplespage.

Thecolumnlayoutcontainsavertical
LinearSections
containernestedwithinahorizontal
LinearRepeatableSections
container.Theverticalcontainerhasamaximumwidthofthecolumnwidth.Itrendersasmuchofitscontentaspossiblewithinacolumn,thenreturnstothehorizontalcontainer.Thehorizontalcontaineradvancesbythewidthofthecolumn(plusanymarginrequiredinthemiddle)andrepeatsthecalltotheverticalcontainer.Thisgivestheparentcontainerthename
LinearRepeatableSections
sinceitwillrepeatcallstoachildsectionwithinasinglepage.

Intheexampleshown,the
LinearRepeatableSections
hasaspecialdividersectionassignedtoitofaverticalline.Thisdividerisprintedbetweencallstotheverticalsection.

Boxsection

Theboxisn'tstriclyacontainerintheclasshierarchysense,sinceitdoesn'tinheritfrom
SectionContainer
.However,itdoescontainonesectionwhichcanbeassignedtoit.Andthe
ReportBuilder
class,whenitsupportsthe
SectionBox
,willtreatitasacontainerbyassigningalayeredorlinearcontainertothebox.

TheboxfollowsmanyofthepropertiesfromtheW3Cboxmodel.Irecommendvisitingthispagetolearnmore.



[/code]

(graphicfromw3.org)
Themarginsaresetthesameasforanyotherreportsection.Themarginsarealwaysclear.ThebordersaresetbyspecifyingaSystem.Drawing.Pen
objecttouseforeachborder.Thepenobjectspecifiescolorandwidth.Thepaddingisspecifiedininches,againforeachsideindependently.

Ifabackgroundbrushisset(usingthe[code]System.Drawing.Brushes),itwillpainttheentireareainsidetheborder(includingthepadding).

Thewidthandheightcaneachbesetindependently,ornotatall.Ifthewidthisnotspecified,thenthewidthwillsizetothatofthecontents.Ifthewidthisexplictlysetusingthe
Width
property,thatwidthincludescontent,padding,border,andmargins.Thewidthcanalsobesetasapercentageoftheparent(usingthe
WidthPercent
property).Thesameistrueforthe
Height
and
HeightPercent
properties.

Anoffsetcanbesetwhichmovestheboxinrelationtotheparentandnormalflow.Thisshouldnormallybeusedin
LayeredLayout
,astheresultsin
LinearLayout
haven'tbeenadequatelytested.

结束语

以上总结了ReportPrinting库的使用,了解更多请到网站:http://www.mag37.com/projects/Printing/.

版本

06-9-2003:最初版本

11-9-2003:修正版本

作者:MikeMayer



[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: