1 / 59

第十章 数据库应用程序开发

第十章 数据库应用程序开发. ADO.Net 程序实现典型的数据库应用程序开发。 数据库连接 数据加载 数据的简单和复杂绑定 对 DataSet 数据浏览、插入、删除、确认和取消 DataSet 的表达式列和数据检索 DataSet 中数据表的关联 对数据库的更新: CommandBuilder 的更新机制 报表设计- CrystalReport. SQL Server 基本数据准备:. 建立数据库 teaching 建立下列数据表: Students: 学生表( id,name,classid) Grade: 成绩表( id,subid,grade)

Télécharger la présentation

第十章 数据库应用程序开发

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第十章 数据库应用程序开发 • ADO.Net • 程序实现典型的数据库应用程序开发。 • 数据库连接 • 数据加载 • 数据的简单和复杂绑定 • 对DataSet数据浏览、插入、删除、确认和取消 • DataSet的表达式列和数据检索 • DataSet中数据表的关联 • 对数据库的更新:CommandBuilder的更新机制 • 报表设计-CrystalReport

  2. SQL Server基本数据准备: • 建立数据库teaching • 建立下列数据表: • Students:学生表(id,name,classid) • Grade:成绩表(id,subid,grade) • Classes:班级表(classid,name)

  3. 1.1 ADO.NetADO/OLE DB和ODBC关系图 VC++ VB Delphi ADO OLE DB ODBC RDBMS RDBMS E-Mail Directory Service

  4. .NET DataProvider DataSet Connection DataAdapter DataTable Transaction DataRowCollection SelectCommand Command DataColumnCollection InsertCommand Parameters UpdateCommand ConstraintCollection DeleteCommand DataReader DataReaderCollection DataBase XML ADO.NET结构

  5. 1.2 实例分析:设计一个Form实现对数据表students、subjects和grade编辑和浏览功能 examplea1 程序设计的两种方式 • 自编程序设计方式:自编程序实例化dataset等对象、设置属性、动态连接数据库、实现数据绑定、数据表关联等。 • 可视化设计方式:设计阶段通过添加组件,设置组件属性完成数据库的连接、数据绑定以及数据表的关联等,然后由系统生成部分程序。

  6. 1.2.1 自编程序方式 一)把数据库中数据表数据显示在控件中 1)连接数据库:teaching (SQL Server) 2)数据库中数据载入DataSet:加载students 3)DataSet中数据和控件的绑定:id和TextBox的绑定

  7. 1)连接数据库:teaching (SQL Server) using System.Data.OleDb; private DataSet dataSet; private OleDbConnection oleDbConnection; private OleDbDataAdapter oleDbDataAdapter; private void ConnectDB() { oleDbConnection = new OleDbConnection("Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=teaching"); oleDbConnection.Open(); } • OleDbConnection中连接字符串:新建teaching.udl文件,双击后进入oleDB数据链接属性编辑,连接对应数据库并成功进行测试连接后,关闭窗口后teaching.udl中包含了连接字符串。

  8. 2)数据库中数据载入DataSet:加载students private void LoadData() { oleDbDataAdapter = new OleDbDataAdapter("select * from students", oleDbConnection); dataSet = new DataSet(); oleDbDataAdapter.Fill(dataSet, "students"); } • DataSet本身没有从数据库加载数据的能力,必须通过OleDbDataAdapter提供的Fill方法进行加载。 • 在Fill过程中,若dataSet中没有students,则会自动建立,并把“select * from students”结果载入。

  9. 3)DataSet中数据和控件的绑定:id和TextBox的绑定3)DataSet中数据和控件的绑定:id和TextBox的绑定 private void BindingControls() { textBox1.DataBindings.Add("text", dataSet, “students.id"); } • textBox1的Text属性和dataSet中students表的id字段进行绑定。 • 绑定的结果是textBox1的值和dataSet中students表的id值会同步变化。 • 在Form的load事件中执行下列语句:ConnectDB();LoadData();BindingControls();

  10. 二)复杂绑定:classid数据取自classes表,提供选择功能。二)复杂绑定:classid数据取自classes表,提供选择功能。 • LoadData中增加下列程序 oleDbDataAdapter.SelectCommand = new OleDbCommand("select * from classes",oleDbConnection); oleDbDataAdapter.Fill(dataSet, "classes"); • 在BindControls中增加下列程序: comboBox1.DataSource = dataSet.Tables["classes"]; ; comboBox1.DisplayMember="name"; comboBox1.ValueMember = "classid"; comboBox1.DataBindings.Add("selectedValue", dataSet, "students.classid");

  11. 三)加入浏览功能 • DataSet中的Table和数据库中数据表一样,没有行顺序的概念,所有没有提供行号定位的方法和属性 • 当一个数据表数据绑定在Form上的若干控件后,由其BindingContext成员管理从Control类继承的任意对象的 BindingManagerBase对象集合,通过设置其属性Position来控制当前显示的行: this.BindingContext[dataSet, "students"].Position += 1; • 注意students和控件的绑定方式不能写成下列形式,这将使Form仅显示首行内容,而无法通过上述方式定位到其他行。(一致性) textBox1.DataBindings.Add("text", dataSet.Tables["students"], "id"); • 首行对应Position=0,当Position=0,再-1属性值将不变。当Position=总行数-1,再加1同样值不变。

  12. 四)使用绑定对DataSet数据表进行插入和删除 • 插入: this.BindingContext[dataSet, "students"].EndCurrentEdit(); this.BindingContext[dataSet, "students"].AddNew(); • 删除: this.BindingContext[dataSet, "students"].RemoveAt(this.BindingContext[dataSet, "students"].Position); • 取消当前行的修改: this.BindingContext[dataSet, "students"].CancelCurrentEdit();

  13. 五)通过控件输入的DataSet数据的取消和确认功能五)通过控件输入的DataSet数据的取消和确认功能 • 控件输入数据与DataSet数据同步: this.BindingContext[dataSet, “students”].Position值变化或调用this.BindingContext[dataSet, “students”].EndCurrentEdit()后,界面控件数据才写入DataSet。 • 取消修改: dataSet.RejectChanges():取消dataSet创建或调用dataSet.AcceptChanges()以后对dataSet所作的修改。 • 接收修改: dataSet.AcceptChanges():接收对dataSet中数据所做的修改。 • dataSet中各表及表中各行对象均有取消和接受修改的上述两个方法,区别是其作用的对象范围不同。

  14. 六)把DataSet数据存入数据库 • 更新数据库中数据表的唯一途径是执行SQL的insert、update或delete语句,dataSet中数据的修改(表的增、删、改),要反映到数据库中,必须根据修改情况产生并执行相应的SQL语句。所以要根据对DataSet数据表的修改更新对应数据库中的数据表,必须完成下列三部分工作: • 标记DataSet中数据表做过修改的所有行,并记录作了何种修改(增、删、改)。 • 编写或生成相应的带参数的insert、delete或update语句。 • 对每个修改行,根据修改的种类,执行相应的更新数据库的语句。

  15. 1)标记修改行及修改种类 • 在dataSet中表的每一行对象有一个RowState属性,记录了该行是否作了修改及作了何种修改的信息: dataSet.Tables["students"].Rows[0].RowState 其值可为:DataRowState.Added/Modified/Deleted和Unchanged。 • 当dataSet中表数据载入后,各行的状态为Unchanged,当对表中某行进行修改后,会根据修改类型自动改变该行的状态。在调用AcceptChanges后,对应行状态恢复到Unchanged状态。 • 由于更新数据库的update方法是依据该状态来确定哪些行要修改和做什么修改,所以在用dataSet数据更新数据库前,不要调用用AcceptChanges。

  16. 2)编写或生成相应的带参数的SQL语句(1) 使用OleDBCommandBuilder产生更新数据库的带参数的SQL语句 • OleDbCommandBuilder可根据oleDBDataAdapter中的Select Command中的select语句,即调用Fill方法时执行的select语句,生成带参数的update、delete或insert语句。 • 在窗口类中增加成员:OleDbCommandBuilder oleDbCommandBuilder; 在LoadData加载(Fill)students数据语句后加: oleDbCommandBuilder = new OleDbCommandBuilder(oleDbDataAdapter); • 实例化oleDbCommandBuilder,并把oleDbDataAdapter作为其属性DataAdapter值,在要生成SQL语句前, oleDbCommandBuilder必须能访问非空的oleDbDataAdapter.SelectCommand。 • 在生成SQL语句前,首先会执行上述的select语句,以获得对应表的列名信息,为生成做准备。 • 生成的SQL语句将存放在该类的基类对应的三个private属性中:它们是UpdateCommand、DeleteCommand和InsertCommand。

  17. 生成带参数的SQL语句条件和时机: • 生成SQL语句的条件(如UpdateCommand) • 基类对应的属性为空(如UpdateCommand=null) • dataSet对应数据表中存在对应状态的行(如存在Modified状态的行) • 在下列调用中会调用oleDbCommandBuilder中生成SQL语句的程序(并非在初始化的时候): • 调用其方法GetUpdateCommand或GetDeleteCommand或GetInsertCommand时,返回对应的命令串。 • 调用其属性DataAdapter值对象oleDbDataAdapter所包含的方法:oleDbDataAdapter.Update(dataSet, “students”);该语句用生成的SQL语句及dataSet修改过的数据更新数据表。 比较特别是oleDbDataAdapter是oleDbCommandBuilder的DataAdapter属性值,其方法却要调用拥有它的对象方法以生成SQL语句,这种调用方法见“3)更新数据库”后的例。

  18. 加入生成SQL语句的语句: • 在LoadData中实例化oleDbCommndBuilder后调用下列语句以生成带参数的更新数据库的SQL语句。 oleDbCommandBuilder.GetDeleteCommand(); oleDbCommandBuilder.GetUpdateCommand(); oleDbCommandBuilder.GetInsertCommand(); • 由于LoadData中先使用oleDbDataAdapter加载了students表,然后同样使用它加载classes表,所以如没有上述语句,则在update时,oleDbCommandBuilder. DataAdapter指向的oleDataAdapter的SelectCommand属性的CommandText为Select * from classes,所以将生成对classes的更新语句。 • 而上述语句生成了关于students的SQL语句,在update时发现oleDbCommandBuilder中已存在这些语句,就不会再生成关于classes的更新语句。

  19. OleDbCommandBuilder其他说明: • 由上可知,SQL语句一但生成,改变oleDbDataAdapter的SelectCommand属性,由于生成SQL语句的第一个条件不满足,不会改变oldDbCommandBuilder中已生成的SQL语句。 • oleDbCommandBuilder.RefreshSchema()可用于清空生成的SQL语句(基类属性),可使生成SQL语句的第一个条件满足。 • 使用OleDbCommandBuilder的限制:select语句必须为单表的查询,包含主键,但不包含只读列(如计算列)。 • 为了生成 INSERT、UPDATE 或 DELETE 语句,OleDbCommandBuilder 会自动使用 SelectCommand 属性来检索所需的元数据集,以获得如列名等信息,所以会降低执行效率。

  20. (2)编写更新数据库的SQL • 如果Fill一个DataSet数据表对应的select语句牵涉多个表,就不能使用oleDbCommandBuilder生成更新数据库的SQL语句,必须手工编写。 • 目标:在Form的下方用DataGrid显示当前学生的各门课的名称及成绩,可以修改成绩但不能添加和删除,点击保存按钮后保存修改内容。 • 增加下列Form类成员: private DataRelation dataRelation; //students和grade的关联 private OleDbDataAdapter oleDbDataAdapter1;//用于grade表

  21. 把grade数据加载到DataSet private void LoadGrade() { oleDbDataAdapter1 = new OleDbDataAdapter(); oleDbDataAdapter1.SelectCommand = new OleDbCommand("select a.id,a.subid,b.subname,a.grade from grade a,subjects b where a.subid=b.subid", oleDbConnection); oleDbDataAdapter1.Fill(dataSet, “grade”);//dataSet中产生grade表 //update时调用的update语句,其中“?”表示定位参数(非命名参数),依次和//下面oleDbDataAdapter1.UpdateCommand.Parameters中的参数对应。 oleDbDataAdapter1.UpdateCommand = new OleDbCommand("update grade set grade=? where id=? and subid=?",oleDbConnection); //定义执行update时三个参数来自dataSet.grade表的那个列 //定义第一个参数:名为“grade”,对定位参数,参数按次序对应,名称无用 OleDbParameter gradeParameter = new OleDbParameter("grade", OleDbType.Integer); //该参数数据来源grade列 gradeParameter.SourceColumn = “grade”; //数据取修改后数据,此语句可省略 gradeParameter.SourceVersion = DataRowVersion.Current;//缺省

  22. //定义第二个参数id:数据取修改前数据 OleDbParameter idParameter = new OleDbParameter("id", OleDbType.Char,6); idParameter.SourceColumn = “id”; //数据来源于id列 idParameter.SourceVersion = DataRowVersion.Original; //定义第三个参数subid:数据取修改前数据,同时指定数据来源于subid列 OleDbParameter subidParameter = new OleDbParameter("subid", OleDbType.Char, 6,"subid"); subidParameter.SourceVersion = DataRowVersion.Original; //将三个参数依次加入参数表,作为update语句中的三个参数 oleDbDataAdapter1.UpdateCommand.Parameters.Add(gradeParameter); oleDbDataAdapter1.UpdateCommand.Parameters.Add(idParameter); oleDbDataAdapter1.UpdateCommand.Parameters.Add(subidParameter); //建立名为students_grade的两表关系,联结条件students.id=grade.id dataRelation = new DataRelation("students_grade", dataSet.Tables["students"].Columns["id"], dataSet.Tables["grade"].Columns["id"]); //将关系加入dataSet.Relation中 dataSet.Relations.Add(dataRelation); } • 在LoadData方法最后加LoadGrade()

  23. 设置并显示DataGrid: private void SetDataGrid() { DataGrid dataGrid = new DataGrid(); //dataGrid与Panel1对齐并设置大小相同 dataGrid.SetBounds(panel1.Location.X, panel1.Location.Y + panel1.Size.Height, panel1.Size.Width, panel1.Size.Height); //把dataGrid加入Form this.Controls.Add(dataGrid); dataGrid.CaptionText = “成绩”; dataGrid.DataSource = dataSet; //students_grade为LoadGrid中已建立的students和grade关系名,其//中students为父表,如此定义使dataGrid仅显示students的当前学生 //成绩,而非所有grade行。 dataGrid.DataMember = “students.students_grade”;

  24. //定义两个在DataGrid中显示的列:课程名称和成绩 //定义两个在DataGrid中显示的列:课程名称和成绩 DataGridTextBoxColumn column1=new DataGridTextBoxColumn(); DataGridTextBoxColumn column2=new DataGridTextBoxColumn(); column1.MappingName="subname"; column1.HeaderText="课程名称"; column2.MappingName="grade"; column2.HeaderText="成绩"; //把两个列加入dataGridTableStyle.GradeColumnStyles DataGridTableStyle dataGridTableStyle=new DataGridTableStyle(); dataGridTableStyle.GridColumnStyles.Add(column1); dataGridTableStyle.GridColumnStyles.Add(column2); //若不设置下列属性,dataGrid将显示select所有列,上述设置不起作用 dataGridTableStyle.MappingName = "grade"; dataGrid.TableStyles.Add(dataGridTableStyle); } • 在Form的Load事件中加入:SetDataGrid()

  25. 3)更新数据库 • 使用下列语句更新数据库(保存按钮的click事件): //确保把和dataSet中students及grade绑定的控件数据写入dataSet this.BindingContext[dataSet,“students”].EndCurrentEdit();this.BindingContext[dataSet, "grade"].EndCurrentEdit(); oleDbDataAdapter.Update(dataSet, "students"); oleDbDataAdapter1.Update(dataSet,"grade"); • Update执行流程如下: • 如oleDbDataAdapter关联了一个oleDbCommandBuilder (即前者为后者的一个属性,判断方法如下例owner!=null) ,若后者尚未生成更新数据库的SQL语句(其基类对象相应属性为空),则生成。 • 执行更新语句:若程序已设置oleDbDataAdapter中的属性如UpdateCommand中的update语句,则执行该语句(即由程序员编写数据库的更新语句),否则则执行oldDbCommandBuilder基类对象属性所存储的update语句 • 下页示例如何在一个对象(oleDbCommandBuilder)的属性值对象(oleDbDataAdapter)的方法中调用拥有它的对象( oleDbCommandBuilder )的方法(生成SQL的方法)

  26. 例:commandBuilder对象的属性值对象dataAdapter的update方法调用commandBuilder的方法GetSQL例:commandBuilder对象的属性值对象dataAdapter的update方法调用commandBuilder的方法GetSQL 调用示例:若没有第二个语句,即dataAdapter不和commandBuilder关联,仍能调用update,但返回null。 DataAdapter dataAdapter = new DataAdapter(); CommandBuilder commandBuilder = new CommandBuilder(dataAdapter); MessageBox.Show(dataAdapter.update()); public class DataAdapter { public object owner; public string update() { if (owner!=null) return ((CommandBuilder)owner).GetSQL(); else return null; } } public class CommandBuilder { private DataAdapter dataAdapter; private string UpdateText; public CommandBuilder(DataAdapter dataAdapter) { this.dataAdapter = dataAdapter; this.dataAdapter.owner = this; } public string GetSQL() { UpdateText="update students set name='wang'where id='01'"; return UpdateText; } }

  27. 七)DataSet表的表达式列 • 目标:在窗口上动态显示该学生的平均成绩。 • 对DataSet中的students,增加计算列avggradec,其值为子表grade中grade字段值的平均值,并动态绑定到对应的TextBox。 private void AddAvgGradeToStudents() { if (dataSet.Tables["students"].Columns["avggradec"] == null) { dataSet.Tables["students"].Columns.Add("avggradec"); dataSet.Tables["students"].Columns["avggradec"].Expression ="avg(child.grade)"; textBox3.DataBindings.Add("text", dataSet, "students.avggradec"); } } • 在LoadData最后加AddAvgGradeToStudents() • 设置textBox3为readonly。 • 修改成绩,平均成绩即刻更新。

  28. DataColumn.Expresion • 获取或设置表达式,用于筛选行、计算列中的值或创建聚合列,假设在grade表中已增加列c。 • 计算列: dataSet.Tables["grade"].Columns[" c"].Expression= "grade*0.9"; • 聚合列:支持sum、max、min和count等聚合函数,见上例。 • 为筛选器指定表达式:值为true/false。 dataSet.Tables["grade"].Columns["c"].Expression= "subid=‘sub001’";

  29. Expression中对父表或子表关系引用 • 通过在列名称前面加 Parent,就可以在表达式中引用父表。例如,Parent.Price引用父表的名为 Price的列。 • 通过在列名称前面加一个 Child,就可以在表达式中引用子表中的列。因为子关系可以返回多行,所以必须在聚合函数中包括对子列的引用。例如,Sum(Child.Price)将返回子表中名为 Price的列的总和。 • 如果某个表有多个子表,则语法是:Child(RelationName)。例如,如果某个表有两个子表,父表名为 Customers,一个子表名为Orders,则 DataRelation对象被命名为 Customers2Orders,引用将为:Avg(Child(Customers2Orders).Quantity)

  30. 八)使用Tag属性绑定列 • 目标:使用RadioButton选择性别,但Students.sex类型为bit,0表示男,1表示女。 • 需要在一个GroupBox中放两个RadioButton,其Text分别为“男”和“女”,而实际只需要获得一个RadioButton的Checked状态就能确定性别。 • 由于值不同,所以不能使用RadioButton的Text属性和students.sex绑定,可用Tag属性与其绑定。这样的设置必须完成两部分工作: • Tag值改变时(浏览时)要对RadioButton的状态作相应改变 • RadioButton状态改变时要改变Tag值

  31. RadioButton状态改变时改变Tag值 • 在RadioButton的CheckedChanged事件中实现: if (radioButton1.Checked) radioButton1.Tag = false; else radioButton1.Tag = true; • Students的sex属性类型为bit,其值只能取0和1,与radioButton1.Tag绑定后,Tag的值为true和false。

  32. Tag值改变时要对RadioButton的状态作相应改变: • 首先实现下列方法: private void SetSexRadioButton(object sender, EventArgs e) { if (radioButton1.Tag.ToString() == "") { radioButton1.Checked = false; radioButton2.Checked = false; return; } if (radioButton1.Tag.ToString() == "False") { radioButton1.Checked = true; radioButton2.Checked = false; } else { radioButton1.Checked = false; radioButton2.Checked = true; } }

  33. 何时调用该方法: • 方案一:在窗口打开后(Load事件)及按浏览按钮时,调用5次。 • 方案二:在窗口打开后(Load事件)和this.BindingContext[dataSet, “students”]对象的PositionChanged事件中调用,调用2次。 • 在BindingControls最后加: radioButton1.DataBindings.Add("tag", dataSet, "students.sex"); this.BindingContext[dataSet, "students"].PositionChanged += SetSexRadioButton; • 在Form的Load事件中加: SetSexRadioButton(this, null);

  34. 九)使用程序对dataSet中数据表的插入、删除、修改和检索。九)使用程序对dataSet中数据表的插入、删除、修改和检索。 • 以上对dataSet的数据表的操作是通过绑定来实现的,修改控件中数据则自动更新数据表中数据,插入和删除则是通过this.BindingContext[dataSet, “students”]的AddNew方法和RemoveAt方法实现。 • 直接对DataSet中数据表操作方法如下: 修改:dataSet.Tables["students"].Rows[0]["name"] = "zhp"; 插入:dataSet.Tables["students"].Rows.InsertAt(DataRow row,int pos) 删除:dataSet.Tables["students"].Rows.RemoveAt(int pos)

  35. 检索: • DataSet中的Table提供了下列两种方法检索符合条件的行,返回符合条件的行。 • Select方法: DataRow[] dataRow = dataSet.Tables["students"].Select("id='" + textBox4.Text + "'");//使用dataRow.GetLength(0)获得符合条件的行数 • Find方法:检索主键值,必须先建立主键,见下例。 DataRow dataRow = dataSet.Tables[“students”].Rows.Find(textBox1.Text);//找不到返回null • 方法比较:Select检索条件任意,可返回多行,Find方法只能对建立的主键值检索,返回最多一行,由于表总关于主键排序,所以检索速度较select快很多。

  36. 检索实例: • 目标:输入学生编号,按“检索”按钮后若该学生不存在,则提示,若存在,则显示该学生信息: • 分析:定位控件显示的行的唯一方法是设置Binding Context的Position属性,而Find和Select返回的是dataRow,余下的问题是如何由已知行dataRow获得该行在table中的行序号,.Net提供了该方法: dataSet.Tables["students"].Rows.IndexOf(dataRow); • 以下使对学生编号检索的程序:

  37. private void button13_Click(object sender, EventArgs e) { int idx; DataColumn[] dataColumn; dataColumn = new DataColumn[1]; dataColumn[0] = dataSet.Tables["students"].Columns["id"]; dataSet.Tables[“students”].PrimaryKey = dataColumn; //至此为设置主键 DataRow dataRow=dataSet.Tables["students"].Rows.Find(textBox4.Text); if (dataRow == null) MessageBox.Show("没有发现符合条件的行!"); else { idx=dataSet.Tables["students"].Rows.IndexOf(dataRow); this.BindingContext[dataSet, "students"].Position = idx; } }

  38. 1.2.2 可视化设计 • 构造DataSet:前面的设计方式,必须在程序运行后,才能确定DataSet的结构(包含哪些Table和Relation等),.Net可以使用XML文件构建DataSet类,这使程序设计和运行可不依赖于和数据库的连接,并且使数据库结构修改后,只要修改对应的XML文件,而不必修改程序。 • 类型化DataSet和非类型化DataSet:前者在程序设计时定义(可编写或生成)DataSet类的内部结构,编译时进行类型检查,所以类型安全,后者则在程序运行时产生DataSet对象的内部结构(1.2.1中例子的方式),可能由于数据类型不一致而使运行时更容易发生错误。

  39. 设计步骤: • 根据数据库中表结构生成DataSet1.XSD文件:使用菜单Add New Item/ DataSet进入可视化地构建DataSet模式(schema),可把Server Explorer中数据表拖入及使用工具栏中的Relation建立数据表之间的联系,然后保存(XML格式)。 • 依据上面生成的XSD文件产生DataSet类和对象定义:在Form中加入工具栏中DataSet,选择Typed dataset。Name选择examplea1. DataSet1(即对应上面XSD文件),系统根据XSD文件内容在dataSet11. Designer. cs文件中产生类定义DataSet1,并在Form1.Designer. cs中定义了该类的对象dataset11。 • 通过设置控件的DataBindings属性(可选dataset11数据项)实施绑定等设置,对应程序也在Form1.Desig ner. cs文件中生成。

  40. 两种类型DataSet数据的访问的区别: • 非类型化DataSet(弱类型DataSet) dataSet.Tables[“students”].Columns[“id”]或 dataSet.Tables[0].Columns[0] • 类型化DataSet(强类型DataSet) dataSet.students.idColumn

  41. 1.2.3 报表设计:CrystalReport 基本原理: • 使用报表设计器设计报表,设计内容存入特殊格式的CrystalReport.rpt文件中,该报表文件也可供其他语言(C++或VB)开发的程序使用。 • 同时生成了一个继承于ReportClass的报表类定义文件CrystalReport.cs,其中包含了该类与rpt文件的关联,用户通过该类实现对上述设计报表的操作。在该类定义文件中可以加入工具箱中的组件(把组件拖入CrystalReport.cs[Designer]页面)。 • 报表通过可视化控件CrystalReportViewer显示。用户只要实例化一个报表类对象,并把包含数据的DataSet对象传给它,由报表类完成对rpt文件的解析,获取数据并在CrystalReportViewer中显示报表。

  42. 报表的数据来源-用非类型化DataSet产生XSD • 使用CrystalReport,在设计阶段必须确定报表的数据来源,即必须构建DataSet框架,报表设计时各数据项可选自构建好的DataSet。 • 在程序运行时,通过报表类的SetDataSource方法把实例化的DataSet(包含数据的)对象传递给报表,该DataSet必须包含报表设计时所用到数据项,或保证两者数据模型完全一致。 • 由于本例DataSet为非类型化,可使用下列语句把DataSet模型输出到XSD文件,供报表设计用: dataSet.WriteXmlSchema("dataset1.xsd");

  43. 一)设计List报表:学生基本信息表\(CryStalReport1)一)设计List报表:学生基本信息表\(CryStalReport1) • 构建报表数据源: • 在项目中加入DataSet1.XSD:在Solution Explorer中右击项目名,选择Add/Existing Item…,选择前面生成DataSet1.XSD文件。 • 系统会生成DataSet1.Designer.cs,其中定义了名为NewDataSet类,该类为依据XSD文件构造的类型化DataSet类,报表设计时可由此获得数据项 • 新建报表:选择菜单Project/Add New Item,选Reporting /CrystalReport。 1)简要介绍Report Wizard: 选择Using the Report Wizard:以下是Wizard各步: • 在Project Data/ADO.NET DataSets/examplea1. DataSet1中选择报表数据相关的表 • 维护表之间关系:对DataSet1中定义的表间关系进行删除,或根据列名(name)或主键自动建立表间关系。 • 选择报表中要输出的列:

  44. 选择分组信息: • 选择统计信息: • 分组排序: • 报表中是否要包含图表 • 过滤条件 • 报表的风格:table含有表格线 2)从空白报表开始设计:选择As a Blank Report • 出现一个空白报表,有5个部分组成,分别是Report Header、Page Header、Details、Report Footer和Page Footer。 • Report Header和Footer只出现在报表头一页和最后一页,Page Header和Footer出现在每一页上,Details为报表的主体-数据部分。 (1)加入要使用的数据项:右击报表,选择Field Explorer,在打开的窗口中右击DataBase Fields选择DataBase Expert,出现的对话框的Data页框中可选择Available Data Sources,其中列出了Project Data/ ADO.NET DataSets/examplea1.NewDataSet中的所有数据表,把它们全部加入Selected Tables。在Link页框中,以图形方式显示了被选中表在NewDataSet所包含的表间关系,可增加通过classid列建立的Students和classes表的关联。

  45. (2)设置Page Head: • 在工具栏选择Text Object,放入Page Header。 • 右击该Text Object,选择Edit Text Object,输入“学生基本信息表” • 右击该Text Object,选择Format Object,设置其字体和对齐方式 • 在Field Explorer中的Special Fields,选择Print Date,并把它拖到上述的标题下。

  46. (3)设置Details • 把Field Explorer中students的id、name、sex和Height和classes的name拖入Details中。 • 编辑Page Header中自动产生的Text Object,输入对应中文,使用Format Object设置对齐方式为居中。 • 右击报表,选择Preview Report,可预览报表,由于报表设计不需要连接数据库,所以数据为根据类型自动生成的模拟数据。 • 调整数据项位置、尺称、字体、对齐方式等 • 使用工具栏中的Box Object和Line Object为表格加上线框,线和框可跨越报表的不同区域。

  47. (4)使用Formula Field:性别输出 • 数据表中性别为bit类型,输出为True和fase,要求输出“男”,女。 • 在Field Explorer中右击Formula Fields,选New,输入Name为SexText,然后按“Use Expert”,进入Formula Editor。 • 通过选择Report Fields、Functions和Operators或输入 if({students.sex}=true) then "女" else "男" • 在报表中删除Sex,然后加上SexText。

  48. (5)分组统计:总平均身高和分组平均身高 • 插入总平均身高:右击报表,选择Insert/Summary,选择统计字段Height和统计函数average,统计字段自动被安排在Report Footer的Height列下。 • 班级分组平均身高: • 在报表中插入分组块:右击报表,选择Insert/Group,选择依据哪个列值分组(选classes.classid)及对该列值排序方式(升/降),确认后报表上出现GroupHeaderSection1和Group FooterSection1,并在GroupHeaderSection1中出现字段Group #1 Name,删除它,为避免在每组开始时显示一个空行,右击该灰色条带选择Hide,则该块内容不输出。 • 在分组块中插入分组的平均身高:操作基本同插入总平均身高的操作,但Summary location选择Group #1。确认后统计字段被自动安排在GroupFootSection1中的Height列下。

  49. 分组修改: • 右击GroupHeaderSection1的灰色条带,选择Group Expert,可以对分组进行增加、删除和修改。(要删除Group Header Section必须先在Group Expert中移除对应分组) • 可以对每个班级分组中再对性别分组,分别统计班级男同学和女同学的平均身高。 • 第二个分组一定是在第一个分组下的分组,不可能同时存在两个并列的分组,就如同不可能存在两个并列的排序一样。

  50. (6)排序和过滤:每个分组要求按姓名排序。(6)排序和过滤:每个分组要求按姓名排序。 • 右击报表 • 选择Report/Record Sort Expert,其中已经存在按分组1和分组2的排序(分组必须排序),把students.name加入Sort Fields。 • 选择Report/Selection Formula/Record或Group对行或分组加入过滤条件。

More Related