DataTable,DataView和DataGrid中一些容易混淆的概念
一、DataTable
DataTable 表示内存中数据的一个表,它完全是在内存中的一个独立存在,包含了这张表的全部信息。DataTable 可以是从通过连接从数据库中读取出来形成一个表,一旦将内容读到DataTable 中,此DataTable 就可以跟数据源断开而独立存在;也可以是完全由程序自己通过代码来建立的一个表。
◆ DataColumn
一个表是由行和列组成的一个两维的结构。表的结构是由DataColumn 对象的集合组成,DataColumn 对象集合可由DataTable.Columns 属性中能获取到,通过定义每一列的数据类型来确定表的架构,类似数据库中定义表。定义完表的结构就可以根据结构来生成DataRow,用DataTable.NewRow() 方法来生成此DataTable 结构的新行。
一个DataTable 是由DataRow 的集合组成的,DataRow 的集合这个可以由DataTable.Rows 属性来访问。
DataTable 还可以通过现有的列用Expression 属性的表达式创建一些列。
1、创建计算出的列
比如:已经有了一个表结构,表中有一个DataColumn 的集合,其中有一个叫UnitPrice 的列,你可以新建一个DataColumn,设置好ColumnName,再设置此列的表达式,DataColumn.Expression="UnitPrice * 0.086",这个列的值就是名字为UnitPrice 的列计算出来的,在创建表达式时,使用ColumnName 属性来引用列。
2、第二个用途是创建聚合列
聚合列聚合通常沿着关系执行(有关关系的描述见下面DataRelation 部分),如果order 表有名为detail 的子表,两个表之间通过order.orderid 和detail.orderid 两个列建立一个关系DataRelation 对象名为“order2detail”,在主表order 中就可以建立一个聚合列,将计算每个order 在detail 表中含有的所有item的价格的和:DataColumn.Expression = “sum(child(order2detail).price)",child(order2detail) 表示通过关系order2detail 联系到的子表,child(order2detail).price 就表示子表的price 列。
◆ DataRow
DataRow 对象没有直接在代码中使用的构造函数,一般是从具有一定结构的DataTable 用NewRow() 方法来新建一个DataRow 对象。一个DataRow 根据其是独立的,还是属于某个DataTable,是否修改过,是否被DataTable 删除等等不同的情况有不同的状态,由DataRow.RowState 属性公开,如下表:
成员名称 | 说明 |
Added | 该行已添加到DataRowCollection中,AcceptChanges 尚未调用。Deleted该行已通过DataRow的Delete方法被删除。 |
Deleted | 该行已通过DataRow的Delete方法被删除。 |
Detached | 该行已被创建,但不属于任何DataRowCollection。DataRow 在以下情况下立即处于此状态:创建之后添加到集合中之前;或从集合中移除之后。 |
Modified | 该行已被修改,AcceptChanges 尚未调用。 |
Unchanged | 该行自上次调用 AcceptChanges 以来尚未更改。 |
一个DataRow 对象刚被创建之后其状态是Detached,是孤立的一个存在,所以建立了DataRow 之后在DataRow 中的单元填充了数据后还要通过DataTable.Rows.Add(DataRow) 方法将此DataRow 添加到DataTable,DataRow 添加到DataTable 后, 这个DataRow 的状态就转变为Added。当修改了这个DataRow 后,这个DataRow 状态转为Modified,当用DataRow.Delete() 方法删除DataRow 后,DataRow 状态将转为Deleted,不过此行还存在在DataTable 中的,只是状态改变了,这时用DataTable.Rows.Count 查看行数,跟删除前是一样的。只有在调用了DataTable.Remove(DataRow) 方法后,此DataRow 才被从DataTable 移除,状态也回复到Detached 孤立状态。
一旦调用了DataTable.AcceptChanges() 方法后,所有的行将根据不同的状态做不同的处理,Added、Modified、Unchanged 将保留当前值,Deleted 的行将从DataTable中移除,最后所有的行的状态都置为Unchanged。当DataTable 是从DataAdapter.Fill(DataSet,DataTable) 方法填充而形成的,Fill() 方法将自动调用AcceptChanges() 方法,将DataTable 的行状态都置为Unchanged。并且,如果Fill方法中指定的那个DataTable 在要填充的那个DataSet 不存在时,会生成一个跟数据源表同样的结构的DataTable 并填充数据。
◆ DataRelation
表示两个DataTable 对象之间的父/子关系。可以类比于数据库中的表之间的关系,父表相当于关系列为主键的表,子表相当于关系列为外键的表。DataRelation 构造函数一般为:DataRelation(String, DataColumn, DataColumn) , string 为关系名,第一个DataColumn 为建立关系的父表列,第二个DataColumn 为建立关系的子表列,建立关系的两个列的 DataType 值必须相同。
建立好了关系,必须把这个关系加入到DataTable的ParentRelations 属性或ChildRelations 属性,这两个属性包含这个表的所有的跟父表的关系和跟子表的关系。若关系中此表是父表则将此关系加入到ChildRelations 集合中,否则加入到ParentRelations 集合中。
二、DataView
DataView表示用于排序、筛选、搜索、编辑和导航的 DataTable 的可绑定数据的自定义视图。可以将DataView同数据库的视图类比,不过有点不同,数据库的视图可以跨表建立视图,DataView则只能对某一个DataTable建立视图。DataView一般通过DataTable.DefaultView 属性来建立,再通过通过RowFilter 属性和RowStateFilter 属性建立这个DataTable的一个子集。
RowFilter属性用来筛选要查看DataTable中哪些行的表达式,这个表达式同上面所说的建立计算列的表达式相同。例如:"LastName = 'Smith'",这就是只查看列LastName的值为'Smith'的那些数据行。
RowStateFilter 属性用来设置DataView中的行状态筛选器,上面介绍DataRow时介绍了DataRow的状态,一个DataRow可能有五种状态,RowStateFilter就是可以通过这些状态来筛选要查看的行集。其实DataRow不仅有五种状态,DataRow还有版本的问题,比如当DataRow的状态为Modified,即这行已经被修改了,这时这个DataRow就会有两个版本,Current版本和Original版本(修改前的)。实际上RowStateFilter属性是综合了DataRow的状态和版本来筛选的(RowStateFilter确省值是CurrentRows)见下表:
成员名称 | 说明 |
Added | 一个新行。 |
CurrentRows | 包括未更改行、新行和已修改行的当前行。 |
Deleted | 已删除的行。 |
ModifiedCurrent | 当前版本,原始数据(请参阅ModifiedOriginal)的修改版本。 |
ModifiedOriginal | 原始版本(尽管它后来已被修改并以ModifiedCurrent 形式存在)。 |
None | 无。 |
OriginalRows | 包括未更改行和已删除行的原始行。 |
Unchanged | 未更改的行。 |
DataView.Count 属性得到的计数是在应用了 RowFilter 和 RowStateFilter 之后,获取 DataView 中记录的数量。
DataView 是建立在DataTable 基础上的,DataView.Table 属性可以得到此DataView 对应的那个DataTable。DataView 的行叫DataRowView,可以从DataRowView 直接通过DataRowView.Row 属性得到此DataRowView 对应的DataRow。
三、DataGrid
这里说的DataGrid是winform中的DataGrid,一般都是跟DataView绑定来显示DataTable中的数据,和修改DataTable中的数据。
DotNet的DataGrid的功能强大,可是在使用上与以前的习惯不太一样,有时还比较麻烦,所以很多人都对这个DataGrid感到有些摸不着头脑,有一种无从下手的感觉,其实把一些概念搞清楚了许多问题就会迎刃而解了。
DataGrid通过DataSource和DataMember 属性来绑定其要显示的数据源。数据源一般是DataTable、DataView、DataSet等,不过将这些数据源绑定到DataGrid时实际上是绑定的DataView。若数据源是DataTable时,实际上是绑定了此DataTable的DefaultView,若数据源是DataSet时,则可以向DataMember属性设置一个字符串,该字符串指定要绑定到的表,然后再将DataMember指定的那个DataTable的DefaultView绑定到DataGrid。
所以DataGrid实际显示的是DataTable经过筛选的DataView。
◆ DataGrid以何种方式显示DataView的数据
DataGrid绑定到一个DataView后,由DataGrid.TableStyles中的DataGridTableStyle 对象的集合来控制这个DataView的哪些列要显示,列的宽度多少,列标头的文本是什么等等。确省的DataGrid.TableStyles中不包含任何对象,这时DataGrid将会按照DataView列的顺序将所有的列都显示出来。一般应用中都会设置TableStyles来控制显示的内容及格式。
例如DataGrid绑定到一张叫order的DataTable,这个DataTable包含了OrderID、CustomerID、OrderDate、ShipName、ShipAddress等字段,可以看到DataGrid将会按照DataView列的顺序将所有的列都显示出来
我们只想显示OrderID、CustomerID、OrderDate这三个字段,并且想将OrderID的列表头显示为“订单号”,CustomerID显示为“客户号”,OrderDate显示为“订单日期”,这就要用TableStyles来控制了。
新建一个TableStyle,将此TableStyle.MappingName属性对应到这个TableStyle要控制的那个DataTable的名字:
DataGridTableStyle myTableStyle = new DataGridTableStyle();
myTableStyle.MappingName = "myDateTable";
//再建立三个DataGridColumnStyle,分别用来控制将要显示的三个列:
DataGridColumnStyle myColumnStyle1 = new DataGridTextBoxColumn();
myColumnStyle1.MappingName = "OrderID";
myColumnStyle1.HeaderText = "订单号";
DataGridColumnStyle myColumnStyle2 = new DataGridTextBoxColumn();
myColumnStyle2.MappingName = "CustomerID";
myColumnStyle2.HeaderText = "客户号";
DataGridColumnStyle myColumnStyle3 = new DataGridTextBoxColumn();
myColumnStyle3.MappingName = "OrderDate";
myColumnStyle3.HeaderText = "订单日期";
//将这三个DataGridColumnStyle添加到TableStyle中:
myTableStyle.GridColumnStyles.Add(myColumnStyle1);
myTableStyle.GridColumnStyles.Add(myColumnStyle2);
myTableStyle.GridColumnStyles.Add(myColumnStyle3);
//最后将TableStyle添加到DataGrid中:
dataGrid1.TableStyles.Add(myTableStyle);
//将TableStyle添加到DataGrid后,再绑定数据源。
没有目标的人都只在帮有目标的人完成目标