水晶报表调用总结

前不久接到北方区一个个案, 比较有个性, 总结了一下大家以后碰到就有参考拉
 一.开发需求
规格如下:
程序编号 : CrystalReport.exe
程序名称 : 义利报表
          
画面:(备注:应用单选按钮)
 
页签一:销售报表 | 页签二:财务报表 | 页签三 ……(根据目录动态取)
 
    销售额对比表      服务中心销售对比表      表3    ...... (根据报表文件动态取)
    …….              …….                   …….
    …….              …….                   …….
    …….              …….                   …….
 
                查询                 取消
 
信息处理:
1.通过CrystalReport.ini文件去配置报表路径、服务器IP、数据库用户名、密码和数据库名称。
2.查询:根据单选按钮选择的报表,调用对应的水晶报表查询。
3.取消:退出查询界面。
 
二.解决过程
    1.沟通:
看了之后我比较茫然, 只知道要实现exe调用水晶报表, 说实话当时连水晶报表是啥东东长什么模样都不知道.后来与北方区规格撰写人曹铃铃多次电话沟通后才得知, 客户的需求是:
    该客户(义利)一直在使用水晶报表制作相关报表, 也许是水晶报表价格忒贵, 所以只有一个用户, 这样的话看报表的所有人都权限一样(最大权限), 都可以随意编辑保存报表, 并且要看一张报表还要打开水晶报表主程序, 比较麻烦. 因此产生一个问题, 报表文件的管理难, 做这个个案就是给浏览报表的人一个报表入口, 可以浏览报表而不能编辑.
    知道这个需求后, 我联系了易拓负责水晶报表的朋友, 了解到水晶报表其实是一个个的RPT后缀的报表文件, 报表逻辑全封装在里面, 要想脱离水晶报表环境预览它并不容易, 并且什么它的数据源连接方式也还不知道, 采用”推”模式还是”拉”模式也不知道, 具体相关的水晶报表元件是什么他也不了解……不过他告诉我用.NET开发兼容性会更好, 听的我一头雾水啊……
 
    2.知识储备:
    既然如此, 还是让曹铃铃把水晶报表安装程序发过来安装玩玩吧, 于是花了几个小时的时间从北方区传来安装程序并安装了, 并让她帮忙做一个简单的明细表给我, 方便我这边了解及测试. 看到水晶报表的真面目后发现它其实和SQL 2005的ReportService里的报表非常类似: 建一个报表前要建一个数据库连接并创建数据源并绑定当前报表, 然后往报表界面添加相关对象, 保存后是一个*.rpt文件. 哦, 于是上网搜索了一番总算有所得, delphi也是可以调用水晶报表的, 可惜找了很长时间都没有找到delphi5调用Crystal Repots XI的, 可能是delphi版本太老了, Crystal Repots XI又太新了, 郁闷拉. 不过一想, 这个个案和易飞没有关系, 和delphi也谈不上关系啊, 只要能调起Crystal Repots XI无论咋实现都行啊, 搜啊搜, 找到好多vs2005的调用方法, 不过太乱了, 毕竟Crystal Repots的版本太多了, 不同的版本调用方法也不尽一样, 下面整理了vs2005调用Crystal Repots XI的 J
 
水晶报表在应用时分两种方法,分别是拉模式(PULL)、推模式(PUSH)。拉模式:在水晶报表生成时的数据源是从水晶报表文件中的SQL语句从数据库中提取的,在编程时不用重写SQL语句,但要加上登录信息(具体方法,后面介绍)。推模式:在水晶报表生成时的数据源,是用编程时重写水晶报表中SQL语句而生成的dataset对像。也就是说,推模式是用dataset组装水晶报表。水晶报表组件介绍。水晶报表在VS2005中有两种组件,在WEB项目是分别是CrystalReportSource,CrystalReportViewer。在FORM项目里是分别是crystalReport,CrystalReportViewer。CrystalReportSource,crystalReport是水晶报表的数据提供者;CrystalReportViewer是水晶报表的浏览器。
不论是推模式还是拉模式都应使用ReportDocument和ReportViewer两个控件完成报表的数据加载和数据展现。
这样可以将表示层和基础业务逻辑分开, ReportDocument解决报表的封装,CrystalReportView解决报表的展现。
还有一种做法是使用CrystalReportViewer和CrystalReportSource控件, CrystalReportViewer的ReportSourceID属性指定数据的来源, CrystalReportSource控件指定rpt模板,如果模板中设置了过滤参数,可以用ControlID和界面中的数据选择控件关联,这种方法和sqldatasource很相似,但是这种使用方式会丧失灵活性,逻辑层次也不明确,和对sqldatasource一样,不推荐使用。
 
方式一:拉
在拉模式中如要在水晶报表中的SQL语句加上条件参数时要用{?参数名}方式给出。例:“SELECT T1, T2, T3 FROM T Where T1='{?parm}'” parm就是参数名, 以下例子中所用到的水晶报表文件中使用的SQL语句是“SELECT T1, T2, T3 FROM T Where T1='{?parm}'” parm就是参数名。
【1.WEB方式下】

using CrystalDecisions.Shared;
using CrystalDecisions.CrystalReports.Engine;
protected void Button_pull_Click(object sender, EventArgs e)
{
//连接报表文件
CrystalReportSource1.ReportDocument.Load(Server.MapPath("CrystalReport.rpt"));

//设置登陆信息
CrystalReportSource1.ReportDocument.SetDatabaseLogon("name""password", “ServerName”, “DbName”);

//传递参数
CrystalReportSource1.ReportDocument.SetParameterValue("Title""测试报表");
CrystalReportSource1.ReportDocument.SetParameterValue(
"Parm""1");
//绑定水晶报表数据源。
CrystalReportSource1.DataBind();

//给该浏览器赋上对像
CrystalReportViewer1.ReportSource = CrystalReportSource1;
CrystalReportViewer1.DataBind();
}
 
 
2.FORM方式下】
 注意:FORM方式下代码同WEB方式,但用crystalReport控件换掉了CrystalReportSource;用crystalReportViewer换掉了CrystalReportViewer;这两个控件都可以在工具箱里找到。同时在编程时去掉DataBind()方法。
private void Form1_Load(object sender, EventArgs e)
{
//连接报表文件
ReportDocument.Load(Application.StartupPath + "CrystalReport.rpt");

//设置登陆信息
ReportDocument.SetDatabaseLogon("name""password", “ServerName”, “DbName”)

//传递参数
ReportDocument.SetParameterValue("Title""测试报表");
ReportDocument.SetParameterValue(
"Parm""1");

//下面是给该水晶报表浏览器赋上对像
CrystalReportViewer.ReportSource =ReportDocument;
}

 方式二:推
在推模式中编程组装的Dataset里的SQL语句中的字段要与水晶报表里的SQL语句字段一致。简单的说,推模式中的水晶报表是个模板,把在设计器里报表的格式设好后,再组装DataSet就可以生成报表了。方法二里报表的数据来源已经不是报表模板使用拉模式获得了,是推模式的一种实现这样实现的好处是这里的sql是由页面输入条件动态构造出的, 可以实现按条件的动态查询。不使用强类型dataset,用datatable非常方便。容易实现的优点从安全性上看就变成了缺点,页面输入拼装sql,安全性不高啊。如果涉及多个表,sql很长构造麻烦的情况,使用第二种可能不是很方便。
 
1.WEB方式下】
using CrystalDecisions.Shared;
using CrystalDecisions.CrystalReports.Engine;
using System.Data.SqlClient;
protected void Button_push_Click(object sender, EventArgs e)
{
string sql = "SELECT T1, T2, T3 FROM T where T1='a'";
string DBConfig_sql =@"Data Source=chengjiea;Initial Catalog= ERP;UserID=sa;Password=518518";
DataSet ds 
= new DataSet();
SqlConnection sqlCon 
= new SqlConnection(DBConfig_sql);
SqlCommand sqlCmd 
= new SqlCommand(sql, sqlCon);
SqlDataAdapter sqlAd 
= new SqlDataAdapter();
sqlAd.SelectCommand 
= sqlCmd;
sqlAd.Fill(ds, 
"sql");
CrystalReportSource1.ReportDocument.Load(Server.MapPath(
"CrystalReport.rpt")); 
//注意此处必需指明Dataset中的表的名称,否则会提示“您请求的报表需要更多信息.”
CrystalReportSource1.ReportDocument.SetDataSource(ds.Tables["sql"]);
//{?}中的参数可以不用赋值,即使赋了值也不起作用。
//CrystalReportSource1.ReportDocument.ParameterFields["Parm"].CurrentValues.AddValue("123");
CrystalReportSource1.ReportDocument.ParameterFields["Title"].CurrentValues.AddValue("报表样例!");
CrystalReportSource1.DataBind();
CrystalReportViewer1.ReportSource 
= CrystalReportSource1;
CrystalReportViewer1.DataBind();
}

 
2.FORM方式下】
  
private void Form1_Load(object sender, EventArgs e)
{
string sql = "SELECT T1, T2, T3 FROM T where T1='a'";
string DBConfig_sql = @"Data Source=chengjiea;Initial Catalog= ERP;UserID=sa;Password=518518";
DataSet ds 
= new DataSet();
SqlConnection sqlCon 
= new SqlConnection(DBConfig_sql);
SqlCommand sqlCmd 
= new SqlCommand(sql, sqlCon);
SqlDataAdapter sqlAd 
= new SqlDataAdapter();
sqlAd.SelectCommand 
= sqlCmd;
sqlAd.Fill(ds, 
"sql");
crystalReport1.Load(Application.StartupPath 
+ "CrystalReport.rpt");
crystalReport1.SetDataSource(ds.Tables[
"sql"]);
//{?}中的参数可以不用赋值,即使赋了值也不起作用。
//CrystalReportSource1.ReportDocument.ParameterFields["Parm"].CurrentValues.AddValue("123");
crystalReport1.ParameterFields["Title"].CurrentValues.AddValue("这时推模式的报表样例!");
crystalReportViewer1.ReportSource 
= crystalReport1;
}

 3.开发
有了这些储备, 相信问题很快久能解决了吧. 于是针对个案需求选用vs2005C#来做模式的”Form”下实现, 至于C#基本语法以及C#下怎样遍历目录, 遍历文件, 开启新窗体, 等等等 都可以在MSDN上查到相关资料来参考, 下面一同附上代码, 万一大家以后遇到可以参考: J
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Runtime.InteropServices;

namespace CrystalReport
{
    
public partial class Main : Form
    
{
        
public Main()
        
{
            InitializeComponent();
        }


        
//当前选中的报表路径
        public string Path_Str = "";

        
//TabPage序列
        public int FI = 0;

        
//RadioButton序列
        public int FISUB = 0;

        
//RadioButton宽度
        public int FWidth = 0;

        
//RadioButton高度
        public int FHeight = 0;

        
//RadioButton行数
        public int FLines = 0;

        
//当前应用程序执行路径
        public string FExePath = "";

        
//创建TabPage 
        public TabPage[] mTabPage = new TabPage[500];

        
//创建GroupBox  
        public GroupBox[] mGroupBox = new GroupBox[500];

        
//创建RadioButton
        public RadioButton[] mRadioButton = new RadioButton[500];

        
public void CreateAll(DirectoryInfo source)
        
{
            
//判断rpt路径是否存在
            if (Directory.Exists(source.FullName) == false)
            
{
                MessageBox.Show(source.FullName 
+ " 不存在""提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }


            
//遍历目录
            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            
{
                
//MessageBox.Show(diSourceSubDir.Name, "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                
//实例TabPage                
                mTabPage[FI] = new TabPage();
                mTabPage[FI].Parent 
= tabControl1;
                mTabPage[FI].Text 
= diSourceSubDir.Name;
                mTabPage[FI].TabIndex 
= FI;

                
//实例GroupBox      
                mGroupBox[FI] = new GroupBox();
                mGroupBox[FI].Parent 
= mTabPage[FI];
                mGroupBox[FI].Dock 
= DockStyle.Fill;
                mGroupBox[FI].Text 
= "选择报表";

                
//行次
                int MI = 0;

                
int x = mGroupBox[FI].Location.X;
                
int y = mGroupBox[FI].Location.Y;

                
//遍历文件
                foreach (FileInfo fi in diSourceSubDir.GetFiles())
                
{
                    
//换列显示
                    if (MI % FLines == 0)
                    
{
                        
if (MI != 0)
                        
{
                            x 
= x + FWidth;
                            y 
= 0;
                            MI 
= 0;
                        }

                    }


                    
//实例RadioButton                 
                    mRadioButton[FISUB] = new RadioButton();
                    mRadioButton[FISUB].Parent 
= mGroupBox[FI];
                    mRadioButton[FISUB].Location 
= new System.Drawing.Point(x + 25, y + 15 + MI * FHeight);
                    mRadioButton[FISUB].Text 
= fi.Name;
                    mRadioButton[FISUB].AutoSize 
= true;

                    
//默认选择第一个
                    if (mRadioButton[FISUB].Location.Y == 15)
                        
if (mRadioButton[FISUB].Location.X == 25)
                            mRadioButton[FISUB].Checked 
= true;

                    
//行次累加
                    MI = MI + 1;

                    
//当前的RadioButton总Id累加
                    FISUB = FISUB + 1;
                }


                
//当前的TabPage总Id累加
                FI = FI + 1;

                
//递归
                CreateAll(diSourceSubDir);
            }

        }


        
//调用kernel32.dll读写ini文件信息
        [DllImport("kernel32")]
        
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
        [DllImport(
"kernel32")]
        
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        
//写ini
        public void IniWriteValue(string Section, string Key, string Value, string filepath)//对ini文件进行写操作的函数
        {
            WritePrivateProfileString(Section, Key, Value, filepath);
        }


        
//读ini
        public string IniReadValue(string Section, string Key, string filepath)             //对ini文件进行读操作的函数
        {
            StringBuilder temp 
= new StringBuilder(255);
            
int i = GetPrivateProfileString(Section, Key, "无法读取对应的值", temp, 255, filepath);
            
return temp.ToString();

        }


        
//字符串转换为整型
        public static int IntParse(string objValue, int defaultValue)
        
{
            
int returnValue = defaultValue;
            
if (!string.IsNullOrEmpty(objValue))
            
{
                
try
                
{
                    returnValue 
= int.Parse(objValue);
                }

                
catch
                
{
                    returnValue 
= defaultValue;
                }

            }


            
return returnValue;
        }


        
private void Main_Load(object sender, EventArgs e)
        
{

        }


        
private void Main_Shown(object sender, EventArgs e)
        
{
            
//取得当前路径
            FExePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).Replace("file:\\""").Replace("\\"@"\");

            
//从ini读取RPT存放路径          
            Path_Str = IniReadValue("PathInfo""Path", FExePath + "\\CrystalReport.ini");

            
//窗体定位
            Main.ActiveForm.Width = IntParse(IniReadValue("FormInfo""Width", FExePath + "\\CrystalReport.ini"), 540); ;
            Main.ActiveForm.Height 
= IntParse(IniReadValue("FormInfo""Height", FExePath + "\\CrystalReport.ini"), 310);
            Main.ActiveForm.Left 
= IntParse(IniReadValue("FormInfo""Left", FExePath + "\\CrystalReport.ini"), 310);
            Main.ActiveForm.Top 
= IntParse(IniReadValue("FormInfo""Top", FExePath + "\\CrystalReport.ini"), 310);

            
//从ini读取RPT显示信息
            FWidth = IntParse(IniReadValue("ViewInfo""Width", FExePath + "\\CrystalReport.ini"), 160);
            FHeight 
= IntParse(IniReadValue("ViewInfo""Height", FExePath + "\\CrystalReport.ini"), 25);
            FLines 
= IntParse(IniReadValue("ViewInfo""Lines", FExePath + "\\CrystalReport.ini"), 8);

            
//取不出路径则报错退出应用程序
            if (Path_Str.Length == 0)
            
{
                MessageBox.Show(
"CrystalReport.ini中节PathInfo下的Path路径设定错误""提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                
//退出
            }


            
//遍历RPT路径并动态新增页签及radio报表名称
            DirectoryInfo diSource = new DirectoryInfo(Path_Str);
            CreateAll(diSource);
        }


        
private void button1_Click(object sender, EventArgs e)
        
{
            
//获取当前页签及当前页签选中的报表
            string mClass = tabControl1.SelectedTab.Text;
            
string mRpt = "";

            
for (int MI = 0; MI <= FISUB - 1; MI++)
            
{
                
if (mRadioButton[MI].Parent.Parent.Text == mClass)
                
{
                    
if (mRadioButton[MI].Checked == true)
                    
{
                        mRpt 
= mRadioButton[MI].Text;
                        
break;
                    }

                }

            }


            
//获得RPT文件路径
            string mRTPPath = Path_Str + "\\" + mClass + "\\" + mRpt;
            
//MessageBox.Show(mRTPPath, "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            
//新建实例
            Report_View RPT = new Report_View();

            
//预览窗口显示
            RPT.Text = "预览_" + mRpt;
            RPT.Show();

            RPT.RDT1.Load(mRTPPath);
            RPT.RDT1.SetDatabaseLogon(IniReadValue(
"DBInfo""User", FExePath + "\\CrystalReport.ini"), 
                                      IniReadValue(
"DBInfo""Pass", FExePath + "\\CrystalReport.ini"),
                                      IniReadValue(
"DBInfo""Host", FExePath + "\\CrystalReport.ini"),
                                      IniReadValue(
"DBInfo""Dbname", FExePath + "\\CrystalReport.ini")
                                      );
            
//绑定RPT报表
            RPT.RPT1.ReportSource = RPT.RDT1;
        }


        
private void button2_Click(object sender, EventArgs e)
        
{
            
this.Close();//关闭
        }


        
private void Main_FormClosed(object sender, FormClosedEventArgs e)
        
{
            
//保存当前窗口宽高
            string mWidth = Main.ActiveForm.Width.ToString();
            
string mHeight = Main.ActiveForm.Height.ToString();
            
string mLeft = Main.ActiveForm.Left.ToString();
            
string mTop = Main.ActiveForm.Top.ToString();

            IniWriteValue(
"FormInfo""Width", mWidth, FExePath + "\\CrystalReport.ini");
            IniWriteValue(
"FormInfo""Height", mHeight, FExePath + "\\CrystalReport.ini");
            IniWriteValue(
"FormInfo""Left", mLeft, FExePath + "\\CrystalReport.ini");
            IniWriteValue(
"FormInfo""Top", mTop, FExePath + "\\CrystalReport.ini");
        }


    }

}

 4.测试部署
编译后配置好ini, 在我本地测试exe, 通过.
然而事情还没结束, 我们知道虽然它也是exe, 但是.net程序这个冬冬的运行环境和delphi可不一样, 它与java(须配置java虚拟机)类似, 也就是说要想在一台干净的电脑上运行.net程序还必须要安装.net的运行环境----dotnetframwork2.0, 所以我便在测试机器上安装了2.0环境, 本以为应该没问题了, 然而又让我 surprise, 报了一堆错误, 肯定又是环境问题, 那该怎么搞, vs2005里找了一圈, , vs2005自带的安装程序制作工具试试, 果然在做安装程序的时候vs2005检测到一堆依赖项”, 并且这些依赖项都与Crystal Reports有关, 心里大悦啊, 这次总算可以了吧, 编辑后在测试机器上安装, 然后执行, 又报一个文件找不到的错误, 名字是 CrystalDecisions.Web.dll, 于是在回到安装程序制作里, 在应用程序文件夹里添加上这个文件, 再打包测试, 成功.:) (此安装程序发给曹铃铃测试ok)
虽然当前的问题解决了, 但是还遗留了一个问题, 就是上面说的测试通过的机器上有BO-BI的客户端环境, 或者有vs2005的开发环境, 或者有水晶报表的开发环境(仅仅XI版本), 另外我测试了一下, 在除.net外啥环境都没有的情况下会报一个错, 如下图:
 
 

 
posted @ 2008-07-25 13:41  威尼斯的夏天  阅读(1122)  评论(0编辑  收藏  举报