[置顶]用DbProviderFactory 实现通用数据库操作的封装
今天写了一天的代码,关于报表的一些基本控件,回来不想再写了,直接COPY上,有详细的注释。主要内容有上一页,下一页,放大,缩小,导为EXCEL和PDF,打印。
这里再次感谢“蜡人张”,这里有很多功能是参考该大侠的博客后修改的,这里是他的文章链接:http://waxdoll.cnblogs.com/archive/2006/03/03/342435.html
代友:
namespace POSReport.ReportForm
{
public partial class FrmReportViewBase : Form
{
#region 构造函数
public FrmReportViewBase()
{
InitializeComponent();
//this.SetCtlDesc();
//LanguageChange.ChangeDisplay(this);
}
#endregion
#region 自定义报表按钮的操作
#region 返回报表第一页
/// <summary>
/// 回到报表的第一页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnFirst_Click(object sender, EventArgs e)
{
this.rptViewMain.CurrentPage = 1;
}
#endregion
#region 跳转到报表最后一页
/// <summary>
/// 跳转到报表最后一页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLast_Click(object sender, EventArgs e)
{
this.rptViewMain.CurrentPage = this.rptViewMain.LocalReport.GetTotalPages();
}
#endregion
#region 上一页
/// <summary>
/// 上一页
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPre_Click(object sender, EventArgs e)
{
if (this.rptViewMain.CurrentPage > 1)
{
this.rptViewMain.CurrentPage--;
}
}
#endregion
#region 下一页
private void btnNext_Click(object sender, EventArgs e)
{
if (this.rptViewMain.CurrentPage < this.rptViewMain.LocalReport.GetTotalPages())
{
this.rptViewMain.CurrentPage++;
}
}
#endregion
#region 放大
/// <summary>
///放大
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnEnlarge_Click(object sender, EventArgs e)
{
if (this.rptViewMain.ZoomPercent < maxPercent)
{
this.rptViewMain.ZoomPercent += addPercent;
}
}
#endregion
#region 缩小
/// <summary>
/// 缩小
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnReduce_Click(object sender, EventArgs e)
{
if (this.rptViewMain.ZoomPercent > minPercent)
{
this.rptViewMain.ZoomPercent -= addPercent;
}
}
#endregion
#region 关闭
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
#endregion
#region 导出为Excel或PDF
private void btnSaveDest_Click(object sender, EventArgs e)
{
try
{
Microsoft.Reporting.WinForms.Warning[] Warnings;
string[] strStreamIds;
string strMimeType;
string strEncoding;
string strFileNameExtension;
//文件形式
string fileType = "Excel";
string fileName = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.AddExtension = true;
sfd.Filter = "excel|*.xls|PDF|*.PDF";
if (sfd.ShowDialog() == DialogResult.OK)
{
if (string.IsNullOrEmpty(sfd.FileName))
{
return;
}
if (sfd.FilterIndex == 1)
{
fileType = "Excel";
}
else if (sfd.FilterIndex == 2)
{
fileType = "PDF";
}
else
{
fileType = "Excel";
}
fileName = sfd.FileName;
byte[] bytes = this.rptViewMain.LocalReport.Render(fileType, null, out strMimeType, out
strEncoding, out strFileNameExtension, out strStreamIds, out Warnings);
SaveFile(fileName, bytes);
}
}
catch (Exception ex)
{
msgError.GetExcepiton(ex, MODULENAME, Common.CommonBase.MsgError.Title.Hint);
}
}
#endregion
#region 打印
private void btnPrint_Click(object sender, EventArgs e)
{
try
{
Export(this.rptViewMain.LocalReport);
currentPageIndex = 0;
const string printerName = "Microsoft XPS Document Writer";
if (streams == null || streams.Count == 0)
return;
PrintDocument printDoc = new PrintDocument();
printDoc.PrinterSettings.PrinterName = printerName;
if (!printDoc.PrinterSettings.IsValid)
{
return;
}
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
printDoc.Print();
//消毁streams
if (streams != null)
{
Foreach(Stream stream in streams)
{
stream.Close();
}
}
}
catch (Exception ex)
{
msgError.GetExcepiton(ex, MODULENAME, MsgError.Title.Hint);
}
}
#endregion
#endregion
#region 报表事件
private void rptViewMain_Load(object sender, EventArgs e)
{
// 设置自定义报表按钮是否为可用
SetControlVisble();
//设置报表的缩放模式
this.rptViewMain.ZoomMode = ZoomMode.Percent;
}
#region 报表的显示页号改变事件
private void rptViewMain_PageNavigation(object sender, PageNavigationEventArgs e)
{
// 设置自定义报表按钮是否为可用
SetControlVisble();
}
#endregion
#region 报表显示的百分比改变事件
private void rptViewMain_ZoomChange(object sender, ZoomChangeEventArgs e)
{
if (this.rptViewMain.ZoomPercent >= maxPercent)
{
this.btnEnlarge.Enabled = false;
}
else
{
this.btnEnlarge.Enabled = true;
}
if (this.rptViewMain.ZoomPercent <= minPercent)
{
this.btnReduce.Enabled = false;
}
else
{
this.btnReduce.Enabled = true;
}
}
#endregion
#endregion
#region 方法
#region 设置自定义报表按钮是否为可用
/// <summary>
/// 设置自定义报表按钮是否为可用
/// </summary>.
private void SetControlVisble()
{
if (this.rptViewMain.LocalReport.GetTotalPages() <= 1)
{
this.btnFirst.Enabled = false;
this.btnLast.Enabled = false;
this.btnNext.Enabled = false;
this.btnPre.Enabled = false;
return;
}
if (this.rptViewMain.CurrentPage >= this.rptViewMain.LocalReport.GetTotalPages() - 1)
{
this.btnNext.Enabled = false;
this.btnLast.Enabled = false;
}
else
{
this.btnLast.Enabled = true;
this.btnNext.Enabled = true;
}
if (this.rptViewMain.CurrentPage <= 0)
{
this.btnFirst.Enabled = false;
this.btnPre.Enabled = false;
}
else
{
this.btnPre.Enabled = true;
this.btnFirst.Enabled = true;
}
}
#endregion
#region 保存
protected void SaveFile(string fileName, byte[] bytes)
{
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Length);
}
}
catch (IOException ex)
{
return;
}
}
#endregion
#region 创建文件流
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool
willSeek)
{
stream = new FileStream(name + "." + fileNameExtension, FileMode.Create);
streams.Add(stream);
name = string.Empty;
fileNameExtension = string.Empty;
return stream;
}
#endregion
#region 将本地报表导出为文件流
private void Export(LocalReport report)
{
string deviceInfo =
"<DeviceInfo>" +
" <OutputFormat>EMF</OutputFormat>" +
" <PageWidth>8.5in</PageWidth>" +
" <PageHeight>11in</PageHeight>" +
" <MarginTop>0.25in</MarginTop>" +
" <MarginLeft>0.25in</MarginLeft>" +
" <MarginRight>0.25in</MarginRight>" +
" <MarginBottom>0.25in</MarginBottom>" +
"</DeviceInfo>";
Warning[] warnings;
streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream, out warnings);
foreach (Stream stream in streams)
{
stream.Position = 0;
}
}
#endregion
#region Page打印方法
private void PrintPage(object sender, PrintPageEventArgs e)
{
Metafile pageImage = new Metafile(streams[currentPageIndex]);
e.Graphics.DrawImage(pageImage, e.PageBounds);
currentPageIndex++;
e.HasMorePages = (currentPageIndex < streams.Count);
}
#endregion
#endregion
#region 私有成员
//报表显示的百分比的增量因子
private const int addPercent = 25;
//报表显示的最大百分比
private const int maxPercent = 200;
private const int minPercent = 25;
private MsgError msgError = MsgError.getInstance();//实例化错误异常提示类
private const string MODULENAME = "FrmReportViewBase"; //模块名称
//当前页的索引
private int currentPageIndex;
//打印需要的文件流序列
private IList<Stream> streams;
#endregion
}
}
因为过春节的缘故,第四篇多等了一年。
上一篇写到表参数设置和建立子表,分组,本篇将学习使用钻取报表。其实只要会使用参数和建立子报表,钻取报表是很简单的事情了。
首先简单地说一下这里的钻取报表的目的:当点击相应的学生后面的查看按钮后,能跳转到另一张报表,显示该学生所在班级的所有学生信息。
一 在文件夹Report中添加报表文件StudentDetail.rdlc,拖放一张表。报表-选择数据源为RptDataSet_Student,报表-建立参数ClassID,选中表,右键-属性-筛选器,设置(=Fields!ClassID.Value)=(=Fields!Sname.Value),确定。
二 在rptStudent.rdlc的表的最右侧插入列,列标题为"查看所在班级",详细内容为"查看",如下图1.1
public FrmRptMain()
{
InitializeComponent();
}
private ClassDataSource dataSource = new ClassDataSource();
private StudentDataSource studentDataSource = new StudentDataSource();
private void FrmRptMain_Load(object sender, EventArgs e)
{
this.rptViewMain.LocalReport.SubreportProcessing +=
new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
this.rptViewMain.Drillthrough += new DrillthroughEventHandler(LocalReport_DrillthroughEventHandler);
this.rptViewMain.LocalReport.ReportEmbeddedResource = "ReportingAPP.Report.rptClass.rdlc";
this.rptViewMain.LocalReport.DataSources.Add(
new ReportDataSource("RptDataSet_Class", dataSource.GetClassData())
);
this.rptViewMain.RefreshReport();
}
private void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
e.DataSources.Add(
new ReportDataSource("RptDataSet_Student", studentDataSource.GetStudentData())
);
}
private void LocalReport_DrillthroughEventHandler(object sender, DrillthroughEventArgs e)
{
LocalReport rpt = e.Report as LocalReport;
if (rpt != null)
{
rpt.DataSources.Add(new ReportDataSource("RptDataSet_Student", studentDataSource.GetStudentData()));
}
}
五 调试运行
主界面如下:
昨天写了用C#做最基本的报表,并手动配置数据源,这篇主要讲报表参数设置和建立子表,分组。
一 建立报表参数
打开昨天的项目,在Report文件夹中新建报表rptStudent .rdlc,拖入一张表,点击报表-报表参数--参数--添加,参数名称Cno,确定
二 为子报表添加数据集
依然是报表--数据源--把RptDataSet_Student添加到报表,确定.
三 设计子报表
添加分组,如图1
前面讲了用报表向导创建报表项目,今天用C#代码做与前面相同功能的基本报表。
一 创建三个文件夹 DataSet(存放数据集),DataSource(提供数据源),Report(存放报表文件)
二 创建数据集.
在DataSet中新建数据集RptDataSet.xsd,VS--视图--服务器资源管理器--选择前面创建的两张表--拖入数据集中(删除ClassTableAdapter和StudentTableAdapter,因为不删除的话系统会调用里面的FILL方法自动填充数据集。)注,这里的数据集不是必须从服务器资源管理器拖,也可以直接写,这里是为了方便数据的绑定,下面会讲到。
三在DataSet中新建ClassDataSource.cs,代码如下
public class ClassDataSource
{
public DataTable GetClassData()
{
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ReportingAPP.Properties.Settings.RptDBConnectionString"].ToString());//注意添加ConfigurationManager的引用和自己配置连接字符串
StringBuilder cmdText = new StringBuilder();
cmdText.Append("SELECT [SystemKey],[Cno] ,[CName] FROM [RptDB].[dbo].[Class]");
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(cmdText.ToString(), conn);
dt.Load(cmd.ExecuteReader());
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
return dt;
}
}
四 在Report文件夹中添加报表文件rptClass.rdlc
在报表里面拖一张表,选择VS上文工具栏的数据源(选中表后才能看得见报表工具,和报表有关的资源大多放在这里,比如参数,数据源,图片),选择RptDataSet_Class添加到报表.
然后用如下的设计
最近公司要用到Report报表,为什么要用Report报表,原因很简单,客户要求用免费的。因此专门去学习了一下,看了园子里面很多大牛的随笔,谢园子里面无私的朋友,把自己的学习经验和大家分享,这里特别感谢“蜡人张”。
好吧,就这么开始,我是第一次写随笔,因为还到明年才毕业,经验比较少,觉压力很大,写随笔也主要是学习而已,比较适合像我这样按步部就班的初学者。
下面用的方法是用报表向导直接在界面上操作的。
一 建数据库(比较懒的朋友可以直接复制下面的SQL)
CREATE DATABASE RptDB
GO
USE RptDB
GO
CREATE TABLE Class
(
SystemKey UNIQUEIDENTIFIER DEFAULT(NEWID()) PRIMARY KEY ,
Cno NVARCHAR(20) NOT NULL,
CName NVARCHAR(50) NOT NULL
)
GO
CREATE TABLE Student
(
SystemKey UNIQUEIDENTIFIER DEFAULT(NEWID()) PRIMARY KEY ,
Sno NVARCHAR(20) NOT NULL,
Sname NVARCHAR(50) NOT NULL,
ClassID UNIQUEIDENTIFIER
)
ALTER TABLE Student ADD CONSTRAINT FK_Stu_Class FOREIGN KEY(ClassID)
REFERENCES Class(SystemKey)
插入数据若干。
二 新建报表就用程序 ReportingAPP
窗体上拖放MicrosoftReportView,点击其右上角的三角符号,选择设计新报表。
依次选择数据库--新建连接--连接到刚才新建的数据库RptDB。
数据源选择表Class--下一步--表格式--详细信息选择CName--完成--重命名报表--完成
再选择窗体上的reportView控件,点右上角的三角符号,选择刚刚设计好的报表,F5,就可以看到下面的结果了。
到此,一个最基本的报表就可以呈现出来了。
现在观察主窗体,发现上面多了三个控件,
Load事件里面也多了以下语句
1) this.ClassTableAdapter.Fill(this.RptDBDataSet.Class);//填充数据集
2) this.rptViewMain.RefreshReport(); //这个是报表最见的方法之一,其作用是呈现当前报表内容
先到这里,下一篇介绍一用C#语句而非向导形式做最基本的报表。