入門:
通常報表打印是一件很棘手的事情,在這裏我們用ReportPrinting library,你可以看到用少量的代碼實現複雜的報表。
報表由文本片段(例如上圖中Birthdays)或者由數據庫(具體的所說是從DataView對象)生成的表格組成。同時也得支持圖片線條等。框架體現處了能夠滿足基本得擴展需求。
如果你想快速瞭解它,看這個文檔就夠了,如果你想了解最新信息,請登陸http://www.mag37.com/projects/Printing/.。
這一節將一步步帶你瞭解ReportPrinting library的使用過程。
Create a DataView:建立數據視圖
首先建一個DataView或者DataTable,下面我們將建一個關於一些名人的出生年月信息的DataTable。
public static DataView GetDataView()
{
DataTable dt = new DataTable("People");
dt.Columns.Add("FirstName", typeof(string));
dt.Columns.Add("LastName", typeof(string));
dt.Columns.Add("Birthdate", typeof(DateTime));
dt.Rows.Add(new Object[] {"Theodore", "Roosevelt",
new DateTime(1858, 11, 27)});
dt.Rows.Add(new Object[] {"Winston", "Churchill",
new DateTime(1874, 11, 30)});
dt.Rows.Add(new Object[] {"Pablo", "Picasso",
new DateTime(1881, 10, 25)});
dt.Rows.Add(new Object[] {"Charlie", "Chaplin",
new DateTime(1889, 4, 16)});
dt.Rows.Add(new Object[] {"Steven", "Spielberg",
new DateTime(1946, 12, 18)});
dt.Rows.Add(new Object[] {"Bart", "Simpson",
new DateTime(1987, 4, 19)});
dt.Rows.Add(new Object[] {"Louis", "Armstrong",
new DateTime(1901, 8, 4)});
dt.Rows.Add(new Object[] {"Igor", "Stravinski",
new DateTime(1882, 6, 17)});
dt.Rows.Add(new Object[] {"Bill", "Gates",
new DateTime(1955, 10, 28)});
dt.Rows.Add(new Object[] {"Albert", "Einstein",
new DateTime(1879, 3, 14)});
dt.Rows.Add(new Object[] {"Marilyn", "Monroe",
new DateTime(1927, 6, 1)});
dt.Rows.Add(new Object[] {"Mother", "Teresa",
new DateTime(1910, 8, 27)});
DataView dv = dt.DefaultView;
return dv;
}
這個函數將會建一個DataTable,包含了一打的名人信息和他們的出生日期
Create a ReportMaker:建一個ReportMaker
接口ReportPrinting.IreportMaker
被用來建一個對象來啓動一個ReportDocument
。一個繼承了IreportMaker
的對象包含一個報表所有的代碼。在例子中,這個類叫做SampleReportMaker1
。
它有一個繼承的方法:
public void MakeDocument(ReportDocument reportDocument)
{
我們一步步的看看這個繼承的方法,首先,他有一個好的創意,重設文本的樣式的類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提供了幾個靜態方法,全局的樣式會應用的所有的文本快上,當然也可以爲某一特定快單獨的設置樣式,就像代碼中的顯示,我們會改變一些字體和顏色。
// Setup the global TextStyles
TextStyle.Heading1.FontFamily = new FontFamily("Comic Sans MS");
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;
// Add some white-space to the page. By adding a 1/10 inch margin
// to the bottom of every line, quite a bit of white space will be added
TextStyle.Normal.MarginBottom = 0.1f;
用此方法非常簡單,我們將得到一個dataview,在一個GUI中設定默認的排序(注意:這裏是一個hack,好的封裝應該是低耦合的對話框,後期設定)
// create a data table and a default view from it.
DataView dv = GetDataView();
// set the sort on the data view
if (myPrintDialog.cmbOrderBy.SelectedItem != null)
{
string str = myPrintDialog.cmbOrderBy.SelectedItem.ToString();
if (myPrintDialog.chkReverse.Checked)
{
str += " DESC";
}
dv.Sort = str;
}
下一步將建一個ReportPrinting.ReportBuilder
的實例,這個對象的任務就是組合文本,數據和包含的段落。
// create a builder to help with putting the table together.
ReportBuilder builder = new ReportBuilder(reportDocument);
用五個重載的函數建頁眉頁腳是非常容易的。下面建了一個簡單的頁眉,Brithdays Report居左,page#居右。頁腳局中。
// Add a simple page header and footer that is the same on all pages.
builder.AddPageHeader("Birthdays Report", String.Empty, "page %p");
builder.AddPageFooter(String.Empty, DateTime.Now.ToLongDateString(),
String.Empty);
現在,真正有意思的纔開始:我們開啓垂直行佈局,因爲所有的後加的元素都依次載先前元素的後面。
builder.StartLinearLayout(Direction.Vertical);
現在:加上兩個文本節,第一個元素被Header1所控制,第二個被Nomal控制。這兩個樣式在前面已經定義過了。
// Add text sections
builder.AddTextSection("Birthdays", TextStyle.Heading1);
builder.AddTextSection("The following are various birthdays of people " +
"who are considered important in history.");
接下來:我們加一個DataTable的元素,第一行是可見的標題行,設定三列,如下所示一次是LastName,FirstName,Birthdate,
AddColumna
方法的第一個參數是是綁定DataTable的列名,第二個參數是顯示的標題,第三個參數是寬,最大寬度在inches中規定,默認的她的尺寸將遵循表頭或者是數據行。在這個case中,false被傳遞,不會呈現自動尺寸。
// Add a data section, then add columns
builder.AddDataSection(dv, true);
builder.AddColumn ("LastName", "Last Name", 1.5f, false, false);
builder.AddColumn ("FirstName", "First Name", 1.5f, false, false);
builder.AddColumn ("Birthdate", "Birthdate", 3.0f, false, false);
我們設置最後一行的格式必須是日期,這裏的格式表達是語String.Format是一樣的。這裏我們定義長日期。
// Set the format expression to this string.
builder.CurrentColumn.FormatExpression = "{0:D}";
最最後的事情就是完成佈局設置:
builder.FinishLinearLayout();
Create a form for printing:爲打印建一個窗體。
這裏只有簡單的幾個控件:標籤,combox,複選框,和一個從ReportPrinting
命名控件調用的用戶控件PrintControl
,
這個窗體也包含一個
ReportPrinting.ReportDocument System.Drawing.Printing.PrintDocument
的實例,它是System.Drawing.Printing.PrintDocument
的子類,如果你是在設計是做這些事情,在構造函數中需要新建一個對象。
private ReportDocument reportDocument;
public ReportPrinting.PrintControl PrintControls;
public System.Windows.Forms.ComboBox cmbOrderBy;
public System.Windows.Forms.CheckBox chkReverse;
public SamplePrintDialog1()
{
InitializeComponent();
this.reportDocument = new ReportDocument();
this.PrintControls.Document = reportDocument;
SampleReportMaker1 reportMaker = new SampleReportMaker1(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下載的代碼中看到,連同一起的還有其他的一些例子。
Report Document Classes
在
ReportPrinting
命名空間中引入了幾個類,他們一起共同完成了報表的工作。下面的uml圖說明了他們之間的關係:三角形表示擴展,黑色菱形表示組合,使用。
ReportDocument
ReportDocument
繼承自PrintDocument
並且自定義了報表的數據,從一個或多個表的數據。 ReportDocument
對象是一個報表中頂級的容器,包含了報表中的所有元素。
ReportDocument
's 的主要工作是打印,靠調用基類的Print()方法實現。 Print()
方法 迭代組成文檔的所有ReportSections
,並打印。
The strategy design pattern is employed for formatting the report. 一個繼承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
對象作爲數據源 (from the .NET System.Data
namespace) 。然後用一些 ReportDataColumns
來表示詳細內容。 這些ReportDataColumns
與 DataGridColumnStyle
類似。
ReportDataColumn
ReportDataColumn
提供了報表行數據格式化的信息。對每一行,都有一個新的ReportDataColumn
被實例並被附加到ReportSection中。立刻,每一行的通過提取數據源描述最寬的顯示在頁面上。
ReportDataColumn
能夠爲頭和記本行啓用一個單獨的 TextStyle
。因此,每一行數據,都可以被不同的格式化。
TextStyle
TextStyle
允許文本選擇樣式和字體 ,允許默認的樣式,所有的樣式都有他們默認的值(除了靜態的TextStyle.Normal
)。如果屬性不被指定,它會一直用默認的值。
例如一個新的樣式能夠定義用默認,但是它可以設定加粗。
ReportBuilder
ReportBuilder
指定了建立一個報表。這個類是你的代碼和庫的主要接口。在許多cases中,你沒有顯示的聲明許多對象,,通過 ReportBuilder
可以爲你建立他們。
對於一個
ReportBuilder
實例,你必須顯式的建立ReportDocument
。讓後你可以自由的使用它Add某某方法爲報告文檔添加片段。
我們已經在上面的實例中看到了這些。
Example:
The following example shows the creation of a report using the ReportBuilder. The following methods would be part of a class that implements
IReportMaker
(this is from example1 in the sample project).
private DataView GetDataView()
{
DataTable dt = new DataTable("People");
dt.Columns.Add("FirstName", typeof(string));
dt.Columns.Add("LastName", typeof(string));
dt.Columns.Add("Birthdate", typeof(DateTime));
dt.Rows.Add(new Object[] {"Theodore", "Roosevelt", new DateTime(1858, 11, 27)});
dt.Rows.Add(new Object[] {"Winston ", "Churchill", new DateTime(1874, 11, 30)});
dt.Rows.Add(new Object[] {"Pablo", "Picasso", new DateTime(1881, 10, 25)});
dt.Rows.Add(new Object[] {"Charlie", "Chaplin", new DateTime(1889, 4, 16)});
dt.Rows.Add(new Object[] {"Steven", "Spielberg", new DateTime(1946, 12, 18)});
dt.Rows.Add(new Object[] {"Bart", "Simpson", new DateTime(1987, 4, 19)});
return dt.DefaultView;
}
public void MakeDocument(ReportDocument reportDocument)
{
// Clear the document
reportDocument.ClearSections();
// create a data table and a default view from it.
DataView dataView = this.GetDataView();
// create a builder to help with putting the table together.
ReportBuilder builder = new ReportBuilder(reportDocument);
// Add a simple page header and footer that is the same on all pages.
builder.AddPageHeader("Birthdays Report", String.Empty, "page %p");
builder.AddPageFooter(String.Empty, DateTime.Now.ToLongDateString(), String.Empty);
builder.StartLinearLayout(Direction.Vertical);
// Add text sections
builder.AddTextSection("Birthdays", TextStyle.Heading1);
builder.AddTextSection("The following are various birthdays of people who "
+ "are considered important in history.");
// Add a data section, then add columns
builder.AddDataSection(dataView, true);
builder.AddColumn ("LastName", "Last Name", 1.5f, false, false);
builder.AddColumn ("FirstName", "First Name", 1.5f, false, false);
builder.AddColumn ("Birthdate", "Birthdate", 3.0f, false, false);
// Set the format expression to this string.
builder.CurrentColumn.FormatExpression = "{0:D}";
builder.FinishLinearLayout();
}
IReportMaker
IreportMaker
在設計上被所爲一個接口來被使用。一個繼承它的對象可以添加 ReportDocument
. 當一個文檔要被打印,它自動的調用單例方法MakeDocument()
. 在上面的例子中顯示一個繼承的方法打印一個報告。
例如,你可以有個應用打印詳細和預覽。報告的邏輯被分離在單獨的類中。每一個都繼承
IReportMaker
的類。你的打印對話框有個打印什麼的momboxbox 允許用戶選擇報告類型, 用它來選擇指定的擴展自的ReportDocument
。
Print Dialogs
打印對話框引導用戶使用打印過程。許多應用有許多設置來影響打印什麼和怎麼打印,許多的windows應用自定義標準的打印對話框,附加一些按鈕來做各種各樣的操作。有一片文章說怎麼用mfc擴展打印對話框,但是我仍然有.net.如果有人做的.net控件看起來象標準的打印對話框,並且很容易的附加到窗體上,或者知道其他的一些辦法,請你告訴我。
PrintControl
To make printing easy for my applications, I created this very basic control that can be dropped onto any form. It gives the user options to setup, preview, submit (ok) or cancel. Providing a preview button and a page setup button on a print dialog are not standard in the windows interface, but I wish they were. So this control provides that functionality to your print dialog. Note, you can still provide access via a File menu (File | Print Preview, File | Page Setup).
The control uses the following dialogs associated with printing:
PrintPreviewDialog
PageSetupDialog
PrintDialog
To use the print control, place it on a form. Set the
Document
property to a valid PrintDocument
. (it doesnt have to just be the ReportDocument
described earlier). Thats it!
You can customize a few things with the following properties:
ShowStatusDialog
- The progress of the print job is shown in a status dialog. Default is true.
PrintInBackground
- Indicates that printing should be done in the background. Default is true.
Printing
- This event is raised prior to printing. It allows user code to setup for printing. (This is useful for dumping data from the GUI to a helper class, for instance).
Print Dialog with PrintControl
Print Dialog with PrintControl
A sample form with a PrintControl is shown below. This dialog allows a user to select tables to print from the Northwind sample database.
佈局
佈局
Linear layout :流佈局
Linear layout :流佈局
The
LinearSections
class is used to print a variety of sections, one below (or next to) another. In its simplest form, the linear layout looks like the figure to the right.
Each section may consume a different amount of real-estate. Some may be as wide as the page, others may not. The
LinearSections
container (shown in color) is a rectangle as wide as the widest section and as tall as all sub-sections combined.
This is the simplest class to use for structuring a report. Simply start a
LinearLayout
with the ReportBuilder
class and then add ReportSections
for text and data as needed.
Layered layout
Layered layout
The layered layout is for combing sections together into one region. This is often used for placing varying
ReportSectionText
objects together in one PageHeader
or PageFooter
. It could also be used to add a watermark to the body of a ReportDocument
.
Column layout
Column layout
As the name suggests, the column layout is used for creating columns on a page. A picture of a document using columns can be found on the samples page.
The column layout contains a vertical
LinearSections
container nested within a horizontal LinearRepeatableSections
container. The vertical container has a maximum width of the column width. It renders as much of its content as possible within a column, then returns to the horizontal container. The horizontal container advances by the width of the column (plus any margin required in the middle) and repeats the call to the vertical container. This gives the parent container the name LinearRepeatableSections
since it will repeat calls to a child section within a single page.
In the example shown, the
LinearRepeatableSections
has a special divider section assigned to it of a vertical line. This divider is printed between calls to the vertical section.
Box section
Box section
The box isn't stricly a container in the class hierarchy sense, since it doesn't inherit from
SectionContainer
. However, it does contain one section which can be assigned to it. And the ReportBuilder
class, when it supports the SectionBox
, will treat it as a container by assigning a layered or linear container to the box.
The box follows many of the properties from the W3C box model. I recommend visiting this page to learn more.
(graphic from w3.org)
The margins are set the same as for any other report section. The margins are always clear. The borders are set by specifying a
System.Drawing.Pen
object to use for each border. The pen object specifies color and width. The padding is specified in inches, again for each side independently.
If a background brush is set (using the
System.Drawing.Brushes
), it will paint the entire area inside the border (including the padding).
The width and height can each be set independently, or not at all. If the width is not specified, then the width will size to that of the contents. If the width is explictly set using the
Width
property, that width includes content, padding, border, and margins. The width can also be set as a percentage of the parent (using the WidthPercent
property). The same is true for the Height
and HeightPercent
properties.
An offset can be set which moves the box in relation to the parent and normal flow. This should normally be used in
LayeredLayout
, as the results in LinearLayout
haven't been adequately tested.
結束語
結束語
以上總結了ReportPrinting庫的使用,瞭解更多請到網站: http://www.mag37.com/projects/Printing/.
版本
版本
06-9-2003 : 最初版本
11-9-2003 : 修正版本
作者:Mike Mayer
作者:Mike Mayer