应用 MS Reporting Services 101(使用智能客户端)






4.76/5 (26投票s)
2006年8月24日
14分钟阅读

141037

1174
使用智能客户端主机开始使用 MS Reporting Services 的应用方法
引言
我还记得那是一个做得非常棒的报表,它为我赢得了第一次加薪(每个人都喜欢加薪,对吧?)。从那时起,我对报表编写就充满了热情。在这篇文章中,我将一步一步地指导您如何使用 MS Reporting Services 2005 创建一个简单的报表;并将其托管在 Smart Client 应用程序中。
那么,您准备好分享加薪的喜悦了吗?为什么不呢!谁知道呢,您精心制作的报表或许就能带来这样的结果。
在这篇文章之前,我写了另外三篇,它们解决了与报表服务相关的不同问题。然而,它们都面向中高级读者。根据我收到的所有反馈,有一个普遍的请求:很多人希望有一篇专门针对初学者级别的文章。
我假设读者对 Visual Studio 2005 IDE 有基本的了解,并且熟悉使用 C# 编写代码。您不必了解 MS Reporting Services 即可理解本文;尽管如此,任何以前的报表编写经验都有助于您更快地掌握。
虽然我称这篇文章为 101,但我的意图是采用实践方法,而不是讨论报表服务相关的每一个话题。我将重点介绍最常见的报表设计方面以及最常用的控件。我强烈建议您查阅 MSDN 文档以获取更详细的信息。
*已更新,添加了 Access 数据库接口。
撸起袖子,开始报表制作
请看 图 1。这个报表有多复杂?您觉得创建这样的报表需要多长时间?嗯,就复杂性而言,这是一个从源文件 NorthWind->Products (SQL Server 2000) 中提取出来的简单报表,列出了所有产品的详细信息以及汇总总计。
至于时间,显然,它不应该花费您几个小时。至于研发和试错时间,我留给您;深入探索,越深入您会发现越好的宝藏。
就是这样,价值百万美元的问题:如何开始?第一步是什么?
通常,找出第一步是什么很容易。您见过在没有地基的情况下建造房屋吗?没有!那么,我在这里给您提示了吗?当然,我们必须先开发 Smart Client 来托管我们的报表。
步骤 1:创建 Windows 应用程序项目
请执行以下操作来创建 Windows 应用程序(Smart Client)项目
- 选择 文件菜单 -> 新建 -> 项目。
- 在 项目类型窗格 中选择 C#。
- 在 模板窗格 中,为 Visual C# 项目选择 Windows 应用程序。
在“名称”框中,为项目命名一个唯一的名称(我为附加的项目代码命名为 - rsWin101
),以表明应用程序的用途。在“位置”框中,输入要保存项目的目录,或单击“浏览”按钮进行导航。完成后,您会发现 Form1
已添加到项目中,您可以使用 Forms Designer 开始对其进行操作。
请更新 Form1
的以下属性
Form1.Text = “MS Reporting Services 101 with Smart Client”
Form1.Size = 750, 300
您可以根据需要自由更改 Form1
的任何其他属性。
步骤 2:将 Report Viewer 添加到窗体
那么,什么是报表查看器?就像我们需要 DVD 播放器来播放 DVD 一样;报表也一样,我们需要一个报表查看器来预览报表。
对于所有刚接触报表编写的人,我想说,报表查看器赋予了您的报表生命。它不仅可以预览输出,还可以帮助您以各种流行格式(pdf、Excel 等)生成信息。您还可以打印出正在查看的报表的硬拷贝。
请执行以下操作,将 Report Viewer 控件设置到 Form1
上
- 从工具箱拖动 工具箱 -> 数据 -> ReportViewer 并将其拖放到
Form1
上。此步骤将创建一个名为reportViewer1
的新ReportViewer
实例。我一直想将reportViewer1
重命名为rpvAbraKaDabra
,所以现在不能错过这个机会。既然我选择了rpvAbraKaDabra
,您也可以随意选择您喜欢的名字,让想象力尽情驰骋! - 通过设置
reportViewer1.Dock = Fill
,报表查看器将填充窗体的整个表面用于报表显示。
完成步骤 1 和步骤 2 后,您的项目应该如 图 2 所示。
步骤 3:将 DataSet 添加到项目
万岁!我们已经完成了基础。现在是时候为地基砌墙了;最终这些墙将支撑您家的门窗。DataSet
对 Report Viewer 来说就像是这样,它存储并提供来自数据源的原始数据,以便进行处理并准备在 Smart Client 界面上输出。
需要执行以下步骤将 DataSet
添加到项目
- 从“解决方案资源管理器”选择 添加 -> 新建项 -> DataSet。将名称从
DataSet1
更改为dsProduct
,然后单击“添加”按钮完成操作。
让我们向新创建的 DataSet
添加一个 DataTable
。DataTable
对于加载报表数据至关重要;我们在设计报表时将使用 DataSet
/DataTable
中的信息。
需要执行以下步骤将 DataTable
添加到 DataSet(dsProduct)
- 双击“解决方案资源管理器”中的
dsProduct
;它将打开设计器视图。右键单击设计器表面,选择 添加 -> DataTable。 请单击标题并将其名称更改为dtProductList
。请参见 图 3。
让我们开始为 DataTable(dtProductList)
添加列。您的设计器屏幕应如 图 4 所示。右键单击 dtProductList
并选择 添加 -> 列 以开始向 DataTable
添加列。
请为以下列重复此操作
ProductName (字符串)
QuantityPerUnit (字符串)
UnitPrice (双精度)
UnitsInStock (双精度)
UnitValue (Double)
– 基于UnitsInStock * UnitPrice
的计算字段
当您添加列时,默认数据类型是 string
。请选择列后转到属性窗口,将其从 String
更改为 Integer
或 Double
。
请参见 图 5。您的 DataTable
应与此相同。您也可以查看属性窗口来更改数据类型。
您听说过“强类型 DataSet”吗?如果没有,那么我们刚刚在这里创建了一个强类型 DataSet。请查阅在线帮助以了解更多关于强类型 DataSet 的信息。
步骤 4:将报表添加到项目
好的,到目前为止我们已经创建了项目;添加了 Report Viewer 和 DataSet
。现在,是时候处理这场演出的明星了!让我们创建那个漂亮的报表。
需要执行以下步骤来创建 Report (rptProductList.rdlc)
- 从“解决方案资源管理器”选择 添加 -> 新建项 -> 报表。将名称从 Report1.rdlc 更改为 rptProductList.rdlc,然后单击“添加”按钮完成操作。
通常,添加操作完成后,您的屏幕应与 图 6 类似。当报表添加到项目中时,它就可以使用 DataSet
进行设计了。
无论这是您的第一个报表,还是您像我一样是个报表爱好者;我们都必须处理报表编写最基本的构建块,即:页眉、正文和页脚。
通常,报表的设计会考虑特定的页面大小和布局。我们的报表是 Letter 尺寸和纵向布局。您可以通过右键单击打开的设计器表面的任意位置并选择“属性”来探索报表布局的各种属性。
在开始设计尝试之前,始终建议在纸上绘制报表的原型。如您在 图 1 中所见,我们在页眉部分有报表名称和报表日期。正文部分包含产品列表信息以及汇总总计;页脚包含页码。
让我们开始处理页面页眉
当新报表添加到项目时,默认情况下,您在报表设计器中看到的只有正文部分。右键单击报表设计器表面的正文以外的任何位置,然后选择 Page Header。这将在报表中添加页眉。请随意调整页眉和正文部分的高度。请参见 图 7,我已减小了正文的高度并增加了页眉的高度。
在报表设计器中,如果您查看工具箱,会看到各种可用于设计报表的控件。在本例中,我们将使用 TextBox
、Line
和 Table
控件。如果您需要有关所有可用控件的详细信息,我鼓励您查阅在线文档。
页眉部分
让我们开始设计页眉。我们将通过将两个 TextBox
控件拖放到页眉部分开始。Textbox
可以显示静态和动态数据。Line
控件用于将页眉与正文部分分隔开。
在报表设计器表面放置控件后,您可以通过更改关联属性来控制外观。我们将指定一个 TextBox
用于报表标题,另一个用于显示当前日期。您可以通过选择 TextBox
控件并直接在其中键入来输入静态文本。
请更改标题 TextBox
的以下属性
Value = “Product List”
Color = Purple (you like purple too for title right?)
请更改日期 TextBox
的以下属性
Value = ="Run Data: " & Today
Color = Purple (you like purple too for title right?)
请注意,日期 TextBox
的 Value
属性以“=
”符号开头。这不是简单的静态文本,而是表达式。此表达式是字符串“Run Date
”和 VB.NET 脚本 关键字 Today
(用于获取当前系统日期)的结果。
您可以为报表中的所有对象指定所需的名称;我选择保留大多数控件的默认名称,但为了演示目的,我为标题 TextBox
指定了“txtTitle
”。
请参考 图 8;您完成的页眉设计应大致相同。
正文部分
正文部分,也称为详细信息部分,是报表中最重要的部分。如您所见,当我们向项目中添加报表时;正文部分会自动为我们添加。我们只需开始在其上放置控件。
传统上,正文部分用于显示详细信息(在本例中是产品信息),通常是一行以上的信息。正文部分可以根据报表数据的增长而扩展。通常报表设计时会考虑输出一个物理页面(Letter/A4 等);在这种情况下,正文部分仍然可以用于显示信息。
在 Table
、Matrix
和 List
这三个正文部分最常用的控件中;我们将为我们的示例使用 Table
控件。这三者都可以重复信息;Matrix
更进一步,甚至可以生成透视输出。
让我们将 Table
控件拖放到报表设计器表面的正文部分。如果您注意到,此操作将生成一个具有三行三列的表。您可能还注意到中间列也已标记:Header
、Detail
和 Footer
。
现在,如果我告诉您 Table
控件只不过是一堆 TextBox
组合在一起!您不必感到惊讶。是的,Table
中的每一个单元格都像 TextBox
,这意味着您可以键入静态文本,也可以指定动态表达式。
在我们开始设计正文部分之前,让我们再添加两列(记住报表总共有五列)。添加列很容易;请执行以下操作以将新列添加到报表中
- 在正文部分选择 Table 控件
- 单击最右侧的列标题(我假设我们在右侧添加新列)
- 右键单击标题并选择 -> 在右侧插入列
请确保您的报表看起来像 图 9。您可以根据将要保存的数据长度自由调整列宽。
我敢肯定我们中的大多数人都使用过 Excel 或类似工具;可以将 Table
控件想象成迷你工作表。我们可以应用边框,更改单个单元格的字体等。所以,您所要做的就是构思所需的格式主题并开始应用。
从第一列到最后一列,请单击单个列标题单元格并键入以下文本
Header 1: “Product Name”
Header 2: “Packaging”
Header 3: “Unit Price”
Header 4: “Units in Stock”
Header 5: “Stock Value”
让我们继续为 Detail
部分做同样的事情,这里需要知道的是,我们必须键入的不是文本,而是来自 dsProduct.dtProductInfo
的列表达式。您可以键入表达式,也可以简单地将列从“数据源”工具栏(参见左侧的 图 7)拖放。
如果您选择键入,从第一列到最后一列,请单击单个列详细信息单元格并键入以下文本
Detail 1: “=Fields!ProductName.Value”
Detail 2: “=Fields!QuantityPerUnit.Value”
Detail 3: “=Fields!UnitsInStock.Value”
Detail 4: “=Fields!UnitPrice.Value”
Detail 5: “=Fields!UnitsInStock.Value * Fields!UnitPrice.Value”
请注意详细信息 5:这是通过将 Units in Stock 和 Unit Value 相乘计算出的结果。
提示:如果您将列拖放到 Table
控件的详细信息部分,它会尝试自动添加列标题,如果列标题为空。
最后,让我们在 Table
控件的页脚部分添加汇总总计。请确保在 Body
部分内的第 4 列和第 5 列的页脚单元格中键入以下文本
Cell 4: “Total Value:”
Cell 5: “=SUM(Fields!UnitsInStock.Value * Fields!UnitPrice.Value)”
请检查第 5 列中的表达式;我正在使用内置函数 SUM()
来计算报表中列出的所有产品的总库存价值。
页脚部分
在我们开始编写一些酷炫的 C# 代码使报表栩栩如生之前,让我们完成报表页脚部分。正如我们之前添加了报表页眉一样,我们必须右键单击打开的报表设计器表面并选择 Page Footer(参见 图 7)。
将 Line
和 TextBox
控件拖放到 Footer
部分。请在 TextBox
中键入以下表达式
Value: ="Page: " & Globals!PageNumber & "/" & Globals!TotalPages
正如您所见,我使用了 PageNumber
和 TotalPages
,它们都是报表引擎维护的全局变量。
提示:请确保您键入的所有表达式都以“=
”开头。
请确保您的报表看起来像 图 10。正如您所见,我已经引入了一些颜色和右对齐数字数据等。请随意尝试所有不同的格式选项,只需将 Table 控件视为具有列和行的迷你电子表格,您就知道可以对其进行所有格式设置了。
表达式生成器
表达式生成器是 Reporting Services 的一个非常强大的功能。正如您在 图 11 中所看到的,Stock Value 是借助 SUM 函数计算的。DataSet
中的所有字段都可以通过“Fields!
”关键字访问。
步骤 5:编写一些 C# 代码,让报表活起来
呼……希望你们还没有感到疲惫。坚持住;我们现在在最后一步了。就像我们等待了九个月,终于迎来了生命的奇迹。
从解决方案资源管理器中,选择 Form1
。右键单击窗体表面并选择“查看代码”。
using System.Data.SqlClient;
using Microsoft.Reporting.WinForms;
请确保 Form1_Load
事件具有以下代码
private void Form1_Load(object sender, EventArgs e)
{
//declare connection string
string cnString = @"(local); Initial Catalog=northwind;" +
"User Id=northwind;Password=northwind";
//use following if you use standard security
//string cnString = @"Data Source=(local);Initial
//Catalog=northwind; Integrated Security=SSPI";
//declare Connection, command and other related objects
SqlConnection conReport = new SqlConnection(cnString);
SqlCommand cmdReport = new SqlCommand();
SqlDataReader drReport;
DataSet dsReport = new dsProduct();
try
{
//open connection
conReport.Open();
//prepare connection object to get the data through reader and
populate into dataset
cmdReport.CommandType = CommandType.Text;
cmdReport.Connection = conReport;
cmdReport.CommandText = "Select TOP 5 * FROM
Products Order By ProductName";
//read data from command object
drReport = cmdReport.ExecuteReader();
//new cool thing with ADO.NET... load data directly from reader
to dataset
dsReport.Tables[0].Load(drReport);
//close reader and connection
drReport.Close();
conReport.Close();
//provide local report information to viewer
rpvAbraKaDabra.LocalReport.ReportEmbeddedResource =
"rsWin101.rptProductList.rdlc";
//prepare report data source
ReportDataSource rds = new ReportDataSource();
rds.Name = "dsProduct_dtProductList";
rds.Value = dsReport.Tables[0];
rpvAbraKaDabra.LocalReport.DataSources.Add(rds);
//load report viewer
rpvAbraKaDabra.RefreshReport();
}
catch (Exception ex)
{
//display generic error message back to user
MessageBox.Show(ex.Message);
}
finally
{
//check if connection is still open then attempt to close it
if (conReport.State == ConnectionState.Open)
{
conReport.Close();
}
}
}
您可能想知道为什么我在 select
查询中使用了“TOP 5
”;原因是我想限制输出,以便向您展示 图 1 中的汇总总计。
提示:ReportDataSource
对象的 Name 属性应始终为“DataSet_DataTable
”。
我可以使用 Access 而不是 SQL Server 2000 吗?
是的,您可以使用 Access 数据库。请确保在上述代码中应用以下更改,以便从 NorthWind Access Database 报告数据。
尽管 Northwind 数据库 随 Access 数据库安装一起提供;如果您没有它,可以在此处下载。
修改后的代码应如下所示
using System.Data.OleDb;
private void Form1_Load(object sender, EventArgs e)
{
//declare connection string
string cnString = @"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=c:\nwind.mdb;User Id=admin;Password=;";
//declare Connection, command and other related objects
OleDbConnection conReport = new OleDbConnection(cnString);
OleDbCommand cmdReport = new OleDbCommand();
OleDbDataReader drReport;
DataSet dsReport = new dsProduct();
try
{
//open connection
conReport.Open();
//prepare connection object to get the data through
reader and populate into dataset
cmdReport.CommandType = CommandType.Text;
cmdReport.Connection = conReport;
cmdReport.CommandText = "Select TOP 5 * FROM
Products Order By ProductName";
//read data from command object
drReport = cmdReport.ExecuteReader();
//new cool thing with ADO.NET... load data directly
from reader to dataset
dsReport.Tables[0].Load(drReport);
//close reader and connection
drReport.Close();
conReport.Close();
//provide local report information to viewer
rpvAbraKaDabra.LocalReport.ReportEmbeddedResource =
"rsWin101.rptProductList.rdlc";
//prepare report data source
ReportDataSource rds = new ReportDataSource();
rds.Name = "dsProduct_dtProductList";
rds.Value = dsReport.Tables[0];
rpvAbraKaDabra.LocalReport.DataSources.Add(rds);
//load report viewer
rpvAbraKaDabra.RefreshReport();
}
catch (Exception ex)
{
//display generic error message back to user
MessageBox.Show(ex.Message);
}
finally
{
//check if connection is still open then attempt to close it
if (conReport.State == ConnectionState.Open)
{
conReport.Close();
}
}
}
结论
虽然我已尽力使本文的语言尽可能简单;如果您需要任何进一步的澄清,请随时与我联系。我将自己视为一名初出茅庐的作者;我还有很多需要学习;正是像您这样的读者,一直帮助我改进我的写作。
我期待收到您对我的任何评论/建议。
感谢您的阅读;我真诚地希望本文能通过我的实践方法,帮助您更好地了解报表服务。
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。