Page tree
Skip to end of metadata
Go to start of metadata

您可以在各种不同的编程场景使用C1Report,包括Desktop桌面应用和Web应用。使用的步骤在不同平台下是完全一致的:

  1. 首先使用C1ReportDesigner程序创建并定义一个报表,报表的定义描述将保存在XML文件中。您可以从头创建一个全新的报表,或者从一个现有的Microsoft Access报表导入一个现有的报表。之后您可以使用

C1ReportDesigner基于现有报表进行修改。

  1. C1Report组件将读取该报表并使用来自于任何标准的.NET数据源的数据呈现该报表。
  2. 可以在设计时加载一个预先定义的报表,并将其嵌入您的应用程序中,或者您也可以在运行时读取并修改一个现有报表。(同样您也可以使用C1Report对象模型,从头开始创建一个全新的报表。)
  3. 报表可以直接打印到打印机,呈现到一个C1PrintPreview控件上,或者可以输出到HTML或者PDF文件,之后可以直接发布到Web页面上。

    注意:注意:下图中具有粗边框的方框表示具有代码逻辑的组件(比如控件以及应用程序)。具有细边框的方框表示包含信息的文件(报表定义、数据以及完成的报表)。


    以下数字表示图中对应的箭头的编码,这些箭头用来表示组件之间的关系:

  4. 使用C1ReportDesigner应用程序创建、编辑并保存XML格式的报表定义文件。
  5. C1Report组件从设计器创建的XML文件加载定义的报表。既可以在设计时完成加载(这种情况下,XML文件将直接应用到控件的设置上,运行时不再需要引用该文件)也可以通过Load方法在运行时加载。
  6. C1Report组件从数据源加载数据,数据源由报表定义文件指定。同时,您也可以提供自定义的数据源。
  7. C1Report组件按照报表定义格式化数据并将报表呈现至(a)打印机,(b)一种或者多种格式的文件,或者

(c)一个打印预览控件。

  1. 应用程序可以通过丰富的对象模型和C1Report组件进行交互,您可以容易的自定义报表或者生成全新的报表。

C1ReportDesigner就是实现了类似功能的一个很好的例子。
对象模型概述
C1Report组件的对象模型主要基于Microsoft Access的模型,所不同的是Access具有不同的控件类型(标签控件,文本框控件,线形控件等等),而C1Report仅支持一个Field对象,该Field对象可以通过设置属性使得其看起来像是一个标签、文本框、线、图片、子报表等等。下表列举了全部的对象,以及其主要的属性和方法(注意,C1Report使用twips做为测量单位,一个twips表示1/20个 point,因此一英寸等于72个point,或者等于1440个twips)

C1Report Object:主要的组件

ReportName, GetReportInfo, Load, Save, Clear, Render, RenderToFile, RenderToStream, PageImages, Document,
DoEvents, IsBusy, Cancel, Page, MaxPages, Font, OnOpen, OnClose, OnNoData, OnPage, OnError, Evaluate, Execute

Layout Object: 指示如何在页面上呈现报表

Width, MarginLeft, MarginTop, MarginRight, MarginBottom, PaperSize, Orientation, Columns, ColumnLayout,
PageHeader, PageFooter, Picture, PictureAlign, PictureShow

DataSource Object: 管理数据源

ConnectionString, RecordSource, Filter, MaxRecords, Recordset

Groups Collection: 报表可以具有多个分组

Group Object: 控制数据排序和分组

Name GroupBy, Sort, KeepTogether, SectionHeader, SectionFooter, Move

Sections Collection: 任何报表都具有五个以上的报表节

Section Object: 报表节,包含Field对象(也称之为"report band")

Name, Type, Visible, BackColor, OnFormat, OnPrint, Height, CanGrow, CanShrink, Repeat, KeepTogether,
ForcePageBreak

Fields Collection: 一个报表通常包含大量的Field

Field Object: 用来显示信息的方形区域

Name, Section, Text, TextDirection, Calculated, Value, Format, Align, WordWrap, Visible, Left, Top, Width, Height,
CanGrow, CanShrink, Font, BackColor, ForeColor, BorderColor, BorderStyle, LineSlant, LineWidth, MarginLeft,
MarginRight, MarginTop, MarginBottom, LineSpacing, ForcePageBreak, HideDuplicates, RunningSum, Picture,
PictureAlign, Subreport, CheckBox, RTF

报表的节任何报表都具有五个以上的报表节

报表节

描述

Detail

内容部分包含为数据记录集合中间的每一条记录重复渲染一次的Field的集合。

Header

报表的页眉部分呈现在一个报表的开头位置。

Footer

报表的页脚部分呈现在一个报表的末尾。

Page Header

页眉部分呈现在每一页的开始部分(除了包含报表页眉的页,此项为可选项)

Page Footer

页脚部分呈现在每一页的底部(除了包含报表页脚的页,此项为可选项)

除了以上这五个基本的报表节部分,每一个分组还具有额外的两个报表节:一个分组页眉和一个分组页脚部分。比如说,一个具有三个分组级别的报表将具有11个报表节。
注意:报表节可以设置为不可见,但是不可以添加或者删除报表节。添加删除分组功能除外。下图展示在一个典型的报表中,每一个报表节部分是如何呈现的:

Report Header
最开始呈现的是报表页眉部分。该报表节通常会包含报表的标识信息。

Page Header

在报表页眉之后显示的是页眉。如果一个报表不具有任何分组,该部分通常包含内容(Detail)报表节中包含的字段的标签。

Group Headers and Group Footers

接下来的报表节部分包括分组页眉,内容,以及分组页脚。这些报表节部分将包含实际的报表数据。分组页眉和页脚通常包含汇总功能,比如说分组合计,百分比,最大最小值等等。分组页眉和分组页脚通常在由GroupBy属性执行的表达式的值由一个记录变为另一个记录时插入。

Detail

内容报表节部分包含每一条记录的数据。可以通过设置Visible属性的值为False隐藏该部分,而仅仅显示分组页眉和分组页脚。这是创建汇总报表的好办法。
Page Footer
每一页的底部显示的是页脚报表节部分。该部分通常包含诸如页码、报表总页数、以及/或报表打印日期等信息。
Report Footer
最后,Report Footer在page footer前面打印,这个节经常被用来显示整个报表的汇总信息。

Customized sections

你可以通过设置Visible属性为True或者False决定一个节是否可见。Group header可以通过设置Repeat属性为True在每页顶部重复显示(每个分组的开始处或者不是)。Page Header和Footer能从包含了ReportHeader 和Footer节的页面中删掉,只需要在Layout对象中设置PageHeader和PageFooter属性。
为桌面应用场景开发报表
在典型的桌面应用场景中,C1Report在同一台计算机中生成和呈现报表(报表数据任然来自远程服务器)。以下场景假定C1Report托管在Visual Studio .NET环境中。
嵌入报表(在设计时载入)
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
在此情景中,程序使用一套内置的固定的报表定义来生成报表。此种类型的程序不依赖于任何外部的报表定义文件,并且最终用户无法对报表进行修改。
此种类型的程序主要的优点是不需要分发额外的报表定义文件,并且能够确保报表格式不会被别人修改。缺点就是对报表进行任何修改之后,必须重新编译程序。
如果需要用现有的报表定义,并且不需要其他修改,请参照下列步骤(稍后将描述如何编辑嵌入的报表或者从零开始创建报表):

  1. 为每一个需要发布的报表定义添加一个C1Report组件。你可能需要重命名呈现报表的控件(这将使代码更易于管理)。
  2. 右键点击在每一个C1Report组件上,然后在上下文菜单上选择Load Report来载入报表定义到每一个组件中。

弹出Select a Report对话框,允许选择一个报表定义文件以及文件中定义的一个报表。
要载入报表的话,点击省略号按钮来选择在步骤1中创建的报表定义文件,然后在下拉列表中选择一个报表,最后点击Load按钮。属性页显示选择的报表的名字以及分组,区域和字段的总数。对话框显示内容如下所示。

  1. 在窗体上添加一个单独的C1PrintPreview控件(或者一个Microsoft PrintPreview控件)和一个可以让用户选择报表的控件(可以是一个菜单,一个列表框,或者一组按钮)。
  2. 添加代码来呈现用户选择的报表。例如,如果在上一步添加了一个命名为btnProductsReport的按钮,类似代码如下:
  3. Visual Basic

    Visual Basic

    Private Sub btnProductsReport_Click(ByVal sender As System.Object, ByVal e As
    System.EventArgs) Handles btnProductsReport.Click

    ppv.Document = rptProducts
    End Sub
    C#

    C#

    private void btnProductsReport_Click(object sender, System.EventArgs e) { ppv.Document = rptProducts;
    }

    注意rptProducts是包含了用户选择的报表的C1Report组件的名称,ppv是C1PrintPreview控件的名称。
    嵌入报表(在设计时创建)
    在嵌入报表(在设计时载入)中描述的Load Report命令,可以很容易的将现有的报表嵌入到程序中。然而在一些情况
    下,需要定制一个报表,或者用Visual Studio程序中定义的数据源对象替换连接字符串和记录源。在此情景下,使用
    Edit Report命令替代。
    要在设计时创建和编辑报表的话,在C1Report组件上右键点击,然后在上下文菜单中选择Edit Report菜单项以执行 C1ReportDesigner程序(也可以点击组件上方的智能标签来打开C1Report Tasks菜单,然后选择Edit Report项)。

    注意:注意:如果在上下文菜单和Properties窗口中找不到Edit Report命令,很可能是因为组件找不到C1ReportDesigner 程序。解决的方法是,单独运行一次C1ReportDesigner程序。编辑器会将程序的路径保存到注册表中,然后
    C1Report组件就可以找到它。

    C1ReportDesigner程序显示在C1Report组件中载入的报表。如果C1Report组件中没有载入报表,编辑器就会打开
    C1Report Wizard来创建一个新的报表。
    这和单独运行的C1ReportDesigner程序是相同的。当C1ReportDesigner程序被打开时唯一能注意到的区别是:
    可以指定在程序中定义的数据源对象为新报表的数据源。
    关闭编辑器时,任何修改都会被保存到窗体上的C1Report组件内(除非在编辑器的菜单中选择File | Exit,然后在确认对话框中选择No以不保存并且放弃修改)。
    要使用程序中定义的数据源对象的话,点击编辑器的Data Source按钮,然后在Select a Data Source对话框中选择
    Tables选项。
    Tables页显示了当前窗体上定义的数据对象的列表(页面在窗体上没有定义有效的数据源的情况下不可见)。或者,可以点击Build connection string按钮来照例构造或选择一个连接字符串和记录源。
    例如,如果主窗体有一个包含若干DataTable的DataSet对象,Select a Data Source对话框显示内容如下所示:

    一旦完成创建或者编辑报表,可以通过选择 File | Save 和 File | Exit 菜单来关闭编辑器。报表定义将被直接保存到组件中(如同通过Load Report命令从文件中载入)。
    如果改变主意想取消更改,通过选择File | Exit菜单然后选择No以放弃保存修改。
    运行时载入报表
    !MISSING PHRASE 'Show All'!
    !MISSING PHRASE 'Hide All'!
    类似于查看器,在运行时载入报表需要一个报表定义文件。这种类型的程序主要的优点是在修改报表格式后不需要更新程序。只需要的将新的报表定义文件发送给用户,无需其他操作。
    要创建一个在运行时载入报表的程序的话,参照以下步骤:

  4. 使用C1ReportDesigner程序创建所有需要的报表。(参考 使用C1ReportDesigner 获取完成此步骤的详细信息。

  1. 在程序中添加如下控件:

C1Report组件,名字为clr
C1PrintPreview组件,名字为ppv
ComboList控件,名字为cmbReport
StatusBar控件,名字为status

  1. 在文件的最上方添加如下Import语句:

Visual Basic

Visual Basic

Imports C1.C1Report
Imports System.IO

C#

C#

using C1.C1Report; using System.IO;

这将不需要通过完整的命名空间来引用C1Report和System.IO内的类和对象。

  1. 添加代码来读取报表定义文件和组建一个文件中包含的所有报表的列表。具体代码如下:

Visual Basic

Visual Basic

' get application path Dim appPath As String
appPath = Path.GetDirectoryName(Application.ExecutablePath).ToLower() Dim i As Integer = appPath.IndexOf("/bin")
If (i < 0) Then i = appPath.IndexOf("\bin")
If (i > 0) Then appPath = appPath.Remove(i, appPath.Length - i)

' get names of reports in the report definition file m_ReportDefinitionFile = appPath & "\Data\Nwind.xml" Dim reports As String() = c1r.GetReportInfo(m_ReportDefinitionFile)

' populate combo box cmbReport.Items.Clear() Dim report As String For Each report In reports cmbReport.Items.Add(report)
Next

C#

C#

// get application path string appPath;
appPath = Path.GetDirectoryName(Application.ExecutablePath).ToLower(); int i = appPath.IndexOf("/bin"); if ((i < 0) ) { i = appPath.IndexOf("\bin"); }
if ((i > 0) ) { appPath = appPath.Remove(i, appPath.Length - i); }
// get names of reports in the report definition file m_ReportDefinitionFile = appPath + "\Data\Nwind.xml"; string ( reports) = c1r.GetReportInfo(m_ReportDefinitionFile);
// populate combo box cmbReport.Items.Clear(); string report; foreach report In reports cmbReport.Items.Add(report);
}

代码开头获取包含报表定义的文件的路径。通过系统定义的Path和Application类的静态方法实现。可以调整代码来指向自己的报表定义文件的路径和名字。
然后使用GetReportInfo方法获取报表定义文件(在第一步创建)中包含的所有报表的名字的数组,并且填充到允许用户选择报表的组合框中。

  1. 添加代码来呈现用户选择的报表。例如:

Visual Basic

Visual Basic

Private Sub cmbReport_SelectedIndexChanged(ByVal sender As Object, ByVal e As
EventArgs) Handles cmbReport.SelectedIndexChanged
Try
Cursor = Cursors.WaitCursor
' load report
status.Text = "Loading " & cmbReport.Text c1r.Load(m_ReportDefinitionFile, cmbReport.Text)
' render into print preview control status.Text = "Rendering " & cmbReport.Text ppv.Document = c1r
' give focus to print preview control ppv.StartPage = 0 ppv.Focus()
Finally
Cursor = Cursors.Default
End Try
End Sub

C#

C#

private void cmbReport_SelectedIndexChanged(object sender, System.EventArgs e) { try { Cursor = Cursors.WaitCursor;

// load report status.Text = "Loading " + cmbReport.Text; c1r.Load(m_ReportDefinitionFile, cmbReport.Text);
// render into print preview control status.Text = "Rendering " + cmbReport.Text; ppv.Document = c1r;
// give focus to print preview control ppv.StartPage = 0; ppv.Focus();

} finally {
Cursor = Cursors.Default;
} }
自定义报表
!MISSING PHRASE 'Show All'! !MISSING PHRASE 'Hide All'!
自定义报表是在运行时载入报表的基础上进行的变化。此情形包含从文件中载入报表定义和写代码自定义用户选择的报表。例如,下列代码更改了Detail区域的字体:
Visual Basic

Visual Basic

Imports C1.C1Report

Dim s As Section = c1r.Sections(SectionTypeEnum.Detail)
Dim f As Field For Each f In s.Fields
f.Font.Name = "Arial Narrow"
Next

C#

C#

using C1.C1Report;
Section s = c1r.Sections[SectionTypeEnum.Detail]; foreach (Field f in s.Fields)

f.Font.Name = "Arial Narrow";

下列代码切换分组的显示,通过设置分组的Sort属性为on或off,并且设置组页眉和组页脚的Visible属性。
Visual Basic

Visual Basic

Dim bShowGroup As Boolean bShowGroup = True With c1r.Groups(0)
If bShowGroup Then
.SectionHeader.Visible = True
.SectionFooter.Visible = True
.Sort = SortEnum.Ascending
Else
.SectionHeader.Visible = False
.SectionFooter.Visible = False
.Sort = SortEnum.NoSort
End If
End With


C#

C#

bool bShowGroup; bShowGroup = true; if (bShowGroup) { c1r.Groups[0].SectionHeader.Visible = true; c1r.Groups[0].SectionFooter.Visible = true; c1r.Groups[0].Sort = SortEnum.Ascending; } else { c1r.Groups[0].SectionHeader.Visible = false; c1r.Groups[0].SectionFooter.Visible = false; c1r.Groups[0].Sort = SortEnum.NoSort;

这些例子仅仅演示了自定义报表的一些简单情况。通过对象模型可以访问报表的任何地方,这提供了无限的可能性。
(实际上,完全可以用代码创建整个报表。)Web应用场景开发报表
如需要开发Web(ASP.NET)下的报表,可以使用ComponentOne Studio Enterprise开发套件中的C1WebReport控件。
此控件封装了C1Report组件并且提供了一系列方法和属性来容易的为Web页面添加报表。C1WebReport控件无缝的兼容C1Report,并且提供了专为Web场景设计的缓存和呈现选项。
仍然可以在Web程序中使用C1Report组件,但需要写额外的代码来创建报表的HTML或PDF版本。
在典型的Web场景,C1Report在服务器上以批处理或者按需创建报表。用户可以在客户端浏览器中选择报表然后查看或者打印报表。
静态Web报表
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
静态Web报表基于定期运行的创建预先定义的系列报表的服务器端程序。这些文件被网站的Web页面引用,并且可以像其他Web页面一样被客户端下载。
要实现此类型的程序,参照下列步骤:

  1. 使用C1ReportDesigner程序来创建所有需要的报表。(参见 使用C1ReportDesigner 阅读完成此步骤的详细信息。)
  2. 在服务器上创建一个程序,包含一个C1Report组件。如果不想用form或window,使用CreateObject函数创建控件。
  3. 添加一个定期运行的程序,更新所有用户可见的报表。循环内容如下所示:

Visual Basic

Visual Basic

' 每6个小时运行一次:

' 获取目标文件中所有报表列表
sFile = "c:\inetpub\wwwroot\Reports\MyReports.xml" sList = c1r.GetReportInfo(sFile)

' 刷新服务器上的报表
For i = 0 To sList.Length - 1 c1r.Load(sFile, sList(信息)) sFile = "Reports\Auto\" & sList(信息) & ".htm" c1r.RenderToFile(sFile, FileFormatEnum.HTMLPaged)
Next

C#

C#

// 每6个小时运行一次:

// 获取目标文件中所有报表列表
sFile = "c:\inetpub\wwwroot\Reports\MyReports.xml"; sList = c1r.GetReportInfo(sFile);

// 刷新服务器上的报表
for ( i = 0 ; GAIS <= sList.Length - 1 c1r.Load(sFile, sList(信息)); sFile = "Reports\Auto\" + sList(信息) + ".htm"; c1r.RenderToFile(sFile, FileFormatEnum.HTMLPaged);
}

代码用GetReportInfo方法获取在MyReport.xml报表定义文件(在第一步中创建)中包含的所有报表的列表,然后呈现每一个报表到分页的HTML文件中。(分页的HTML文件为原始报表中的每一页生成一个HTML页面,包含能够方便浏览的导航条。)

  1. 编辑HTML主页面,添加刚才保存的报表的链接。

不仅仅局限于HTML,C1Reort也可以导出为PDF文件,可以在任何浏览上用免费的插件查看。PDF格式在多方面优于
HTML,特别是生成Web报表的硬拷贝的情况下。
动态Web报表
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
动态Web报表是按需创建的,还可能依赖于用户提供的数据。此种方案常常会通过ASP.NET页面中的表单来向用户收集创建报表所需要的信息,然后创建一个C1Report组件来呈现报表到临时文件中,然后返回文件的引用地址。
下面的例子是一个简单的ASP.NET页面,允许用户填写一些信息并且选择需要的报表类型。基于此,ASP代码创建了一个定制版本的NorthWind "Employee Sales by Country"报表,然后以用户选择的格式展现给用户。
此示例在服务器端使用临时文件来保存报表。在实际生产环境中,必须生成唯一的文件名并且在一段时间后将其删除,以避免报表在用户查看之前被覆盖。尽管如此,此示例演示了在Web上使用C1Report发布报表的主要技术。按如下步骤来实现此类型的程序:

  1. 从创建一个带有一个Web页面的Web程序开始,如下图所示:


页面包含五个服务器端控件:页面包含五个服务器端控件:
_1stYear:包含有效年份的列表,具体数据为(:包含有效年份的列表,具体数据为(1994,,1995,和,和1996)。注意可以通过点击智能标签然后)。注意可以通过点击智能标签然后从菜单中选择从菜单中选择Edit Items来添加项目。在来添加项目。在ListItem Collection Editor对话框中添加三个新项目。对话框中添加三个新项目。
_txtGoal: 包含每一位员工的年度销售目标。包含每一位员工的年度销售目标。
_btnHTML, _btnPDF: 用于将报表呈现到用于将报表呈现到HTML或或PDF然后显示结果的按钮。然后显示结果的按钮。
_lblStatus: 在程序出错的情况下显示错误信息。在程序出错的情况下显示错误信息。

注意:注意:如果使用demo或beta版的C1Report来运行程序,将引发控件尝试在服务器端显示其About对话框的错误。
如果发生了这样的情况,只需重新加载页面就能消除这个问题。

  1. 配置完页面之后,需要在项目中添加对C1Report组件的引用。在Solution Explorer窗口右键点击项目,选择Add

Reference然后选取C1Report组件。

  1. 添加Nwind.xml到项目的Data目录。右键点击Solution Explorer窗口中的项目,选择New Folder然后重命名目录为Data。然后右键点击这个目录,选择Add Existing Item然后选取Nwind.xml报表定义文件。Nwind.xml文件默认安装在Documents或My Document文件夹下的ComponentOne Samples\Studio for

WinForms\C1Report\C1Report\VB\NorthWind\Data文件夹中。

  1. 在项目中添加一个Temp目录。在Solution Explorer窗口中右键点击项目,选择New Folder然后重命名目录为

Temp。

  1. 如果使用传统的ASP,有一些有趣的事情。在控件上双击会打开代码窗口,可以写完整的代码去处理事件,与

Windows Forms项目具有相同的编辑器和环境。
添加如下代码:
Visual Basic

Visual Basic

Imports C1.C1Report
'处理用户点击操作
Private Sub _btnHTML_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Handles _btnHTML.Click

RenderReport(FileFormatEnum.HTMLDrillDown)
End Sub
Private Sub _btnPDF_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Handles _btnPDF.Click
RenderReport(FileFormatEnum.PDF)
End Sub
C#

C#

using C1.C1Report;

//处理用户点击操作
private void _btnHTML_Click(object sender, System.EventArgs e) {
RenderReport(FileFormatEnum.HTMLDrillDown);
} private void _btnPDF_Click(object sender, System.EventArgs e) {
RenderReport(FileFormatEnum.PDF);
}

当用户点击任何一个按钮时,这些代码在服务器端运行。
6. 下列代码表达了程序的主要工作,RenderReport:
Visual Basic

Visual Basic

Private Sub RenderReport(ByVal fmt As FileFormatEnum)

' 构建文件名
Dim rpt As String = "Employee Sales by Country"
Dim fileIn As String = GetDataPath() & "NWind.xml"
Dim ext As String = Iif(fmt = FileFormatEnum.PDF, ".pdf", ".htm")
Dim fileOut As String = GetOutputPath() & rpt & ext

Try
' 创建C1Report 组件
Dim c1r As New C1Report()
' 加载报表 c1r.Load(fileIn, rpt)
' 获取用户参数
Dim year As String = _lstYear.SelectedItem.Text
Dim goal As String = _txtGoal.Text
' 自定义报表数据源
Dim sSQL As String = "SELECT DISTINCTROW " & _

"Employees.Country, Employees.LastName, " & _
"Employees.FirstName, Orders.ShippedDate, Orders.OrderID, " & _
" [Order Subtotals].Subtotal AS SaleAmount " & _

"FROM Employees INNER JOIN (Orders INNER JOIN " & _
" [Order Subtotals] ON Orders.OrderID = " & _
]]>
" [Order Subtotals].OrderID) " & _
]]></ac:plain-text-body></ac:structured-macro>
" ON Employees.EmployeeID = Orders.EmployeeID " & _ "WHERE Year(Orders.ShippedDate) = " & year & ";" c1r.DataSource.RecordSource = sSQL
' 自定义报表时间处理
Dim sScript As String = _
"If SalespersonTotal > " & goal & " Then" & vbCrLf & _
" ExceededGoalLabel.Visible = True" & vbCrLf & _
" SalespersonLine.Visible = True" & vbCrLf & _
"Else" & vbCrLf & _
" ExceededGoalLabel.Visible = False" & vbCrLf & _
" SalespersonLine.Visible = False" & vbCrLf & _ "End If" c1r.Sections(SectionTypeEnum.GroupHeader2).OnPrint = sScript
' 把报表呈现到临时文件
c1r.RenderToFile(fileOut, fmt)
' 重新寄送报表文件
Response.Redirect("Temp/" + rpt + ext)

Catch x As Exception _lblStatus.Text = "*** " & x.Message
End Try
End Sub

C#

C#

// 呈现报表
private void RenderReport(FileFormatEnum fmt) {

// 构建文件名
string rpt = "Employee Sales by Country"; string fileIn = GetDataPath() + "NWind.xml"; string ext = (fmt == FileFormatEnum.PDF)? ".pdf": ".htm"; string fileOut = GetOutputPath() + rpt + ext;
try {
// 创建C1Report 组件

C1Report c1r = new C1Report();

// 加载报表
c1r.Load(fileIn, rpt);

// 获取用户参数
string year = _lstYear.SelectedItem.Text; string goal = _txtGoal.Text;

// 自定义报表数据源
string sSQL = "SELECT DISTINCTROW " + "Employees.Country, Employees.LastName, " +
"Employees.FirstName, Orders.ShippedDate, Orders.OrderID, " +
" [Order Subtotals].Subtotal AS SaleAmount " +

"FROM Employees INNER JOIN (Orders INNER JOIN " +
" [Order Subtotals] ON Orders.OrderID = " +

" [Order Subtotals].OrderID) " +
]
" ON Employees.EmployeeID = Orders.EmployeeID " + "WHERE Year(Orders.ShippedDate) = " + year + ";"; c1r.DataSource.RecordSource = sSQL;

// 自定义报表时间处理 string sScript = "If SalespersonTotal > " + goal + " Then \n" +
" ExceededGoalLabel.Visible = True\n" +
" SalespersonLine.Visible = True\n" +
"Else\n" +
" ExceededGoalLabel.Visible = False\n" +
" SalespersonLine.Visible = False\n" + "End If"; c1r.Sections[SectionTypeEnum.GroupHeader2].OnPrint = sScript;

// 把报表呈现到临时文件
c1r.RenderToFile(fileOut, fmt); // 重新寄送报表文件
Response.Redirect("Temp/" + rpt + ext); } catch (Exception x) {
_lblStatus.Text = "*** " + x.Message;
}
}

RenderReport程序比较长,但是也很简单。开头解决输入和输出文件的名字。所有文件的名字都相对于当前程序的目录。
接着,程序创建了一个C1Report组件然后载入"Employee Sales by Country"报表。这是一个初始的报表,在下一步将对它进行定制。
用户输入的参数可以从_lstYear和_txtGoal服务器端控件中得到。代码读取这些值然后使用它们来定制报表的
RecordSource属性并且为OnPrint属性构建了一个VBScript处理程序。上一个章节中提及到这些技术。一旦报表定义准备好了,代码就调用RenderToFile方法让C1Report组件写HTML或PDF文件到输出目录。在方法返回后,报表就可以被显示给用户了。
最后一步就是调用Response.Redirect,以在用户的浏览器上显示刚刚创建的报表。
注意所有的代码被包括在一个try/catch块中。如果在生成报表的时候发生任何错误,用户可以看见描述问题的错误信息。

  1. 最后,需要添加一些简单的辅助程序:

Visual Basic

Visual Basic

'获取加载和保存文件的路径
Private Function GetDataPath() As String
Return Request.PhysicalApplicationPath + "Data\"
End Function
Private Function GetOutputPath() As String
Return Request.PhysicalApplicationPath + "Temp\"
End Function

C#

C#

//获取加载和保存文件的路径
private string GetDataPath() { return Request.PhysicalApplicationPath + @"Data\"; } private string GetOutputPath() { return Request.PhysicalApplicationPath + @"Temp\";
}

  1. 完成这些代码,程序就准备好了。可以按F5在Visual Studio中监视运行情况。


下面的屏幕截图展示了程序在浏览器中显示的样子:

数据的分组与排序

!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
本节中将为你展示如何使用数据分组与排序,动态求和以及创建表达式等方式对报表中的数据进行有效的管理和组织。
数据分组数据分组
完成基本布局的设计之后,你可以按照某些字段或者其他条件对报表内容进行分组,从而使报表阅读起来更加容易。通过分组,你可以将报表更加直观的分割成几部分从而对每个分组单独进行介绍和数据汇总。报表主要依靠分组表达式来进行分组。分组表达式主要基于一个或者多个字段集,当然你也可以根据需要设置更加复杂的分组条件。
你可以使用C1ReportDesigner程序或者使用代码来实现报表的数据分组:使用使用C1ReportDesigner进行分组和排序进行分组和排序
即使不打算显示分组的页眉和页脚部分,你也可以使用分组来对数据进行排序。你可以使用C1ReportDesigner对你的报表进行分组,具体位置如下图所示。

要在报表中添加或编辑分组,你需要完成以下步骤:

  1. 打开C1ReportDesigner应用程序。更多细节,可以参阅葡萄城系列文档中的Accessing C1ReportDesigner from

Visual Studio章节。

  1. 在Data分组中的Design选项卡上单击Sorting and Grouping按钮。单击之后,将会弹出Sorting and Grouping 对话框。你可以在此页面中创建、编辑、排序和删除分组。
  2. 单击Add按钮,创建一个分组,并设置新分组的属性。Group By字段定义数据将如何在报表中分组。对于简单的分组,你可以直接从下拉列表中选择字段。对于更复杂的分组,你可以输入分组表达式。例如,您可以使用国家字段来分组或者使用Left(Country, 1)表达式通过国家首字母来分组。
  3. 本示例中,选择Country作为Group By的表达式。
  4. 接下来,选择你想要的排序类型(本例中选择升序(Ascending))。你还可以指定新分组的页眉和页脚部分是否可见,以及分组是否在同一页面上进行呈现。

    注意:注意: 你不能将备注型字段或者二进制(对象)字段用于分组和排序,这是OLEDB对此进行的限制。

    Sorting and Grouping对话框效果如下图所示:对话框效果如下图所示:

    如果你添加了很多字段,可以通过Group列表右侧的箭头按钮改变字段的顺序。该操作将自动调整报表分组中页眉和页脚的位置。如果想要删除某个字段,可以使用Delete按钮。
    完成字段设置之后,单击OK按钮关闭对话框,你可以在Designer中看到设置后的效果。报表中增加了页眉和页脚两个新的
    区域。新增区域(页眉和页脚)的高度均默认为0,你可以通过鼠标拖动边缘来扩展该区域。需要注意的是,页眉区域是可见的。而页脚区域是不可见的。因为在之前的对话框中,Group Header按钮已经被选中,而Group Footer按钮未被选中。不可见区域通过一个阴影图案表明该区域在报表中是不可见的。
    具体实现效果如下图所示:

    在新区域顶部的标题栏上的标签中,包含了该区域的名称以及分组Group By属性的值。为了了解分组是如何工作的,你可以单击Add Data Field按钮 ,从菜单中选择Country选项,在新创建的分组页眉区域增加一个位置。单击选中该字段,通过改变字体Font属性使字体更为醒目一些。
    使用代码添加分组和排序使用代码添加分组和排序
    好的报表并不只是简单的展示数据,更重要的是将数据有效的组织起来。C1Report使用groups来实现数据的分组和排序。
    为了说明分组是如何工作的,我们将回到报表主题模板的代码创建过程中,通过国籍对员工进行分组。
    下面的代码将展示如何创建一个分组对象,并且根据国籍对报表内容进行分组:
    Visual Basic

    Visual Basic

    If chkGroup.Checked Then
    ' group employees by country, in ascending order Dim grp As Group
    grp = c1r.Groups.Add("GrpCountry", "Country", SortEnum.Ascending)
    ' format the Header section for the new group
    With grp.SectionHeader
    .Height = 500 .Visible = True f = .Fields.Add("CtlCountry", "Country", 0, 0, c1r.Layout.Width, 500) f.Calculated = True
    f.Align = FieldAlignEnum.LeftMiddle
    f.Font.Bold = True
    f.Font.Size = 12
    f.BorderStyle = BorderStyleEnum.Solid
    f.BorderColor = Color.FromArgb(0, 0, 150)
    f.BackStyle = BackStyleEnum.Opaque
    f.BackColor = Color.FromArgb(150, 150, 220)
    f.MarginLeft = 100 End With

    ' sort employees by first name within each country c1r.Groups.Add("GrpName", "FirstName", SortEnum.Ascending)
    End If

    C#

    C#

    if (chkGroup.Checked) {
    // group employees by country, in ascending order
    Group grp = c1r.Groups.Add("GrpCountry", "Country", SortEnum.Ascending);

    // format the Header section for the new group s = grp.SectionHeader;
    s.Height = 500;
    s.Visible = true;

    f = s.Fields.Add("CtlCountry", "Country", 0, 0, c1r.Layout.Width, 500); f.Calculated = true;
    f.Align = FieldAlignEnum.LeftMiddle;
    f.Font.Bold = true;
    f.Font.Size = 12;
    f.BorderStyle = BorderStyleEnum.Solid;
    f.BorderColor = Color.FromArgb(0, 0, 150); //f.BackStyle = BackStyleEnum.Opaque;
    f.BackColor = Color.Transparent;
    f.BackColor = Color.FromArgb(150, 150, 220);
    f.MarginLeft = 100;
    // sort employees by first name within each country c1r.Groups.Add("GrpName", "FirstName", SortEnum.Ascending);
    }

    每个分组都有页眉和页脚部分。在默认情况下,它们均不可见。但是上面的代码把页眉部分设为可见,是为了显示该分组所属的国籍。然后它使用Country字段新增了一个额外字段,并且将该字段的背景色设为纯色。
    最后,代码中增加了另外一个分组,为了在国家分组中按照名字对员工排序。该分组仅用于排序,因此页眉和页脚部分都是不可见的。
    完成设置之后,你需要调用Render方法呈现报表从而完成本例程。在btnEmployees_Click的事件处理器中输入下面的代码:
    Visual Basic

    Visual Basic

    ' render the report into the PrintPreviewControl ppv.Document = c1r

    C#

    C#

    // render the report into the PrintPreviewControl ppv.Document = c1r;

    下图为使用分组报表的实际效果图:

    数据排序数据排序:
    你可以使用下面两种方式对数据进行排序:
    数据源对象自身排序(例如,使用SQL语句中的ORDER BY子句)
    在报表中添加分组并指定每个分组如何通过设置Group和Sort属性进行排序
    使用DataView.Sort属性进行排序,你只需要使用一个列名列表(不需要列名表达式)。因此,如果你使用DatePart("yyyy",
    dateColumn)作为分组表达式,控件将自动根据dateColunm字段中的日期字段进行排序,而不是像你所想的那样使用日期中的年份。
    根据日期排序,需要在数据表中增加一个计算列(通过修改SQL语句),然后根据计算列中的内容对报表进行分组或者排序。下面将为你展示一个XML的Sort属性,并举例说明这一过程。
    下图为你展示的就是C1ReportDesigner中的Sorting and Grouping编辑器。你可以通过指定字段来实现分组排序:

    如果你同时使用上述两种方法,在报表中设置的Sort属性将具备更高的优先级(它应用于数据从数据库中检索之后)。

    注意:注意:完整的报表,可参阅在报表模板文件下CommonTasks.xml文件的"19: Sorting"章节,该文件保存在
    ComponentOne示例文件目录下。

    增加动态求和
    !MISSING PHRASE 'Show All'!
    !MISSING PHRASE 'Hide All'!
    C1Report字段对象有一个RunningSum 属性,从而使其很容易的对分组或者整个报表中的数据进行动态求和。

    在分组中增加动态求和

    在分组中进行动态求和,需要完成以下步骤:
  5. 打开C1ReportDesigner应用程序。更多如何使用C1ReportDesigner的信息,可以参阅葡萄城系列文档中的

Accessing C1ReportDesigner from Visual Studio章节。

  1. 创建一个新报表或者打开一个已存在的报表。通过C1ReportDesigner打开之后,你就可以对报表属性进行修改。
  2. 单击Design按钮,开始编辑报表。
  3. 在设计模式下,从属性窗口上方的下拉列表中选择报表。报表中的有效属性将显示在这里。
  4. 在报表中增加一个calculate字段:
    1. 在Designer的工具栏中,单击Add Calculated Field按钮。
    2. 在VBScript编辑器中,输入以下脚本:Sum(ProductSalesCtl)
    3. 将鼠标拖过报表分组的页眉区域,然后光标会变成十字线样式。单击并拖动鼠标来重新定义该字段所占区域,然后释放鼠标按键完成新字段的创建。
  1. 将RunningSum属性设置为SumOverGroup(注意,如果想要显示该属性,属性过滤必须关闭。属性过滤设置按钮是属性窗口上的漏斗图标)

在整个报表中增加动态求和

如果想要实现跨页面动态求和,你需要使用脚本来完成这一功能。
例如,你可以增加一个pageSum字段到报表中,使用脚本对其进行更新。完成以下步骤来实现该功能:

  1. 打开C1ReportDesigner应用程序。更多如何使用C1ReportDesigner的细节,可以参阅葡萄城系列文档中的

Accessing C1ReportDesigner from Visual Studio章节。

  1. 创建一个新报表或打开一个已存在的报表。通过C1ReportDesigner打开之后,你可以对报表属性进行修改。
  2. 单击Design按钮,开始编辑报表。
  3. 在设计模式下,从属性窗口上方的下拉列表中选中相应报表。这里将显示报表中的可用属性。
  4. 找到OnPage属性,单击旁边的空白字段,然后点击省略号按钮。
  5. 在VBScript编辑框中,输入下面的VB表达式脚本代码:

' VBScript: Report.OnPage pageSum = 0

  1. 在属性窗口上方的下拉列表中选中Detail选项,此处将显示Detail区域的可用属性。
  2. 找到OnPrint属性,单击旁边的空白字段。然后单击省略号按钮。
  3. 在弹出VBScript编辑器中输入以下VBScript表达式脚本代码:

' VBScript: Detail.OnPrint
pageSum = pageSum + UnitsInStock

注意:注意:完整的报表可参阅在报表模板文件下CommonTasks.xml文件的"17: Running Sums"章节,该文件保存在 ComponentOne示例文件目录下。

添加汇总和其他合计
C1Report支持合计表达式的所有计算字段。合计表达式包含所有常用的函数如Sum, Avg, Min, Max, Count, Range,
StDev等等。
所有的合计功能都会使用一个表达式作为参数,并且根据表达式在报表中所处的位置来决定其计算范围。例如,在分组页眉和或者页脚中的合计,其范围是在分组内。在报表的页眉或者页脚中的合计,其范围是在报表内。
例如,下面的合计表达式将返回范围(分组或报表)内的Sales字段值的总和。
Sum(Sales)
下面的合计表达式将会返回报表中支付销售税款的总量(假设销售税为8.5%):
Sum(Sales * 0.085)
你可以使用域(domain)作为合计表达式的第二参数,从而缩小其作用范围。域参数是一个表达式,它将决定合计表达式当前范围内的值是否应该被包含在本次合计计算中。
例如,下面的合计表达式将返回所有产品类别为1的Sale字段值的总和:
Sum(Sales, Category = 1)
下面的合计表达式将返回金额超过10000美元的数量。
Count(*, Sales > 10000)

Note: For the complete report, see report "13: Subtotals and other Aggregates" in the CommonTasks.xml report definition file, which is available in the ComponentOne Samples folder.

建立交叉报表
交叉报表会在两个维度上对数据进行分组(横向或纵向)。交叉报表主要用于汇总报表中大量交叉引用的数据信息。想要创建交叉报表,首先你需要调用GROUP BY查询语句将数据汇总到行中,然后使用转化(某个支点)服务创建分组的列。转化服务通常由数据库服务自身提供,它可以是一个自定义程序,或者你也可以使用C1Report内置的域合计功能。无论何时,交叉报表中最重要的元素都是数据的原始汇总视图。例如,一个典型的汇总视图如下所示:

Year

Quarter

Amount

1990

1

1.1

1990

2

1.2

1990

3

1.3

1990

4

1.4

1991

1

2.1

1991

2

2.2

1991

3

2.3

1991

4

2.4

我们首先为每季度添加一个新列,并且将合并之后的值加入到新列中。通过这种方式改变数据后的效果如下图所示:

Year

Total

Q1

Q2

Q3

Q4

1990

5

1.1

1.2

1.3

1.4

1991

9

2.1

2.2

2.3

2.4

您可以使用C1Report合计函数完成该操作。该报表将会依据年份进行分组。Detail区域将会隐藏,分组的页眉将会包含的合计表达式如下所示:

Year

Total

Q1

Q2

Q3

Q4

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6839f769-158c-44b8-af35-4052874b5111"><ac:plain-text-body><![CDATA[

[Year]

Sum(Amount)

Sum(Amount,
]]></ac:plain-text-body></ac:structured-macro>
Quarter=1)

Sum(Amount,
Quarter=2)

Sum(Amount,
Quarter=3)

Sum(Amount,
Quarter=4)

第一个合计表达式将计算当前年份中的销售总额,而指定季度的合计将会通过指定域的方式来限制合计表达式只能获取指定月份的数值。
To increase compatibility with code written in Visual Basic and Microsoft Access (VBA), C1Report exposes two functions that are not available in VBScript: Iif and Format.
Iif evaluates a Boolean excodession and returns one of two values depending on the result. For example:
Iif(SalesAmount > 1000, "Yes", "No")
Format converts a value into a string formatted according to instructions contained in a format excodession. The value may be a number, Boolean, date, or string. The format is a string built using syntax similar to the format string used in Visual Basic or VBA.
The following table describes the syntax used for the format string:

Value Type

Format String

Description

Number

Percent, %

Formats a number as a percentage, with zero or two decimal places. For example: Format(0.33, "Percent") = "33%" Format(0.3333333, "Percent") = "33.33%"

 

#,###.##0

Formats a number using a mask. The following symbols are recognized: # digit placeholder 0 digit placeholder, force display , use thousand separators ( enclose negative values in parenthesis % format as percentage For example: Format(1234.1234, "#,###.##") =
"1,234.12"
Format(-1234, "#.00") = "(1234.12)"
Format(.1234, "#.##") = ".12"
Format(.1234, "0.##") = "0.12"
Format(.3, "#.##%") = "30.00%"

Currency

Currency, $

Formats a number as a currency value. Displays number with thousand separator, if appropriate; displays two digits to the right of the decimal separator. For example: Format(1234, "$") = "$1,234.00"

Boolean

Yes/No

Returns "Yes" or "No".

Date

Long Date

Format(#12/5/1#, "long date") = "December 5, 2001"

 

Short Date

Format(#12/5/1#, "short date") = "12/5/2001"

 

Medium Date

Format(#12/5/1#, "medium date") = "05-Dec-01"

 

q,m,d,w,yyyy

Returns a date part (quarter, month, day of the month, week of the year, year). For example: Format(#12/5/1#, "q") = "4"

String

@@-@@/@@

Formats a string using a mask. The "@" character is a placeholder for a single character (or for the whole value string if there is a single "@"). Other characters are intercodeted as literals. For example:
Format("AC55512", "@@-@@/@@") = "AC-555/12"
Format("AC55512", "@") = "AC55512"

 

@;Missing

Uses the format string on the left of the semi-colon if the value is not null or an empty string, otherwise returns the part on the right of the semi-colon. For example: Format("@;Missing", "UK") = "UK"
Format("@;Missing", "") = "Missing"

Note that VBScript has its own built-in formatting functions (FormatNumber, FormatCurrency, FormatPercent,
FormatDateTime, and so on). You may use them instead of the VBA-style Format function described here.
修改字段
你除了可以使用VBScript来执行已计算字段中的表达式外,还可以指定在报表渲染完成后触发特定的脚本,同时,你也能够使用脚本去修改报表的格式化方式。这些脚本都包含在事件属性(event properties)中。这里的事件属性与Visual
Basic中的事件处理程序(event handler)很相似,唯一不同的是,这些脚本是在报表自身的运行域内被执行的,而不是在显示报表的应用程序域内被执行。例如,通过给事件属性赋值的方式,你可以根据字段的值来设定该字段的字体和前景色。这个行为会被报表保存下来,并成为其自身的一部分,而与渲染该报表的应用程序本身无关。
当然,传统的事件也是依旧可用的,在那些需要影响应用程序本身而不是报表的地方,你就应该使用传统的事件去实现。例如,你可以在你的应用程序中为开始页面(StartPage)事件写一个事件处理程序,用于更新页数,而并不用去关心何种报表在该页面中渲染。
下表罗列了事件属性中的可用于设定的属性以及他们的典型用法。

对象

属性

描述

Report

OnOpen

报表开始渲染时触发。可用于修改连接字符串(ConnectionString)或记录源
(RecordSource )属性,或者初始化VBScript变量

 

OnClose

报表渲染结束后触发。可用于执行相关清理任务。

 

OnNoData

当报表开始渲染,且数据源的记录集为空时触发。你可以在此时将Cancel属性设定为True来取消报表的生成。
你也可以显示一个对话框,来提醒用户报表为何没有被显示。

 

OnPage

当一个新的页面开始时触发。可用于根据一些条件来设定部分字段的Visible属性。当一个新的页面开始时,控件维持着一个Page变量,每当新的页面开始时,它会自动递增。

 

OnError

当发生错误时触发。

Section

OnFormat

在字段被运算前触发。此时,源记录集中的字段反映了将要渲染的值,但报表中字段不能反映。

 

OnPrint

在字段被输出前触发。此时,字段已经被运算,你可以执行条件格式化。

根据字段值进行格式化
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
根据字段值进行格式化可能是OnPrint属性的最常见用法。例如报表显示了一组基于产品(product)分组的订单
(order)数据。除了使用一个额外的字段来显示库存量外,报表能够将那些低于重定货限值的产品的名称用红色粗体字符着重显示出来。
使用如下代码来着重显示那些低于重定货限值的产品:
将那些低于重定货限值的产品的名称用粗体的红色着重显示的事件,事件脚本如下:
Visual Basic

Visual Basic

Dim script As String = _
"If UnitsInStock < ReorderLevel Then" & vbCrLf & _
"ProductNameCtl.ForeColor = RGB(255,0,0)" & vbCrLf & _
"ProductNameCtl.Font.Bold = True" & vbCrLf & _
"Else" & vbCrLf & _
"ProductNameCtl.ForeColor = RGB(0,0,0)" & vbCrLf & _
"ProductNameCtl.Font.Bold = False" & vbCrLf & _ "End If" c1r.Sections.Detail.OnPrint = script

C#

C#

string script = "if (UnitsInStock < ReorderLevel) then\r\n" +
"ProductNameCtl.ForeColor = rgb(255,0,0)\r\n" +
"ProductNameCtl.Font.Bold = true\r\n" +
"else\r\n" +
"ProductNameCtl.ForeColor = rgb(0,0,0)\r\n" +
"ProductNameCtl.Font.Bold = false\r\n" + "end if\r\n"; c1r.Sections.Detail.OnPrint = script;

代码生成了一个包含VBScript事件处理代码的字符串,然后将字符串赋给某个区域(section)上的的OnPrint 属性

通过C1报表设计器(C1ReportDesigner)将那些低于重定货限值的产品着重显示

除了编写代码外,你可以使用C1报表设计器(C1ReportDesigner)将下面的脚本代码直接输入到详情区域(Detail section)的OnPrint属性的VBScript编辑器中。完整步骤如下:

  1. 从设计器的属性窗口下拉列表中选择Detail。这样会显示出该区域(section)的全部可用属性。
  2. 点击OnPrint属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor)
  3. 在VBScript编辑器中,直接输入下面脚本代码:

If UnitsInStock < ReorderLevel Then ProductNameCtl.ForeColor = RGB(255,0,0)

ProductNameCtl.Font.Bold = True Else ProductNameCtl.ForeColor = RGB(0,0,0) ProductNameCtl.Font.Bold = False End If
当这个区域将要被输出时,控件会执行刚才输入的VBScript代码。脚本获取数据库字段"ReorderLevel"的值,然后根据获取的值来设置报表字段"ProductName"的Font.Bold 和 ForeColor属性。如果产品低于重定货限值,它的名称将会显示为加粗的红色。下方的截图展示了带有该显示效果的报表的一部分:

无数据时隐藏该区域
!MISSING PHRASE 'Show All'! !MISSING PHRASE 'Hide All'!
通过给Detail区域的OnFormat属性指定表达式,你可以根据该字段的数据来改变该字段的显示格式例如,你的Detail区域中有个字段带有image控件,当对应记录的图像不存在时,你可能希望隐藏这个记录。想要隐藏这样没有数据的Detail区域,请在Detail区域的OnFormat属性中添加如下脚本:
If isnull(PictureFieldName) Then
Detail.Visible = false
Else
Detail.Visible = true
End If

隐藏无数据的区域请用如下代码:

如果想隐藏无数据的区域,这个例子中指的是记录的图像数据不存在时,请使用如下脚本代码:
Visual Basic

Visual Basic

C1Report1.Sections.Detail.OnFormat = "Detail.Visible = not isnull(PictureFieldName)"


C#

C#

c1Report1.Sections.Detail.OnFormat = "Detail.Visible = not isnull(PictureFieldName)";

使用C1报表设计器实现隐藏无数据对应区域:

除了编写代码外,你还可以使用C1报表设计器(C1ReportDesigner)将下面的脚本代码直接输入到Detail区域(Detail section)的OnFormat属性的VBScript编辑器中。完整步骤如下:

  1. 从设计器的属性窗口下拉列表中选择Detail。这样会显示出该区域(section)的全部可用属性。
  2. 点击OnFormat 属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor) 3. 在VBScript编辑器中,直接输入下方的脚本代码: 在窗口中键入下面脚本:

If isnull(PictureFieldName) Then
Detail.Visible = false
Else
Detail.Visible = true
End If 也可以使用更简洁一些的脚本: Detail.Visible = not isnull(PictureFieldName)
基于值来显示或隐藏字段
!MISSING PHRASE 'Show All'! !MISSING PHRASE 'Hide All'!
除了改变字段的格式来着重显示内容外,你还可以通过设置另一个字段的Visible属性为True或False来创建特殊效果。例如,如果你创建了一个新的字段叫"BoxCtl",并且将其格式化显示为产品名称外框是加粗的矩形,然后你就可以像下面这样修改脚本:
If UnitsInStock < ReorderLevel Then
BoxCtl.Visible = True
Else
BoxCtl.Visible = False
End If
用代码将无法预定的产品着重显示:
使用事件脚本将无法预定的产品以外面套一个框的形式着重显示,看起来像这样:
Visual Basic

Visual Basic

Dim script As String = _
"If UnitsInStock < ReorderLevel Then" & vbCrLf & _
" BoxCtl.Visible = True" & vbCrLf & _
"Else" & vbCrLf & _

" BoxCtl.Visible = False" & vbCrLf & _ "End If"
c1r.Sections.Detail.OnPrint = script
C#

C#

string script = "if (UnitsInStock < ReorderLevel) then\r\n" +
"BoxCtl.Visible = true\r\n" +
"else\r\n" +
"BoxCtl.Visible = false\r\n" + "end if\r\n"; c1r.Sections.Detail.OnPrint = script;


代码生成了一个包含VBScript事件处理代码的字符串,然后将字符串赋给某个区域(section)上的的OnPrint 属性通过C1报表设计器(C1ReportDesigner)将那些低于重定货限值的产品着重显示:
除了编写代码,你还可以使用C1报表设计器(C1ReportDesigner)将下面的脚本代码直接输入到详情区域(Detail section)的OnPrint属性的VBScript编辑器中。完整步骤如下:

  1. 从设计器的属性窗口下拉列表中选择Detail。这样会显示出该区域(section)的全部可用属性。
  2. 点击OnPrint 属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor)
  3. 在VBScript编辑器中,直接输入下方的脚本代码:

If UnitsInStock < ReorderLevel Then
BoxCtl.Visible = True
Else
BoxCtl.Visible = False End If
下方的截图显示了带有该显示效果的报表的一部分:

从用户处获取参数
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
除了着重显示那些存储在数据库中的低于重定货限值的产品外,你还可以让报表去提示用户那些可供订购的产品。为了从用户处获取数据,你需要修改报表的RecordSource 属性为使用参数查询(如果想进一步了解如何创建参数查询,请查阅Parameter Queries)
Visual Basic

Visual Basic

c1r.DataSource.RecordSource = _ "PARAMETERS [Critical Stock Level] Short 10;" & _ c1r.DataSource.RecordSource

C#

C#

c1r.DataSource.RecordSource = "PARAMETERS [Critical Stock Level] short 10;" + c1r.DataSource.RecordSource;

这个设置是让控件提示用户输入一个"即将缺货"的限值,这个值保存在全局的VBScript 变量中,你可以在你的事件中使用它。这个变量的默认值是10.
如果要使用这个由用户指定的值,脚本应该修改成这样:
Visual Basic

Visual Basic

 

Dim script As String = _
"level = [Critical Stock Level]" & vbCrLf & _

"If UnitsInStock < level Then" & vbCrLf & _
" ProductNameCtl.ForeColor = RGB(255,0,0)" & vbCrLf & _
" ProductNameCtl.Font.Bold = True" & vbCrLf & _
"Else" & vbCrLf & _
" ProductNameCtl.ForeColor = RGB(0,0,0)" & vbCrLf & _
" ProductNameCtl.Font.Bold = False" & vbCrLf & _ "End If" c1r.Sections("Detail").OnPrint = script

C#

C#

string script = "level = [Critical Stock Level]\r\n" +
"if (UnitsInStock < level) then\r\n" +
"ProductNameCtl.ForeColor = rgb(255,0,0)\r\n" +
"ProductNameCtl.Font.Bold = true\r\n" +
"else\r\n" +
"ProductNameCtl.ForeColor = rgb(0,0,0)\r\n" +
"ProductNameCtl.Font.Bold = false\r\n" + "end if\r\n"; c1r.Sections.Detail.OnPrint = script;


区别在于脚本的头两行。不同于将"UnitsInStock"字段的当前值与存储在数据库中的重定货限值相比较,这里的脚本将它与用户输入的并存储在名为"[Critical Stock Level]"的VBScript 变量中的值相比较。
<span style="color: #3f529c">重置页计数器</span>
Unable to render embedded object: File (MISSING PHRASE 'Show All') not found.
Unable to render embedded object: File (MISSING PHRASE 'Hide All') not found.
控件会自动创建和更新Page变量的值。这对于在页眉和页脚添加页数非常有用。
分组开始时重置页面计数器:某些情况下,在分组开始时你可能会需要去重置页面的计数器。例如,一个报表根据"国家"(country)字段分组显示,并且它有一个带表达式的已计算的页脚字段:
=[Country] &" - Page "& [Page] <span style="color: #3f529c">使用代码<strong>:</strong></span>
通过设置页脚字段的Text属性,可以在分组(例如,一个新的国家)开始时重置页计数器。输入如下代码:
Visual Basic


Visual Basic

C1Report1.Fields("PageFooter").Text = "[ShipCountry] & "" "" & [Page]"

C#

C#

 

c1Report1.Fields("PageFooter").Text = "\[ShipCountry\] + "" "" + \[Page\]";


<span style="color: #3f529c">使用<strong>C1</strong>报表设计器<strong>:</strong></span>
通过设置页脚字段的Text属性,可以在分组(例如,一个新的国家)开始时重置页计数器。完整步骤如下:


  1. 从设计器的属性窗口下拉列表中选择页脚(PageFooter)的PageNumber字段,这样会显示出该字段的全部可用属性。
  2. 点击Text属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor)
  3. 在VBScript编辑器中,直接输入下方的脚本代码:


=[Country] &" - Page "& [Page]
<span style="color: #3f529c">修改字段的大小来创建柱状图(<strong>Bar Chart</strong>)</span>
Unable to render embedded object: File (MISSING PHRASE 'Show All') not found.
Unable to render embedded object: File (MISSING PHRASE 'Hide All') not found.
这是最复杂的一个例子。除了以文本形式显示字段值以外,你还可以改变字段的大小来创建一个图表。


创建图表


如果要创建图表,你首先要做的是确定图表的比例,也就是用于确定图表中最大最小值的度量方法。在"销售图表"报表中有一个字段就是用来实现这个目的的。有个名为SaleAmountMaxFld的页脚字段,包含了你想要画到图表上的最长的那条图形的值,它带有如下表达式:
=Max([SaleAmount])


使用代码:

通过设置SaleAmountMaxFld字段的的Text属性,来设置图表的最大值。输入如下代码:
Visual Basic

Visual Basic

SaleAmountMaxFld.Text = "Max([SaleAmount])"

C#

C#

SaleAmountMaxFld.Text = "Max([SaleAmount])";

使用C1报表设计器:

通过设置SaleAmountMaxFld字段的的Text属性,来设置图表的最大值。完整步骤如下:

  1. 从设计器的属性窗口下拉列表中选择SaleAmountMaxFld 。这样会显示出该字段的全部可用属性
  2. 点击Text属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor)
  3. 在VBScript编辑器中,直接输入下方的脚本代码: =Max([SaleAmount])

绘制柱状图

为了绘制柱状图,报表中包含一个名为BarFld的详情字段,该字段被格式化显示为实心的矩形。在Detail区域的OnPrint 属性上,带有如下脚本:BarFld.Width = SaleAmountMaxFld.Width * (SaleAmountFld / SaleAmountMaxFld) 这个表达式根据相关字段的宽度和值,以及当前记录的SaleAmountFld 字段的值来计算柱状条的宽度。
使用代码:使用代码:
通过设置OnPrint属性来绘制柱状图。请输入如下代码:
Visual Basic

Visual Basic

c1r.Sections.Detail.OnPrint = & _ "BarFld.Width = SaleAmountMaxFld.Width * " & _
"(SaleAmountFld / SaleAmountMaxFld)"

C#

C#

c1r.Sections.Detail.OnPrint = + "BarFld.Width = SaleAmountMaxFld.Width * " +
"(SaleAmountFld / SaleAmountMaxFld)";

使用使用C1报表设计器:报表设计器:
通过设置OnPrint属性来绘制柱状图。完整步骤如下:

  1. 从设计器的属性窗口下拉列表中选择Detail 。这样会显示出该Detail区域的全部可用属性
  2. 点击OnPrint 属性旁边的空白框,然后点击下拉箭头,从列表中选择脚本编辑器(Script Editor)
  3. 在VBScript编辑器中,直接输入下方的脚本代码:

BarFld.Width = SaleAmountMaxFld.Width * (SaleAmountFld / SaleAmountMaxFld)
下方的截图显示了带有该显示效果的"销售图表"报表的一部分

如果要查看完整的"销售图表"报表,你可以在ComponentOne 范例文件夹内的NorthWind 范例中查看报表定义文件
Nwind.xml。
高级功能
本节将介绍如何添加参数查询,创建无绑定报表,使用自定义数据源和为报表添加数据安全过滤器。
参数查询
参数查询是通过显示对话框提示用户输入或选择,根据用户输入的信息执行查询,例如使用要查询的记录作为标准值或报表字段值提示用户。 参数查询可以提示用户输入一个或多个值进行数据查询,例如,用户可以输入两个日期:起始日期和截止日期, C1Report会根据用户输入的两个日期,查询满足这两个日期之间的数据记录。
可以使用参数查询功能创建月收入报表。 当加载报表时,C1Report会显示一个信息对话框,要求您选择希望查看的月份, C1Report会根据您的选择,加载相应的月收入报表。
创建参数查询,需要在RecordSource属性中编辑带参数的SQL语句。 创建参数查询的语句与Microsoft Access提供的相同。

  1. 创建参数查询的最简单的方法是在SQL语句的WHERE条件下添加一个或多个参数表达式,然后使用参数手动替换表达式的固定值。 例如,先从普通的SQL语句:


strSQL = "SELECT DISTINCTROW * FROM Employees " & _
"INNER JOIN (Orders INNER JOIN [Order Subtotals] " & _
"ON Orders.OrderID = [Order Subtotals].OrderID) " & _
"ON Employees.EmployeeID = Orders.EmployeeID " & _
"WHERE (((Orders.ShippedDate) " & _
"Between #1/1/1994# And #1/1/2001#));"


  1. 下一步是确定将要用参数替换的SQL语句中的表达式。 上述的例子中,参数替代的就是WHERE子句中日期的表示,黑体字表示的。 参数名为Beginning Date和Ending Date 。 由于这些名称中包含'空格'字符,需要将各个参数使用方括号括起来:



strSQL = "SELECT DISTINCTROW * FROM Employees " & _
"INNER JOIN (Orders INNER JOIN [Order Subtotals] " & _
"ON Orders.OrderID = [Order Subtotals].OrderID) " & _
"ON Employees.EmployeeID = Orders.EmployeeID " & _
"WHERE (((Orders.ShippedDate) " & _
"Between [Beginning Date] And [Ending Date]));"


  1. 最后一点,参数必须在SQL语句的开始使用带有PARAMETERS的关键词声明,参数声明包括参数名称,类型和缺省值的声明:



strSQL = "PARAMETERS [Beginning Date] DateTime 1/1/1994, " & _
"[Ending Date] DateTime 1/1/2001;" & _
"SELECT DISTINCTROW * FROM Employees " & _
"INNER JOIN (Orders INNER JOIN [Order Subtotals] " & _
"ON Orders.OrderID = [Order Subtotals].OrderID) " & _
"ON Employees.EmployeeID = Orders.EmployeeID " & _
"WHERE (((Orders.ShippedDate) " & _ "Between [Beginning Date] And [Ending Date]));"
一旦执行到该语句时,C1Report将显示一个对话框,提示用户输入"开始日期(Beginning Date)"和"结束日期(Ending Date)"的值。 根据用户输入的日期值生成相应时间段的报表。
该对话框通过C1Report即时创建的。 该对话框会显示查询需要输入的所有参数,并根据参数类型选择合适的输入控件。 例如,复选框用于表示布尔类型的参数,日期时间选择器控件用于表示日期类型的参数。 如图显示了上节的SQL 语句呈现的对话框:

PARAMETERS的语法是使用"逗号"分隔的参数的属性声明,使用"分号"表示参数声明结束。 每个逗号里面的内容都描述了参数的属性,包含以下信息:
参数名称 : 如果名称中包含空格字符,则必须使用方括号(如[起始日期])。 参数名称会显示在参数查询对话框中,也会出现在SQL语句中的WHERE语句中,在SQL语句中会被实际的用户输入替换。
参数类型:以下是C1Report支持的数据类型:


类型名称类型名称

ADO类型类型

Date

adDate

DateTime

adDate

Bit, Byte, Short, Long

adInteger

Currency

adCurrency

Single

adSingle

Double

adDouble

 

 

Text, String

adBSTR

Boolean, Bool, YesNo

adBoolean

缺省值 :在对话框中显示的初始值。
创建参数化查询的最简单的方法是增量式的。首先使用一个简单的查询,然后在查询语句中添加参数定义语句(不要忘记输入分号结束参数定义语句)。 最后,编写WHERE子句,并在适当的地方添加参数名。
您可以使用GetRecordSource方法来获取有关参数查询的正确的SQL语句(不具有参数语句)。 如果你想使用报表的数据来创建自己的数据集,参数查询会非常有用。

注意:注意: 如果不使用参数查询,你可以使用Visual Basic或C#语言编写代码来创建对话框,从而获得用户的输入信息,并根据需要修改SQL语句或设置 数据源 对象的 过滤器 属性。 使用参数查询的优点在于,参数查询逻辑是嵌入于报表内部的,独立于浏览器应用程序。 (同时,这也节省了编写代码的时间。)

无绑定报表
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
无绑定报表是没有底层数据源的报表记录集。 这种报表类型在以下两种情况下是非常有用的:
当创建具有固定格式的文档时,只有少量的字段内容每次发生改变时需要重新渲染文档。 业务表单是一个典型的例子:表单具有固定的格式且字段值会发生的变化。
将多个总结报表合并为一个汇总报表。 在这种情况下,创建无绑定的主报表,然后在该主报表中添加已绑定数据源的子报表。
举一个简单的无绑定报表的例子,创建一个简单的没有源记录的通讯录。 使用C1ReportDesigner就可以完成该功能,
只需要将ConnectionString和RecordSource属性设为空,并在报表中添加占位符。 占位符字段是简单的标签,其内容会通过应用程序来设置。
假如需要创建名称中包含六个占位符的报表如"FldHeadlineXXX"和"FldBodyXXX"(其中XXX为1至3)占位符域,你可以使用下面的代码来渲染报表:
Visual Basic

Visual Basic

Private Sub MakeReport()
' find report definition file
Dim path As String = Application.StartupPath
Dim i As Integer = path.IndexOf("\bin") If i > -1 Then path = path.Substring(0, i) path = path & "\"
' load unbound report c1r.Load(path & "Newsletter.xml", "NewsLetter")
' set field values c1r.Fields("FldHeadline1").Text = "C1Report Launched" c1r.Fields("FldBody1").Text = "ComponentOne unveils..."

c1r.Fields("FldHeadline2").Text = "Competitive Upgrades" c1r.Fields("FldBody2").Text = "Get ahead ..." c1r.Fields("FldHeadline3").Text = "C1Report Designer" c1r.Fields("FldBody3").Text = "The C1Report Designer..."
' done, show the report c1ppv.Document = c1r
' and/or save it to an HTML document so your subscribers
' can get to it over the Web
c1r.RenderToFile(path & "Newsletter.htm", FileFormatEnum.HTML)
End Sub

C#

C#

private void MakeReport() {

// find report definition file string path = Application.StartupPath; int i = path.IndexOf("\bin"); if ( i > -1 ) { path = path.Substring(0, i) path = path + "\";
// load unbound report c1r.Load(path + "Newsletter.xml", "NewsLetter");
// set field values c1r.Fields["FldHeadline1"].Text = "C1Report Launched"; c1r.Fields["FldBody1"].Text = "ComponentOne unveils..."; c1r.Fields["FldHeadline2"].Text = "Competitive Upgrades"; c1r.Fields["FldBody2"].Text = "get { ahead ..."; c1r.Fields["FldHeadline3"].Text = "C1Report Designer"; c1r.Fields["FldBody3"].Text = "The C1Report Designer...";

// done, show the report c1ppv.Document = c1r;
// and/or save it to an HTML document so your subscribers
// can get to it over the Web
c1r.RenderToFile(path + "Newsletter.htm", FileFormatEnum.HTML);
}

下面是ComponentOne的通讯原型。 注意,我们提供的简单的程序没有处理任何格式问题; 它只是简单的呈现了报表内容。 示例中显示的报表是用C1ReportDesigner应用程序创建的,报表定义时考虑了所有的格式,包括带Logo的标题,页脚,字体和文本定位。
内容和格式分离是无绑定报表的主要优点之一。

自定义数据源
通常情况下,C1Report使用ConnectionString和RecordSource属性创建报表内部的数据表(DataTable)对象作为数据源。当然,您也可以自定义数据集,并直接将值赋给Recordset属性。 这种情况下,C1Report会使用自定义的数据集来代替其自身提供的数据集。
您可以为Recordset属性指定三种类型的数据对象: 数据表 , 数据视图 ,或者已实现IC1ReportRecordset接口的任何对象。
使用个性化的数据表(DataTable)对象
!MISSING PHRASE 'Show All'!
!MISSING PHRASE 'Hide All'!
创建全新的DataTable。 您可能需要实现安全模式的功能或使用其他方法自定义对象。
为了能够使用自定义的DataTable对象,只需在渲染报表之前将自定义的DataTable指定给Recordset属性。 例如:
Visual Basic

Visual Basic

Private Sub CreateReport(strSelect As String, strConn As String)
' fill a DataSet object Dim da As OleDbDataAdapter
da = new OleDbDataAdapter(strSelect, strConn)
Dim ds As DataSet = new DataSet()

da.Fill(ds)
' get the DataTable object
Dim dt As DataTable = ds.Tables(0)
' load report c1r.Load("RepDef.xml", "My Report")
' render report c1r.DataSource.Recordset = ds.Tables(0) c1ppv.Document = c1r End Sub
C#

C#

private void CreateReport(string strSelect, string strConn) {

// fill a DataSet object OleDbDataAdapter da;
da = new OleDbDataAdapter(strSelect, strConn); DataSet DataSet ds = new DataSet(); da.Fill(ds);
// get the DataTable object
DataTable dt = ds.Tables[0];

// load report c1r.Load("RepDef.xml", "My Report");
// render report c1r.DataSource.Recordset = ds.Tables[0]; c1ppv.Document = c1r;

}

上述代码使用ADO.NET的标准要求创建了自定义的DataTable对象,然后将该表格数据指定给Recordset属性。 注意,你也可以不依赖实际的数据库,即时创建并填充DataTable对象。编写自己的自定义记录集对象
为了达到自定义数据源的极致,即你可以实现任何自定义的数据源对象。 以下情况描述了该功能使用的场景:

  1. 您的数据已经加载到内存中。
  2. 部分或全部数据已按需求计算,甚至没有计算,直到你发出请求时才执行数据计算。
  3. 数据可来多个不同的数据源,同时你也没有一个便捷的方法来创建一个标准的DataTable对象。

为了实现自己的数据源对象,你需要创建一个实现IC1ReportRecordset接口的对象。 此接口包含几个简单的方法,详情可见本文档的参考部分。在创建自定义数据源对象之后,你只需要做的就是创建一个它的实例,并赋值给Recordset属性。
如需要查看完整的项目,请见ComponentOne Samples文件夹目录下的CustomData示例,。
数据安全性
数据的安全性问题对于大多数公司是非常重要的。 例如您打算创建并分发有关公司员工的电话录的报表,需要显示员工姓名和电话分机。 你不希望浏览者修改报表,或是创建包含了员工薪资等机密信息的报表。 还有一种情况就是,有人可以通过查看报表定义,可获取数据源的连接字符串,就可以浏览(或黑客)数据库记录。
这些担忧也都是合情合理的,而且影响到各类的基于数据开发的应用程序,包括C1Report。 本节讨论保护数据安全可采取的措施。

  • No labels