最近一个项目中,涉及到很多报表的开发,以前对水晶报表搞得比较熟,但它的庞大与其它诸多功能限制,特别是发布与部置时面临的困难,实在让人无法忍受(应该很少有人买得起正版吧),决定试一下MS Reporting Services,当然在这里只使用Local Report,对微软的Reporting Services进行了一番了解,蜡人张(http://www.cnblogs.com/waxdoll/category/49402.html)老兄这里有很多很好的学习材料,我也是从蜡人兄那里对自定义报表的研究得出以下部分修正的Demo.
对于微软提供的ReportView控件,大家都知道,不能在其中对报表的边距等进行自定义,其工具条也是根本上无法自定义控制的,每次用户打印时,都必须对页面,边距等进行调整,对用户来说,这可能是一件无法忍受的事情,这或者是为满足Reporting Services报表而这样设计的.
对蜡人张老兄提供的例子做了一些改进,主要修正以下几点:
1,修正无法使用网络打印机进行打印的问题;
2,报表使用钳入式资源加载方式;
3,提取成一用户控件ReportView,更方便于开发人员将其整合到自己的项目中
Demo文件结构:
自定义报表控件ReportView源码分如下:
1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Drawing;
5
using System.Data;
6
using System.Text;
7
using System.Windows.Forms;
8
using System.IO;
9
using Microsoft.Reporting.WinForms;
10
namespace LocalReportDemo.Report
11
{
12
public partial class ReportViewer : UserControl
13
{
14
15
private EMFStreamPrintDocument printDoc;
16
17
/// <summary>
18
/// 主报表名称
19
/// </summary>
20
private string m_ReportName = string.Empty;
21
public string ReportName
22
{
23
get
24
{
25
return this.m_ReportName;
26
}
27
set
28
{
29
this.m_ReportName = value;
30
}
31
}
32
33
/// <summary>
34
/// 主报表数据源
35
/// </summary>
36
private object m_MainDataSet = null;
37
public object MainDataSet
38
{
39
get
40
{
41
return this.m_MainDataSet;
42
}
43
set
44
{
45
this.m_MainDataSet = value;
46
47
}
48
}
49
50
/// <summary>
51
/// 主报表数据源名称
52
/// </summary>
53
private string m_MainDataSourceName = string.Empty;
54
public string MainDataSourceName
55
{
56
get
57
{
58
return this.m_MainDataSourceName;
59
}
60
set
61
{
62
this.m_MainDataSourceName = value;
63
}
64
}
65
66
/// <summary>
67
/// 钳入资源中的报表路径
68
/// </summary>
69
private string m_ReportEmbeddedResource;
70
public string MReportEmbeddedResource
71
{
72
get
73
{
74
return m_ReportEmbeddedResource;
75
}
76
set
77
{
78
m_ReportEmbeddedResource = value;
79
}
80
}
81
82
/// <summary>
83
/// 钻取报表数据源
84
/// </summary>
85
private object m_DrillDataSet = null;
86
public object DrillDataSet
87
{
88
get
89
{
90
return this.m_DrillDataSet;
91
}
92
set
93
{
94
this.m_DrillDataSet = value;
95
}
96
}
97
/// <summary>
98
/// 钻取报表数据源名称
99
/// </summary>
100
private string m_DrillDataSourceName = string.Empty;
101
public string DrillDataSourceName
102
{
103
get
104
{
105
return this.m_DrillDataSourceName;
106
}
107
set
108
{
109
this.m_DrillDataSourceName = value;
110
}
111
}
112
113
/// <summary>
114
/// 构造器
115
/// </summary>
116
public ReportViewer()
117
{
118
InitializeComponent();
119
this.Dock = System.Windows.Forms.DockStyle.Fill;
120
}
121
122
/// <summary>
123
/// 加载窗体事件
124
/// </summary>
125
/// <param name="sender"></param>
126
/// <param name="e"></param>
127
private void rpv1_Load(object sender, EventArgs e)
128
{
129
}
130
/// <summary>
131
/// 加载报表
132
/// </summary>
133
public void BindReport()
134
{
135
136
System.Type type = this.MainDataSet.GetType();
137
if (this.m_MainDataSet == null)
138
{
139
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
140
return;
141
}
142
if (type == null)
143
{
144
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
145
return;
146
}
147
else
148
{
149
System.Reflection.PropertyInfo[] picData = type.GetProperties();
150
bool bolExist = false;
151
foreach (System.Reflection.PropertyInfo piData in picData)
152
{
153
if (piData.Name == "Tables")
154
{
155
bolExist = true;
156
157
if (MainDataSourceName == string.Empty)
158
{
159
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
160
return;
161
}
162
163
this.rpv1.LocalReport.DataSources.Add(
164
new Microsoft.Reporting.WinForms.ReportDataSource(this.MainDataSourceName,
165
(piData.GetValue(this.MainDataSet, null) as System.Data.DataTableCollection)[0])
166
);
167
this.rpv1.LocalReport.ReportEmbeddedResource = this.MReportEmbeddedResource;
168
this.rpv1.RefreshReport();
169
this.lblTotal.Text = @"/" + (this.rpv1.LocalReport.GetTotalPages()+1).ToString();
170
this.tbxPage.Text = "1";
171
break;
172
}
173
}
174
175
if (!bolExist)
176
{
177
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
178
return;
179
}
180
}
181
}
182
183
工具条事件
368
369
/// <summary>
370
/// 钻取报表事件
371
/// </summary>
372
/// <param name="sender"></param>
373
/// <param name="e"></param>
374
private void rpv1_Drillthrough(object sender, DrillthroughEventArgs e)
375
{
376
if (DrillDataSet == null)
377
{
378
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
379
return;
380
}
381
else
382
{
383
if (this.DrillDataSourceName == string.Empty)
384
{
385
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
386
return;
387
}
388
else
389
{
390
Microsoft.Reporting.WinForms.LocalReport report = e.Report as Microsoft.Reporting.WinForms.LocalReport;
391
report.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(this.DrillDataSourceName, this.GetTableCollection(this.DrillDataSet)[0]));
392
}
393
}
394
}
395
private System.Data.DataTableCollection GetTableCollection(object ReportSource)
396
{
397
System.Type type = ReportSource.GetType();
398
399
if (type == null)
400
{
401
MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
402
return null;
403
}
404
else
405
{
406
System.Reflection.PropertyInfo[] picData = type.GetProperties();
407
bool bolExist = false;
408
foreach (System.Reflection.PropertyInfo piData in picData)
409
{
410
if (piData.Name == "Tables")
411
{
412
bolExist = true;
413
return piData.GetValue(ReportSource, null) as System.Data.DataTableCollection;
414
}
415
}
416
417
if (!bolExist)
418
{
419
MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);
420
return null;
421
}
422
}
423
return null;
424
}
425
}
426
}
427
using System;2
using System.Collections.Generic;3
using System.ComponentModel;4
using System.Drawing;5
using System.Data;6
using System.Text;7
using System.Windows.Forms;8
using System.IO;9
using Microsoft.Reporting.WinForms;10
namespace LocalReportDemo.Report11
{12
public partial class ReportViewer : UserControl13
{14

15
private EMFStreamPrintDocument printDoc;16

17
/// <summary>18
/// 主报表名称19
/// </summary>20
private string m_ReportName = string.Empty;21
public string ReportName22
{23
get24
{25
return this.m_ReportName;26
}27
set28
{29
this.m_ReportName = value;30
}31
}32

33
/// <summary>34
/// 主报表数据源35
/// </summary>36
private object m_MainDataSet = null;37
public object MainDataSet38
{39
get40
{41
return this.m_MainDataSet;42
}43
set44
{45
this.m_MainDataSet = value;46

47
}48
}49

50
/// <summary>51
/// 主报表数据源名称52
/// </summary>53
private string m_MainDataSourceName = string.Empty;54
public string MainDataSourceName55
{56
get57
{58
return this.m_MainDataSourceName;59
}60
set61
{62
this.m_MainDataSourceName = value;63
}64
} 65

66
/// <summary>67
/// 钳入资源中的报表路径68
/// </summary>69
private string m_ReportEmbeddedResource;70
public string MReportEmbeddedResource71
{72
get73
{74
return m_ReportEmbeddedResource;75
}76
set77
{78
m_ReportEmbeddedResource = value;79
}80
}81

82
/// <summary>83
/// 钻取报表数据源84
/// </summary>85
private object m_DrillDataSet = null;86
public object DrillDataSet87
{88
get89
{90
return this.m_DrillDataSet;91
}92
set93
{94
this.m_DrillDataSet = value;95
}96
}97
/// <summary>98
/// 钻取报表数据源名称99
/// </summary>100
private string m_DrillDataSourceName = string.Empty;101
public string DrillDataSourceName102
{103
get104
{105
return this.m_DrillDataSourceName;106
}107
set108
{109
this.m_DrillDataSourceName = value;110
}111
}112

113
/// <summary>114
/// 构造器115
/// </summary>116
public ReportViewer()117
{118
InitializeComponent();119
this.Dock = System.Windows.Forms.DockStyle.Fill;120
}121

122
/// <summary>123
/// 加载窗体事件124
/// </summary>125
/// <param name="sender"></param>126
/// <param name="e"></param>127
private void rpv1_Load(object sender, EventArgs e)128
{129
}130
/// <summary>131
/// 加载报表132
/// </summary>133
public void BindReport()134
{135

136
System.Type type = this.MainDataSet.GetType();137
if (this.m_MainDataSet == null)138
{139
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);140
return;141
}142
if (type == null)143
{144
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);145
return;146
}147
else148
{149
System.Reflection.PropertyInfo[] picData = type.GetProperties();150
bool bolExist = false;151
foreach (System.Reflection.PropertyInfo piData in picData)152
{153
if (piData.Name == "Tables")154
{155
bolExist = true;156

157
if (MainDataSourceName == string.Empty)158
{159
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);160
return;161
}162

163
this.rpv1.LocalReport.DataSources.Add(164
new Microsoft.Reporting.WinForms.ReportDataSource(this.MainDataSourceName,165
(piData.GetValue(this.MainDataSet, null) as System.Data.DataTableCollection)[0])166
);167
this.rpv1.LocalReport.ReportEmbeddedResource = this.MReportEmbeddedResource;168
this.rpv1.RefreshReport();169
this.lblTotal.Text = @"/" + (this.rpv1.LocalReport.GetTotalPages()+1).ToString();170
this.tbxPage.Text = "1";171
break;172
}173
}174

175
if (!bolExist)176
{177
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);178
return;179
}180
}181
}182

183
工具条事件 368

369
/// <summary>370
/// 钻取报表事件371
/// </summary>372
/// <param name="sender"></param>373
/// <param name="e"></param>374
private void rpv1_Drillthrough(object sender, DrillthroughEventArgs e)375
{376
if (DrillDataSet == null)377
{378
MessageBox.Show("报表数据源不存在或为空!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);379
return;380
}381
else382
{383
if (this.DrillDataSourceName == string.Empty)384
{385
System.Windows.Forms.MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);386
return;387
}388
else389
{390
Microsoft.Reporting.WinForms.LocalReport report = e.Report as Microsoft.Reporting.WinForms.LocalReport;391
report.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(this.DrillDataSourceName, this.GetTableCollection(this.DrillDataSet)[0]));392
}393
}394
}395
private System.Data.DataTableCollection GetTableCollection(object ReportSource)396
{397
System.Type type = ReportSource.GetType();398

399
if (type == null)400
{401
MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);402
return null;403
}404
else405
{406
System.Reflection.PropertyInfo[] picData = type.GetProperties();407
bool bolExist = false;408
foreach (System.Reflection.PropertyInfo piData in picData)409
{410
if (piData.Name == "Tables")411
{412
bolExist = true;413
return piData.GetValue(ReportSource, null) as System.Data.DataTableCollection;414
}415
}416

417
if (!bolExist)418
{419
MessageBox.Show("报表数据源格式不正确!", "提醒对话框", MessageBoxButtons.OK, MessageBoxIcon.Information);420
return null;421
}422
}423
return null;424
}425
}426
}427

报表宿主窗口加载报表与数据源的方法:
1
DataSet ds = new DataSet();
2
ds.ReadXml(Application.StartupPath + @"\DataSource.xml");
3
this.reportViewer1.MainDataSet = ds;//报表主数据源
4
this.reportViewer1.MainDataSourceName = "ReportDemoDataSet_ReportDemoTable";//数据源名称
5
this.reportViewer1.MReportEmbeddedResource = "LocalReportDemo.ReportDemo.rdlc";//钳入资源的报表路径
6
this.reportViewer1.ReportName = "ReportDemo";//报表名称
7
this.reportViewer1.BindReport();//加载挷定报表
DataSet ds = new DataSet();2
ds.ReadXml(Application.StartupPath + @"\DataSource.xml");3
this.reportViewer1.MainDataSet = ds;//报表主数据源4
this.reportViewer1.MainDataSourceName = "ReportDemoDataSet_ReportDemoTable";//数据源名称5
this.reportViewer1.MReportEmbeddedResource = "LocalReportDemo.ReportDemo.rdlc";//钳入资源的报表路径6
this.reportViewer1.ReportName = "ReportDemo";//报表名称7
this.reportViewer1.BindReport();//加载挷定报表数据源名称如下图所示:

其中钳入资源的报表路径中,LocalReportDemo为报表钳入的程序集名称
报表格式配置文件ReportSettings.xml:
1
<?xml version="1.0" standalone="yes"?>
2
<ReportSettings>
3
<ReportDemo>
4
<ReportName>ReportDemo</ReportName>
5
<PrinterName>Microsoft Office Document Image Writer</PrinterName>
6
<PaperName>A4</PaperName>
7
<PageWidth>21.01</PageWidth>
8
<PageHeight>29.69</PageHeight>
9
<MarginTop>0.5</MarginTop>
10
<MarginBottom>0.2</MarginBottom>
11
<MarginLeft>2.3</MarginLeft>
12
<MarginRight>0.2</MarginRight>
13
<Orientation>Z</Orientation>
14
</ReportDemo>
15
</ReportSettings>
<?xml version="1.0" standalone="yes"?>2
<ReportSettings>3
<ReportDemo>4
<ReportName>ReportDemo</ReportName>5
<PrinterName>Microsoft Office Document Image Writer</PrinterName>6
<PaperName>A4</PaperName>7
<PageWidth>21.01</PageWidth>8
<PageHeight>29.69</PageHeight>9
<MarginTop>0.5</MarginTop>10
<MarginBottom>0.2</MarginBottom>11
<MarginLeft>2.3</MarginLeft>12
<MarginRight>0.2</MarginRight>13
<Orientation>Z</Orientation>14
</ReportDemo>15
</ReportSettings>此Demo运行效果:

打印预览:

有任何问题欢迎在此提出,大家共同讨论解决
此Demo完整代码下载:
/Files/mshwu/LocalReportDemo.rar

