22.1.2 数据源
DataGrid是一种显示数据的非常灵活的方式。在除了调用SetDataBinding()方法时参数可以是DataSet和要显示的表名,此外,还可以将下述任何一个数据源作为参数调用该方法:
● 数组(网格可以绑定到任何一个一维数组上)
● DataTable
● DataView
● DataSet或DataViewManager
● 实现IListSource接口的组件
● 实现IList接口的组件
下面几节将给出这些数据源的示例。
1. 显示数组中的数据
这初看起来非常简单,创建一个数组,填充一些数据,再在DataGrid控件上调用SetDataSource (array,null)。下面是一些示例代码:
string[] stuff = new string[] {"One", "Two", "Three"};
dataGrid.SetDataBinding(stuff, null);
SetDataBinding带有两个参数,第一个参数是数据源,在本例中就是数组,第二个参数是null,除非数据源是一个DataSet 或 DataViewManager类,此时这个参数应是要显示的表名。
可以用这个数组代码替换上面示例中的retrieveButton_Click事件处理程序,这段代码的结果如图22-2所示。

图 22-2
可以看出,网格显示出了数组中定义的字符串的长度,而不是这些字符串。原因是在把数组用作DataGrid的数据源时,网格会查找数组中对象的第一个公共属性,并显示这个值,而不会显示字符串。字符串的第一个(也是惟一的)公共属性是其长度,所以就显示这个长度值。
解决这个问题的一种方法是为字符串创建一个包装类,如下所示:
protected class Item
{
public Item(string text)
{
_text = text;
}
public string Text
{
get{return _text;}
}
private string _text;
}
在数据源数组代码中添加一个Item类(从进行的各种处理来讲,它也可以是一个结构)的数组后,结果如图22-3所示。

图 22-3
2. DataTable
在DataGrid控件中显示DataTable有两种方式:
● 如果DataTable是独立的,就调用SetDataBinding(DataTable, null)。
● 如果在DataSet中包含DataTable,就调用SetDataBinding(DataSet, "<Table Name>")。
图22-4所示为运行DatasourceDataTable示例代码的结果。

图 22-4
注意最后一列显示了一个复选框,而不是更常见的编辑控件。DataGrid没有显示其他信息,而是从数据源中读取模式(在本例中是Products表),确定在该列中应显示什么控件。但不要太激动,目前支持的类型只有两种:文本框和复选框。其他类型的映射必须手工处理。
在修改了DataGrid中的字段时,数据库中的数据不会改变,因为此时数据仅存储在本地计算机上—— 没有与数据库的活动连接。后面的“更新数据源”一节将更新原始数据。
3. 显示DataView中的数据
DataView提供了一种过滤和排序DataTable中数据的一种方式。在从数据库中选择数据时,用户一般可以单击列标题,对数据排序。此外,还可以只过滤要显示在某些行中的数据,例如用户修改过的所有数据。DataView允许限制要显示给用户的数据行,但不允许限制DataTable中的数据列。
提示:
DataView不允许修改要显示的数据列,只允许修改要显示的数据行。
本章后面的“DataGridTableStyle和DataGridColumnStyle”一节将介绍一个示例,说明如何限制要显示的数据列。
根据现有的DataTable创建DataView的代码如下所示:
DataView dv = new DataView(dataTable);
创建好后,就可以改变DataView上的设置,当该视图显示在DataGrid中时,这些设置会影响要显示的数据,以及允许对这些数据进行的操作。例如:
● 设置 AllowEdit = false表示在数据行上禁用所有列的编辑功能。
● 设置AllowNew = false 表示禁用新行功能。
● 设置 AllowDelete = false表示禁用删除行的功能。
● 设置 RowStateFilter只显示指定状态的行。
● 设置 RowFilter 可过滤数据行。
● 按照给定的列排序
下一节将介绍使用RowStateFilter设置,其他选项都是可以自我解释的。
(1) 通过数据过滤数据行
创建好DataView后,就可以通过设置RowFilter属性,来改变视图中的数据。这个属性是一个字符串,可用作按照给定条件过滤数据的一种方式—— 该字符串的值就是过滤条件。其语法类似于一般SQL中的WHERE子句,但主要是对已经从数据库中选择出来的数据进行操作。
过滤子句的一些示例如表22-1所示。
表 22-1
|
子 句 |
说 明 |
|
UnitsInStock > 50 |
只显示UnitsInStock列大于50的行 |
|
Client = 'Smith' |
只返回给定客户的记录 |
|
County LIKE 'C*' |
返回County字段以C开头的所有记录—— 例如返回Cornwall、 Cumbria、Cheshire和 Cambridgeshire所在的行,可以使用%表示匹配一个字符的通配符,而*表示匹配0个或多个字符的通配符 |
运行环境尽可能在过滤表达式中使用与源列相匹配的数据类型。例如,在前面的示例中,使用UnitsInStock > '50'表达式就是合法的,尽管该列是一个整数列。但如果提供了一个无效的过滤字符串,就会产生EvaluateException。
(2) 根据状态过滤数据行
DataView中的每一行都有一个定义好的行状态,它们的值如表22-2所示,这些状态也可以用于过滤用户查看的行。
表 22-2
|
DataViewRowState |
说 明 |
|
Added |
新创建的所有行 |
|
CurrentRows |
除了被删除的行以外的其他行 |
|
Deleted |
最初被选中,且已经删除的行—— 不显示已经删除的新建行 |
(续表)
|
DataViewRowState |
说 明 |
|
ModifiedCurrent |
列出所有已被修改的行,并显示这些行的当前值 |
|
ModifiedOriginal |
列出所有已被修改的行,但显示这些行的初值,而不是当前值 |
|
OriginalRows |
最初从数据源中选中的所有行,不包括新行。显示列的初值(即如果进行了修改,不显示当前值) |
|
Unchanged |
没有修改的行 |
图22-5显示了两个网格,一个网格显示已添加、删除或修改的数据行,另一个网格显示状态为上表中一种的行。

图 22-5
过滤器不仅可以用于可视化的行,还可以用于这些行中列的状态。在进行ModifiedOriginal 或 ModifiedCurrent选择时,这是很明显的。这两个状态都在第21章介绍过了,它们都是基于DataRowVersion枚举的。例如,如果用户更新了数据行中的一列,该行就会在选择ModifiedOriginal或 ModifiedCurrent时显示出来,但其实际值可以是从数据库中选择出来的初值(如果选择了ModifiedOriginal),或者DataColumn中的当前值(如果选择了ModifiedCurrent)。
(3) 对数据行进行排序
除了过滤数据外,有时还需要对DataView中的数据进行排序。可以在DataGrid控件中单击列标题,这会按照升序或降序的顺序对该列进行排序,如图22-6所示。惟一的问题是控件只能对一列进行排序,而底层的DataView可以对多个列进行排序。
在对数据列进行排序时,可以单击列的标题(例如上面的ProductName列),也可以通过代码排序,DataGrid会显示一个箭头位图,表示对哪一列进行排序。

图 22-6
要编程设置列的排列顺序,可以使用DataView的Sort属性:
dataView.Sort = "ProductName";
dataView.Sort = "ProductName ASC, ProductID DESC";
上面的第一行按照ProductName列对数据排序,如图22-6所示。第二行按照ProductName列对数据进行升序排序,再以ProductID的降序来排序。
DataView支持对列进行升序或降序排序—— 默认为升序。如果选择对DataView中的多个列进行排序,DataGrid就不会显示任何排序箭头。
网格中的每一列都是强类型化的,其排序顺序不是基于列的字符串表示,而是基于实际的数据。如果DataGrid有一个日期列,要对它进行排序,网格就会按日期来进行排序,而不是按日期字符串来进行排序。
4. 显示DataSet类中的数据
DataGrid主要用于显示DataSet中的数据。和以前的示例一样,DataGrid一次只能显示一个DataTable,但在下面的示例DataSourceDataSet中,可以浏览DataSet中的关系。下面的代码可以根据Northwind数据库中的Customers和Orders表生成这样一个DataTable。这个示例从两个DataTable中加载数据,然后在这些表之间创建了一个关系CustomerOrders:
string source ="server=(local)\\NetSDK;" +
"uid=QSUser;pwd=QSPassword;" +
"database=northwind";
string orders = "SELECT * FROM Orders";
string customers = "SELECT * FROM Customers";
SqlConnection conn = new SqlConnection(source);
SqlDataAdapter da = new SqlDataAdapter(orders, conn);
DataSet ds = new DataSet();
da.Fill(ds, "Orders");
da = new SqlDataAdapter(customers , conn);
da.Fill(ds, "Customers");
ds.Relations.Add("CustomerOrders",
ds.Tables["Customers"].Columns["CustomerID"],
ds.Tables["Orders"].Columns["CustomerID"]);
创建好后,通过调用SetDataBinding,就可以把DataSet绑定到DataGrid上:
dataGrid1.SetDataBinding(ds, "Customers");
这样会得到如22-7所示的屏幕图。

图 22-7
注意,与本章前面的DataGrid不同,每个记录的左边都有一个+号,这表示DataSet在customers 和orders表之间有一个可导航的关系。在代码中可以定义许多这类关系。
单击+号,就会显示关系列表(如果关系已经显示出来,单击+号就会隐藏该关系)。单击关系名,就可以定位到链接的记录上,如图22-8所示,在本例中是列出选中客户的所有订单。

图 22-8
DataGrid控件的右上角还包含两个新图标。箭头允许用户可以导航回父行,显示上一页的内容。标题行显示父记录的细节,单击另一个按钮会隐藏或显示该箭头。
在DataViewManager中显示数据
DataViewManager中显示的数据与DataSet中显示的数据相同,但在为DataSet创建DataViewManager时,会为每个DataTable创建一个单独的 DataView,根据过滤条件或者行的状态改变显示出来的行,如上面的DataView示例所示。即使不希望过滤数据,也可以把DataSet包装到 DataViewManager中,以进行显示,因为这样在修改源代码时可以使用更多的选项。
下面的示例根据上一例中的DataSet 创建一个DataViewManager,然后改变Customers表中的DataView,使之只显示来自英国的客户:
DataViewManager dvm = new DataViewManager(ds);
dvm.DataViewSettings["Customers"].RowFilter = "Country='UK'";
dataGrid.SetDataBinding(dvm, "Customers");
如图22-9所示为DataSourceDataViewManager示例代码的运行结果。

图 22-9
5. IListSource和 IList接口
DataGrid还支持执行IListSource和IList接口之一的所有对象。IListSource只有一个方法GetList(),它返回一个IList接口,而IList比较有趣,它可由运行库中的许多类执行,执行这个接口的类有Array、ArrayList和StringCollection。
在使用IList时,对前面Array的警告也适用于集合中的对象—— 如果使用StringCollection作为DataGrid的数据源,网格中就会显示字符串的长度,而不是我们希望显示的元素文本。





