程序
# 程序
程序是一种用高级语言编写的程式,它是报表的一部分。报表运行时,程序也会运行。程序可以使用FastReport核心的正常操作无法处理数据;例如,程序可以根据预定义的条件隐藏冗余数据。程序还可用于控制作为报表一部分的对话表单的属性。
程序使用程序引擎支持的一种语言(FastScript)编写。
FastScript引擎支持以下功能:
- 标准语言集:变量,常量,过程,函数(可以嵌套并具有变量,常量,默认参数),所有标准
- 运算符(包括
case
,try
,finally
,except
,with
),类型(整数,分数,逻辑,字符,行,多维数组,变体),类(包含方法,事件,属性,索引和默认属性) - 类型兼容性检查
- 访问任何报表的对象
但是,FastScript不支持以下内容:
- 这些类型的声明:记录,类
- 指针,集合(但'IN'运算符可用于表达式,例如“a'['a'...'c','d']中的a”)
- 短字符型
- 无条件跳跃(
GOTO
)
可以在FastReport设计器中创建程序,该设计器包含段语法突出显示的程序编辑器。还有一个嵌入式调试器,它具有以下功能:“Step”,“Breakpoint”,“Run to cursor”和“Evaluate”。
# 1. 程序初试
程序的工具位于FastReport设计器的“代码”选项卡中。切换到此选项卡时,界面如下所示:

上述标签的说明如下:
“代码”标签
程序编辑器窗格
用于选择编写程序的语言的下拉列表
调试工具栏
图标 说明 在调试模式下运行报表F9 运行到光标F4 追踪至,执行一行F7 结束程序Ctrl+F2 表达式求值Ctrl+F7 放置断点F5 观察面板,输出调试信息。
书签和断点显示区。
此外,这里可以突出显示执行代码的行。
以下是可以在程序编辑器中使用的快捷键列表。
按键名称 | 说明 |
---|---|
光标箭头按键 | 移动光标 |
PageUpPageDown | 转到上一页、下一页 |
Ctrl+PageUp | 转至文本的开头 |
Ctrl+PageDown | 转至文本的末尾 |
Home | 光标移至行首 |
End | 光标移至行尾 |
Enter | 转至下一行 |
Delete | 删除光标位置的符号; 删除所选文字 |
Backspace | 删除光标左侧的符号 |
Ctrl+Y | 删除当前行 |
Ctrl+Z | 撤销上次的操作(最多32个事件) |
Shift+光标箭头按键 | 选择一个文本块 |
Ctrl+A | 选择全文 |
Ctrl+U | 将所选块向左移动两个符号 |
Ctrl+I | 将所选块向右移动两个符号 |
Ctrl+C,Ctrl+Insert | 将所选块复制至剪切板 |
Ctrl+V,Shift+Insert | 粘贴剪切板中的文本 |
Ctrl+X,Shift+Delete | 将所选块剪切到剪切板 |
Ctrl+Shift+数字按键 | 在当前行上设置编号为0~9的书签 |
Ctrl+编号 | 跳转至指定编号的书签 |
Ctrl+F | 搜索一行 |
Ctrl+R | 替换一条线 |
F3 | 从光标位置重复搜索/替换 |
F4 | 设置要运行的程序的断点(运行到游标) |
Ctrl+F2 | 重置程序 |
Ctrl+F7 | 变量的预览值 |
F9 | 运行程序(运行) |
F7 | 执行代码行(Step into) |
Ctrl+空格按键 | 显示对象的方法和属性列表 |
Ctrl+Shift+Delete | 删除光标右侧的单词 |
Ctrl+Shift+Backspace | 删除光标左侧的单词 |
# 2. 程序结构
以PascalScipt
结构的程序示例如下。
#language PascalScript // 可选项
program MyProgram; // 可选项
// “uses” 段落应当在其他段落前
uses 'unit1.pas', 'unit2.pas';
var // 变量章节可放置于程序任意位置
i, j: Integer;
const // 常量章节
pi = 3.14159;
procedure p1; // 程序和功能
var
i: Integer;
procedure p2; // 嵌套程序
begin
end;
begin
end;
begin // 主程序
end.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
稍后我们将看一下用PascalScript
语言编写的程序示例。创建新报表时,默认情况下会选择此语言。
# 3. "Hello World"程序
打开设计器,切换到“代码”选项卡并编写以下程序:
begin
ShowMessage('Hello, World!');
end.
2
3
之后运行报表。正如预期的那样,FastReport会显示一个段问候语的小对话:
我们来解释一些细节。我们创建了一个由单个begin..end
块组成的程序。所以我们的程序结构非常简单; 它只包含一个主程序(参见上面的“程序结构”部分)。报表运行后立即执行主程序。
在这种情况下,它显示了问候对话; 该程序在对话结束后立即结束。主程序运行完毕后,正常的报表构建开始。
# 4. 在程序中使用对象
可以从程序访问任何报表对象。因此,如果存在Page1
页面和Memo1
对象,则可以在程序中使用它们,并按名称调用它们:
Memo1.Color := clRed
可从程序访问的报表对象列表显示在“报表树”窗格中。程序可以使用哪些对象属性?答案很简单:在对象检查器中可见的所有内容。对象检查器还显示底部每个属性的提示。使用程序时,两个窗格(报表树和对象检查器)都可用。
这是一个简单的例子。将名为“MyTextObject”并包含“Test”的文本对象放在报表设计页面上。然后写这个程序:
begin
MyTextObject.Color := clRed
end.
2
3
运行报表,看到对象的颜色为红色。
# 5. 从报表的变量列表中调用变量
可以在程序中引用报表变量列表中定义的任何变量(报表-变量...
菜单项)。变量的名称应该用尖括号括起来:
if <my variable> = 10 then ...
另一种方法是使用Get
函数:
if Get('my variable') = 10 then ...
变量的值仅通过Set
程序更改:
Set('my variable', 10);
值得注意的是,要为变量分配字符串值,您必须在值周围添加引号:
Set('my variable', '''' + 'String' + '''');
系统变量,例如Page#
,应以完全相同的方式引用:
if <Page#> = 1 then ...
# 6. 引用数据库字段
与变量一样,在引用报表中的数据库字段时应使用尖括号:
if <Table1."Field1"> = Null then...
或者,Get
函数也可用于访问数据可字段(实际上,当计算括在尖括号中的表达式时,FastReport会隐式使用此函数)。
# 7. 在程序中使用聚合函数
聚合函数的一个特性是它们必须在文本对象中使用; 一旦以这种方式使用,它们就可以在程序本身中使用。如果聚合函数仅出现在程序中(不出现在文本对象中),则会生成错误消息。这是因为聚合函数必须连接到特定的段; 一旦这样连接它将正常工作。
# 8. 在报表中显示变量的值
可以在程序中本地声明和使用变量。声明后,程序变量可以赋值给它。以下是正在使用的程序变量的简单示例:
var
MyVariable: String;
begin
MyVariable := 'Hello!';
end.
2
3
4
5
变量的值可以显示在文本对象中,例如,通过在对象中键入[MyVariable]
。变量的名称必须是唯一的。这意味着名称不得复制任何其他报表对象,标准函数或常量的名称。如果程序中存在错误,则在运行报表时将显示一条消息,并且将停止报表创建。
# 9. 事件
到目前为止,我们只查看了一个主程序的程序,该程序在报表开始运行时执行。在主程序中,可以进行初始设置并初始化变量。但是,这还不足以完全控制报表生成过程。为了在报表生成上实现尽可能多的控制,每个对象都有几个事件可以分配处理程序(即程序中的程序)。例如,将处理程序连接到数据段使得能够过滤记录,从而可以根据满足的特定条件隐藏或显示该段。
让我们演示报表创建过程以及通过包含一个页面并具有一个主数据集段的简单报表触发的事件,段上有两个文本对象:
如上所述,在运行报表的最开始调用程序的主过程。之后,开始进行报表的创建。首先调用Report
对象的OnStartReport
事件。然后,在创建输出页面之前,调用OnBeforePrint
页面事件。对于报表模板中的每个设计页面,都会调用此事件一次(不应将设计页面与报表的输出页面混淆!)。在我们的示例中,事件被调用一次,因为报表设计只包含一个设计页面。
然后按以下顺序调用数据段的事件:
- 段的
OnBeforePrint
事件被触发。 - 调用包含在段中的每个对象的
OnBeforePrint
事件。 - 每个对象都填充了数据(在我们的示例中,包含
FTypeID
与FName
的字段)。 - 调用每个对象的
OnAfterData
事件。 - 诸如在段子上定位物体的动作(如果它们之间有可伸缩的物体),计算段高度并拉伸它(如果它是可拉伸的)被执行。
- 段的
OnAfterCalcHeight
事件被调用。 - 如果段在页面的空白处没有足够的空间,则会创建一个新的输出页面。
- 段及其所有对象都显示在输出页面上。
- 调用每个段中对象的
OnAfterPrint
事件。 - 调用段本身的
OnAfterPrint
事件。
只要连接到段的源有数据,段就会继续打印。在该报表打印停止之后,将调用报表页面的OnAfterPrint
事件,最后调用Report
对的OnStopReport
事件。
因此,通过使用不同对象的事件,几乎可以管理报表创建过程的每个步骤。使用事件的关键是对段输出过程的全面了解,这将在接下来的部分中进行讨论。大多数动作只能使用段的OnBeforePrint
事件来执行; 对对象所做的任何修改都会立即显示。但是,如果段是可伸缩的,则在这种情况下无法说明段将在哪个页面打印,因为段高度的计算是在步骤5中执行的。但是,可以在步骤6中的OnAfterCalcHeight
或步骤9中的OnAfterPrint
事件中完成。请注意,在最后一个事件中,已经输出了段,因此在此事件中对对象的修改将不会产生任何可见效果。必须清楚地理解输出段以及各段内对象的顺序,并了解每个事件的触发顺序。
# 10. 使用OnBeforePrint事件的示例
要演示此事件,请使用7.9节创建的报表示例来进行演示。
在此示例中,选择主数据集段并切换到对象检查器中的“事件”选项卡:
要创建OnBeforePrint
事件处理程序,请双击事件名称右侧的空白字段:
这会向程序添加一个空白处理程序,设计器会切换到“代码”选项卡。
现在需要的只是在处理程序的主体中键入以下代码:
if Copy(<B."FTypeID">, 1, 1) = '1' then
MasterData1.Visible := True
else
MasterData1.Visible := False;
2
3
4
运行报表并确保程序正常工作:
我们来解释几件事。可以将一个处理程序分配给多个对象的事件, Sender
参数显示哪个对象已启动事件。要将现有处理程序分配给事件,请将其直接键入对象
检查器,或从下拉列表中选择它:
可以轻松删除指向处理程序的链接。在对象检查器中选择已分配的处理程序,然后按Delete键。
# 11. 在群组首段中打印组的和
此方法经常使用并且需要使用程序,因为只有在处理了组中的所有记录之后才知道组和的值。要在群组首中显示总和(即在将组输出到报表之前),使用以下方法:
- 打开
两遍过程
选项(报表
-选项...
菜单项)。 - 在第一遍中,计算每个组的总和并将它们保存在一个数组中。
- 在第二遍中,从数组中提取值并将其显示在组标题中。
我们还是以上面使用的Basic_CommonDataType
与Basic_CommonData
为例,在报表管理界面中设置好数据源,进入报表设计界面,按照下图的方式设置好各段及对象。
打开设计器并将数据源连接到报表。在报表的设置中启用两遍过程
(报表
-选项...
菜单项)。在报表表中添加两个段:“群组首”和“主数据集”。在“群组首”频段的编辑器中,输入FTypeID
。FDataName
数据字段。将数据段连接到B
数据源,然后按以下方式排列一些对象:
为了显示总和,我们在设计中使用了箭头对象(此处项指示为Memo4
)。
- 第一种方式,我们使用
TStringList
类作为存储和的数组。程序如下图所示。
var
List: TStringList;
i: Integer;
procedure frxReport1OnStartReport(Sender: TfrxComponent);
begin
List := TStringList.Create;
end;
procedure frxReport1OnStopReport(Sender: TfrxComponent);
begin
List.Free;
end;
procedure Page1OnBeforePrint(Sender: TfrxComponent);
begin
i := 0;
end;
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin
if Engine.FinalPass then
Memo4.Text :='Sum:' + List[i];
end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent);
begin
if not Engine.FinalPass then
List.Add(FloatToStr(SUM(<B."FInterID">,MasterData1)));
Inc(i);
end;
begin
end.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
程序中的过程名称显示了我们使用过的事件。 它们是:Report.OnStartReport
,Report.OnStopReport
, Page1.OnBeforePrint
,GroupHeader1.OnBeforePrint
和 GroupFooter1.OnBeforePrint
。前两个事件分别在报表的开头和结尾调用。要为这两个事件创建处理程序,请在对象检查器的下拉列表中选择报表对象,其属性将显示在对象检查器中。然后切换到对象检查器的“事件”选项卡并创建处理程序。
为什么我们不在程序的主程序中创建List
变量?我们在OnStartReport
事件中创建它,因为在报表完成后应该销毁动态创建的变量。在OnStartReport
事件中创建动态变量并在OnStopReport
事件中销毁它们是合乎逻辑的。在其他情况下(当程序完成时不需要释放内存时)可以使用程序的主过程来初始化变量。List
变量的创建和销毁是直截了当的。现在让我们看看程序是如何工作的。在页面的开头,当前组的计数器(变量i
)被重置为零,并且在每个组打印后(在GroupFooter1.OnBeforePrint
事件中)递增。在计数器之前,计算的总和将添加到此事件中的列表中递增 。GroupHeader1.OnBeforePrint
事件在第一次传递期间不执行任何操作( 如 果 是Engine.FinalPass
条件),但在第二次传递期间(当List
填充了值时),从List
中检索对应于当前组的总和“并输出到”Memo8“对象以显示组头中的总和。在完成的报表中,它显示如下:
- 第二种方式:我们将使用报表变量的集合作为存储组和的数组。请记住,可以通过
Get
和Set
来访问报表变量。使用这些函数还可以使我们不必显式创建和销毁这些变量。程序如下:
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin
if Engine.FinalPass then
Memo4.Text := 'Sum:'+ FloatToStr(Get(<B."FTypeID">));
end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent);
begin
Set(<B."FTypeID">,
SUM(<B."FInterID">,MasterData1));
end;
begin
end.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
如您所见,此程序稍微简单一些。GroupFooter1.OnBeforePrint
处理程序中的代码设置具有从客户端编号派生的名称的变量的值(或者可以使用明确标识客户端的任何其他标识符,例如<B."FTypeID">
)。如果没有已存在该名称的变量,则程序会自动创建它;否则,如果确实存在则更新其值,在GroupHeader1.OnBeforePrint
处理程序中,检索适当变量的值。

# 12. OnAfterData事件
在报表对象填充了与其连接的源的数据后,将触发此事件。使用此事件可以访问数据库字段值或对象中包含的表达式。此值放在Value
系统变量中,该变量仅在此事件中可用。因此,如果两个文本对象包含表达式[Table1."Field1"]
和[<Table2."Field1”>+10]
这些表达式的值可以通过引用对象的Value
变量来使用:
if Value > 3000 then
Memo1.Color := clRed
2
这会比写这样的东西简单:
if <Table1."Field1"> > 3000 then
Memo1.Color := clRed
2
使用Value
而不是表达式可以为编写一个多用途处理程序OnAfterData
事件可以连接到多个对象。如果对象包含多个表达式(例如'[expr1] [expr2]'),则它是传递给Value
变量的最后一个表达式的值。
OnAfterData
事件非常适合计算对象的高度和宽度,比如文本对象。也就是说,如果程序中需要包含表达式的拉伸文本对象的确切高度,则可以在OnAfterData
事件中使用此代码:
var
MemoHeight: Extended;
begin
MemoHeight := TfrxMemoView(Sender).CalcHeight;
end;
2
3
4
5
# 13. 服务对象
除了普通的报表对象(如页面,段,文本和其他对象)之外,还有一些其他系统对象可以在程序中使用。它们可能有助于报表构建的管理。前一章中使用的Engine
对象就是这样一个对象,所有这些对象都列在这里:
Report
:报表对象。Engine
:报表引擎的链接。Outline
:预览报表中报表树元素的链接。
让我们看一下这些对象。
# 13.1. Report对象
此对象表示当前报表的链接。在“报表树”窗口中选择“报表”元素时,可以看到此对象的属性。
# 13.1.1. Calc
function Calc(const Expr: String): Variant;
返回Expr
的值,这是一个表达式,例如,Report.Calc('1 + 2')
返回“3”,任何有效的FastReport表达式都可以在参数中传递。
# 13.1.2. GetDataSet
function GetDataSet(const Alias: String): TfrxDataSet;
返回具有指定名称的数据集,数据集必须出现在报表的数据集列表中(报表-数据...
对话框)。
# 13.2. Engine对象
这是最有用和最有趣的对象,它代表了引擎的链接(FastReport的核心,它管理报表构建)。通过使用引擎的属性和方法,可以创建不同寻常的报表设计布局。
- 属性
属性 | 类型 | 描述 |
---|---|---|
CurColum | integer | 多列报表中当前列的索引可以将值分配给此属性 |
CurX | Extended | X轴上的当前打印位置,可以为此属性分配值 |
CurY | Extended | Y轴上的当前打印位置,可以为此属性分配值 |
DoublePass | Boolean | 如果报表是类似于Report.EngineOptions.DoublePass的两遍,则等于“True”。 |
FinalPass | Boolean | 在两遍报表的最后一次传递中等于“真” |
PaperHeight | Extended | 可打印区域的高度,以像素为单位 |
PaperWidth | Extended | 可打印区域的宽度,以像素为单位 |
StartDate | TDateTime | 报表运行的时间,与<Date>相同 |
StartTime | TDateTime | 报表运行的时间,与<Time>系统变量相同 |
TotalPages | Integer | 报表中的页数,与<TotalPages>系统变量相同 |
SecondScriptcall | Boolean | flag返回事件的'repeat-call'状态(在某些情况下,可以在分组期间重复调用事件),如果为True则程序已被调用 |
- 方法
procedure AddAnchor(const Text: String);
将文本添加到锚点列表中。
procedure NewColumn;
在最后一列自动分页后,在多列报表中创建一个新列插入。
procedure ShowBand(Band: TfrxBand);
显示具有指定名称的段,显示段后,CurY
位置自动递增。
function FreeSpace: Extended;
返回页面上剩余的空白高度(以像素为单位)。
function GetAnchorPage(const Text: String): Integer;
返回已放置指定锚点的页码。
# 13.3. Outline对象
此对象表示预览报表中的“报表树”控件元素。
“大纲”显示完成报表的树状结构。单击任何树节点时,预览窗格将跳转到显示此节点的页面。要显示“大纲”,它应该是通过单击预览窗口工具栏中的按钮或将Report.PreviewOptions.OutlineVisible
属性设置为True
来启用 。 也可以在那里设置Outline
的宽度(以像素为单位):Report.PreviewOptions.OutlineWidth
。

- 方法
procedure AddItem(const Text: String);
在当前树位置添加具有文本名称的元素,页面上的当前报表页面和位置被链接对元素。
procedure LevelRoot;
将树中的当前位置移动到根级别。
procedure LevelUp;
将树中的当前位置向上移动一级。
# 14. 使用Engine对象
“Engine”对象代表报表的引擎,它管理报表的构建。可以使用引擎的属性和方法来管理在页面上排列数据段的过程。首先是一些理论。
下图显示了报表页面的各个维度。
页面的物理尺寸是PaperWidth
和PaperHeight
属性,在选择页面时在对象检查器中可见。A4页面的尺寸为210 x 297mm。
PageWidth
和PageHeight
是可打印区域的尺寸,通常小于页面的物理尺寸。可打印区域的大小取决于报表页面属性LeftMargin
,TopMargin
,RightMargin
和BottomMargin
。可打印区域的大小(以像素为单位)由Engine.PageWidth
和Engine.PageHeight
函数返回。
最后,FreeSpace
是页面上可用空间的高度。如果有一个“页脚”段在页面上,计算FreeSpace
时会考虑其高度。Engine.FreeSpace
函数以像素为单位返回此高度。请注意,在显示下一个段后,页面上的可用空间会减少,这在计算FreeSpace
时会被考虑在内。
报表页面是如何构建的?只要有足够的可用空间,报表引擎核心就会在页面上显示段。当没有剩余可用空间时,打印“页脚”段(如果需要)并创建一个新的空白页。如上所述,在显示下一个段后,可用空间的高度减小。此外,显示下一个段从当前位置开始,该位置由X轴和Y轴上的坐标定义。当前位置分别由Engine.CurX
和Engine.CurY
返回。打印完下一个段后,CurY
自动增加打印段的高度。创建新页面后,CurY
等于“0”。打印多列报表时,CurX
会更改。
Engine.CurX
和Engine.CurY
不仅可读还可写。这意味着可以通过递增或递减这些值来移动数据段。
例如,在如下的报表中:
它显示的报表格式如下:
通过编写程序实现。
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
begin
Engine.CurX := Engine.CurX + 5;
end;
begin
end.
2
3
4
5
6
7
8
更改CurY
可以使段重叠,例如:
通过编写程序实现。
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
begin
Engine.CurY := Engine.CurY - 15;
end;
begin
end.
2
3
4
5
6
7
8
Engine.NewPage
方法在报表中的任何所需位置插入分页符,然后从新输出页面的顶部继续打印。可以在打印第二条记录后插入一个break:
procedure MasterData1OnAfterPrint(Sender: TfrxComponent);
begin
if <Line> = 2 Then
Engine.NewPage;
end;
2
3
4
5
请注意,我们使用了OnAfterPrint
事件(也就是说,在打印段之后)。另请注意,“Line”系统变量返回记录的序号。
Engine.NewColumn
方法在多列报表中插入分栏符。如果页面上没有剩余的空闲列,则会创建一个新页面。
# 15. 锚(Anchor)
Anchor
是超链接系统中的一个元素,它可以通过单击(在预览窗口中)跳转到连接到已完成报表对象的任何元素。
可以通过Engine.AddAnchor
方法设置锚点。Anchor
在报表页面中具有名称和位置。要跳转到具有指定名称的锚点,请在任何报表对象的URL属性中键入#锚点的名称
或者#[锚点的名称]
。
在第二种情况下,FastReport会替换表达式的值。
单击该对象将执行跳转到添加锚点的报表部分。在构建内容表时使用锚点,例如使用指向相应章节的链接。让我们在下面的例子中说明这一点。首先,我们需要熟悉的“客户”表。
我们的报表将是一个多页面(有两个设计页面)。我们将内容表放在第一页上,将另外一个列表放在第二页上。单击任何内容行将执行跳转到相应的报表元素。第一个设计页面:
将以下文本放在数据段中包含的文本对象的URL属性中。
#[B."FName"]
并将字体属性设置为蓝色和段下划线,以模拟超链接的外观。
第二个设计页面:
要添加锚点,请在程序中创建MasterData2.OnBeforePrint
事件处理程序:
procedure MasterData2OnBeforePrint(Sender: TfrxComponent);
begin
Engine.AddAnchor(<B."FName">);
end;
2
3
4
这就是所需要的。预览报表并确保“超链接”有效。
最后要提到的是Engine.GetAnchorPage
函数。此函数返回添加了相应锚点的页面编号,在创建“内容”表时非常有用。报表必须启用两遍过程(报表-选项...
)的,否则不能使用此功能。
# 16. 使用Outline对象
如前所述,Outline
对象表示可以在预览窗口中显示的报表树。单击树中的元素将跳转到包含相应元素的报表输出页面。没有必要使用程序来处理“大纲”,因为一些段可以自动创建树。让我们看两个例子,展示如何在段和程序的帮助下使用“大纲”。
几乎所有段都具有OutlineText
属性,以包含自动创建树的文本表达式。在创建报表时评估表达式,并在打印段时将其值添加到树中。因此,树中元素的层次结构类似于报表中的段的层次结构,
这意味着树将具有与报表中的主要和从属段相对应的主要和从属元素(例如,报表具有两个级别的数据或有组)。我们将使用示例来显示在具有组的报表中显示大纲。
将GroupHeader1.OutlineText
的属性的值设置为<B."FDataName">
。要在预览窗口打开后立即显示树,请设置报表(报告)对象的PreviewOptions.OutlineVisible
属性为True
。在预览报表时,您将看到以下内容:

单击树中的任何元素将跳转到报表中的相应页面,窗口顶部将显示所选元素。
让我们将第二级添加到报表树中。只需将MasterData
段的OutlineText
属性设置为<B."FName">
,树将更改为:

很明显,导航到订单号是可能的,并且树中元素的层次结构类似于报表中的元素。
现在我们将创建一个类似的树,但使用程序而不是OutlineText
属性。在报表中清除两个段的OutlineText
属性并创建两个事件处理程序 :GroupHeader1.OnBeforePrint
和MasterData1.OnBeforePrint
:
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin
Outline.LevelRoot;
Outline.AddItem(<B."FDataName">);
end;
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
begin
Outline.AddItem(<B."FName">);
Outline.LevelUp;
end;
begin
end.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
预览报表以确保它的工作方式与之前自动创建树的方式相同。让我们看看程序是如何创建树的。
Outline.AddItem
方法将子节点添加到当前树节点,然后使子节点成为当前节点。因此,如果连续多次调用AddItem
,它将创建此处显示的梯形图:
Item1
Item2
Item3
...
2
3
4
LevelUp
和LevelRoot
设置大纲方法用于控制哪个元素是当前元素。第一种方法将光标移动到位于一级的元素。所以这个程序:
Outline.AddItem('Item1');
Outline.AddItem('Item2');
Outline.AddItem('Item3');
Outline.LevelUp;
Outline.AddItem('Item4');
2
3
4
5
构造成为这样的树:
Item1
Item2
Item3
Item4
2
3
4
这表明Item4
是Item2
元素的子元素。另一方面,LevelRoot
方法将光标向上移动到树的根。例如,程序:
Outline.AddItem('Item1');
Outline.AddItem('Item2');
Outline.AddItem('Item3');
Outline.LevelRoot;
Outline.AddItem('Item4');
2
3
4
5
构造这个树:
Item1
Item2
Item3
Item4
2
3
4
了解这一点很清楚报表是如何运作的。在输出每个组标题之前,树的根成为当前元素。之后,输出明细列表,每个明细标被添加为分类的子元素。确保所有明细都位于正确的分类上。
如果没有显示为梯形图,则在每次添加后调用Outline.LevelUp
方法,将光标移回上一级别。
# 17. OnManualBuild页面事件
报表引擎核心通常负责报表构建。它按特定顺序显示报表段,这是数据所需的次数,从而创建完整的报表。有时需要以非标准形式显示报表引擎核心无法完成的报表。在这种情况下,可以使用报表设计页面的OnManualBuild
事件手动构建报表。如果定义了此事件的处理程序,则报表引擎核心在需要数据输出时将控制权转移给它。同时报表引擎核心自动处理位于页面上的那些段的显示,例如“报表标题”,“页面标题”,“列标题”,“报表页脚”,“页脚”,“列页脚“和”背景“。核心还处理新页面和列的建。OnManualBuild
事件处理程序的目的是以用户控制的顺序显示数据段,它们的标题和页脚。
也就是说,OnManualBuild
处理程序的本质是向报表引擎核心发出命令,以便在特定时间显示段。核心完成剩下的工作:只要当前没有空闲空间,处理附加到事件的程序等,它就会创建新页面。
以报表创建的例子继续执行,在页面中插入第二个主数据段,不设置其连接的数据集。在这种情况下,直接执行打印是无法输出内容的。故在此处使用手动创建。
处理程序将以交替顺序显示这些段(每个段六次)。显示六个段后,将插入一个小间隙。
procedure Page1OnManualBuild(Sender: TfrxComponent);
var
i: Integer;
begin
for i := 1 to 6 do
begin
Engine.ShowBand(MasterData1);
Engine.ShowBand(MasterData2);
if i = 3 then
Engine.CurY := Engine.CurY + 10;
end;
End;
2
3
4
5
6
7
8
9
10
11
12
# 18. 在程序中创建对象
可以使用程序将新对象添加到报表中。让我们用一个简单的例子来说明这是如何完成的。创建一个空白报表,并在程序的主过程中输入以下代码:
var
Band: TfrxReportTitle;
Memo: TfrxMemoView;
begin
Band := TfrxReportTitle.Create(Page1);
Band.Height := 20;
Memo := TfrxMemoView.Create(Band);
Memo.SetBounds(10, 0, 200, 20);
Memo.Text := 'This memo is created in code';
end.
2
3
4
5
6
7
8
9
10
请注意,我们没有销毁我们在此示例中创建的FastReport对象。这不是必需的,因为报表完成后,应用程序会自动销毁FastReport对象。另请注意,当我们在程序中创建标准对象(例如TStringList
)时,我们还必须在程序中销毁它们,因为这不会由应用程序自动完成。