.NET DataTables、DataSets 和 DataGrids 实用指南 - 第 2 部分
本文档旨在提供使用 Microsoft .NET DataTables、DataSets 和 DataGrid 的实用指南
3 表
应理解表的架构和功能,因为这有助于理解 DataSet
和 DataGrid
的功能。在将 DataGrid 绑定到数据库的过程中,底层代码会创建并关联表集合,这些表集合会填充来自数据库的数据。此外,从数据库创建的 DataSet 可能包含比需要显示的信息更多的表,可能需要添加基于使用其他列中信息的复杂公式的列,并且可能需要将来自多个数据库的数据组合成单个表格视图。这些操作通过从这些数据源中提取信息并填充程序化设计的未绑定表来完成。
从根本上说,一个表包含 Columns
和 Rows
集合,这意味着可以使用访问和操作集合的标准方法。Columns
集合包含每个列的名称、数据类型规范,可能还包含一个分配的默认值。Rows 集合中的每个表行包含每个列的一个单元格。表类拥有一整套用于编辑和管理列和行数据版本以及在发生更改时进行事件通知的方法。图 2 说明了表的总体架构。
图 2 DataTable 分解图
3.1 表创建
可以轻松地从 DataTable 类创建能够包含/管理列、行和事件的表内存对象,如下所示
// Create a table object by using the DataTable class:
DataTable dt = new DataTable();
// Name the table by assigning a data string containing
// the name to the table’s
// TableName property:
dt.TableName = “Elements”;
// or use the DataTable(string TableName) constructor
DataTable dt = new DataTable(“Elements”);
3.2 列 – 创建并添加到表中
一个表包含一组列定义,这些定义将用于定义如何引用行中的每个单元格以及数据内容的类型。以下场景展示了如何定义一个列并将其添加到表的列集合中。
a. 按照“表”部分所述定义一个表。
b. 使用列类创建要添加到表中的列对象
DataColumn dc = new DataColumn();
// Set the properties for the column:
// string name for the column that is used as an index
// for columns collection
// and a cell within a row
dc.ColumnName = “AtomicNbr”;
// string name that is used for a column label or header
// for display purposes
// if not set, then the default value is dc.ColumnName
dc.Caption = “Atomic Number”;
// one of the standard system data types using the
// GetType() method.
dc.DataType = System.Type.GetType(“System.Int32);
// or one could use the typeof operator
dc.DataType = typeof(System.Int32);
// a default value that is assigned each time
// a new row is created
dc.DefaultValue = 0;
// or use one of the other constructor’s such as
// DataColumn(string ColumnName, System.Type DataType)
DataColumn dc = new DataColumn(“AtomicNbr”,
System.Type.GetType(“System.Int32”));
c. 将新列添加到表的 Columns
集合。添加列的顺序决定了它们的从零开始的索引。
dt.Columns.Add(dc);
d. 对要添加到表的 Columns
集合中的每个列重复 b 和 c。
dc = new DataColumn(“Element”, System.Type.GetType(“System.String”));
dc.DefaultValue = string.Empty;
dc.Caption = “Element”;
dt.Columns.Add(dc);
dc = new DataColumn(“Symbol”, System.Type.GetType(“System.String”) );
dc.DefaultValue = string.Empty;
dc.Caption = “Symbol”;
dt.Columns.Add(dc);
dc = new DataColumn(“AtomicMass”, System.Type.GetType(“System.Decimal”) );
dc.DefaultValue = 0.0;
dc.Caption = “Atomic Mass”;
dt.Columns.Add(dc);
.NET 环境中支持的数据类型示例。
数据类型 | .NET 系统类型 |
布尔值 |
System.Boolean |
字节型 |
System.Byte |
Byte[] (数组) |
System.Byte[] |
Char (字符) |
System.Char |
日期时间 |
System.DateTime |
十进制 |
System.Decimal |
双精度浮点型 |
System.Double |
整数 |
System.Int16, System.Int32, System.Int64 |
Single |
System.Single |
字符串 |
System.String |
无符号整数 |
System.UInt16, System.UInt32, System.UInt64 |
TimeSpan |
System.TimeSpan |
此时,已经创建了一个名为“Elements”的表,其中包含“AtomicNbr”、“Element”、“Symbol”和“AtomicMass”四个列,并具有各自的数据类型和默认值。以下三个 DisplayColumnInfo()
方法代码示例通过使用不同的技术访问成员和显示其集合中的数据来展示这一点。在第一个示例中,使用 for 循环说明通过整数索引访问表列集合,而在第二个示例中,使用 foreach
循环说明使用列类类型访问相同的集合。第三个示例使用包含列名称的字符串作为索引。这些访问数据示例说明了处理集合的自然语法方法。
1. for
循环
private void DisplayColumnInfo(DataTable dt)
{lder ColInfo = new StringBuilder();
ColInfo.AppendFormat(“Column\tName\tDataType\n”);
// note that the total number of columns in the
// collection is contained in the ‘Count’ property
for(int j=0; j<dt.Columns.Count; j++)
{
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\ t{3}\n”, j,
dt.Columns[j].ColumnName,
dt.Columns[j].Caption, dt.Columns[j].DataType.ToString());
}
MessageBox.Show(ColInfo.ToString() , “Column Name”,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
2. foreach
循环
private void DisplayColumnInfo(DataTable dt)
{
StringBuilder ColInfo = new StringBuilder();
ColInfo.AppendFormat(“Column\tName\tDataType\n”);
int j = -1;
foreach (DataColumn dc in dt.Columns)
{
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\t{3}\n”, ++j, dc.ColumnName,
dc.Caption, dc.DataType.ToString() );
}
MessageBox.Show(ColInfo.ToString(),
“Column Name”, MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
3. 使用已知列名作为索引——而不是列标题!
private void DisplayColumnDataTypeInfo(DataTa}
3. 使用已知列名作为索引——而不是列标题!
private void DisplayColumnDataTypeInfo(DataTable dt)
{
StringBuilder ColInfo = new StringBuilder();
ColInfo.AppendFormat(“Column\tName\tDataType\n”);
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\n”,1, “AtomicNbr”,
dt.Columns[“AtomicNbr”].DataType.ToString());
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\n”,1, “Element”,
dt.Columns[“Element”].DataType.ToString());
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\n”,1, “Symbol”,
dt.Columns[“Symbol”].DataType.ToString());
ColInfo.AppendFormat(“ [{0}]\t{1}\t{2}\n”,1, “AtomicMass”,
dt.Columns[“AtomicMass”].DataType.ToString());
MessageBox.Show(ColInfo.ToString() , “Column Name”,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
3.3 删除/移除列
一旦表定义完成,可以按如下方式删除或移除列
// For example to delete a column “AtomicMass”
dt.Columns.Remove(“AtomicMass”);
// or using a zero-based index – “AtomicMass” is the
// 4th column with index 3
dt.Columns.RemoveAt(3);
// To make sure that a column can be removed,
// for example, first determine
// whether the column exists, belongs to the table,
// or is involved in a constraint
// or relation.
if (dt.Columns.Contains("AtomicMass"))
if (dt.Columns.CanRemove(dt.Columns["AtomicMass"])
{
dt.Columns.Remove(“AtomicMass”);
}
3.4 修改列属性
修改列属性只需访问该属性并设置其新值。例如
dt.Columns[“AtomicNbr”].ColumnName = “AtomicNumber”;
dt.Columns[“AtomicMass”].DataType = typeof(System.float);
3.5 清除列集合
只需使用 Clear()
方法即可清除整个表列集合。
dt.Columns.Clear();
3.6 克隆表
一旦定义了一个表,就可以用它来创建一个具有相同列集合的相同表,或者作为新表的基础,在新表中可以删除、添加或修改列。原始表的 Clone()
方法用于创建具有相同结构(包括模式和约束)的新表;但是,它不复制行中包含的内容。
DataTable dt1 = dt.Clone();
现在 dt1 可以被修改,例如
// delete a column
dt1.Columns.Remove(“AtomicMass”);
// add a new column to the table
dc = new DataColumn(“IsotopeNbr”,
System.Type.GetType(“System.Int32”));
dc.DefaultValue = 0;
dt1.Columns.Add(dc);
// modify the name and caption of an existing column
dt1.Columns[“AtomicNbr”].ColumnName = “AtomicNumber”;
dt1.Columns[“AtomicNbr”].Caption = “Atomic Number”;
3.7 行——创建并添加到表中。
本节将展示如何使用四种等效方法访问行中的单个单元格,从而在表行集合中添加行并为行赋值。方法的选择实际上取决于任务类型,例如用于填充行的数据源或简单地从行中提取数据。
1. 按照“表和列”部分所述定义一个带有列的表
2. 以下场景是创建行、填充行中的单元格,然后将行添加到表中的基本过程。本节还说明了索引单元格的等效方法,这为开发人员提供了很大的灵活性。
// First create a DataRow variable
DataRow dr;
// Next create a new row and assign it to the DataRow
// object dr using DataTable’s
// NewRow() method.
dr = dt.NewRow();
// dr now contains a cell for each column defined in the
// Columns collection
用于为行中的单个单元格赋值的四种等效方法。
方法 1
// fill each cell using a zero-based cell integer column indexes
dr[0] = 1;
dr[1] = “Hydrogen”;
dr[2] = “H”;
dr[3] = 1.0078;
方法 2
// fill each cell using the column name as the string column index
dr[“AtomicNbr”] = 1;
dr[“Element”] = “Hydrogen”;
dr[“Symbol”] = “H”;
dr[“AtomicMass”] = 1.0078;
方法 3
// fill each cell using DataColumn dc -- this is more applicable
// when using a DataColumn foreach loop
// e.g. foreach (DataColumn dc in dt.Rows) …
DataColumn dc;
dc = dt.Columns[“AtomicNbr”];
dr[dc] = 1;
dc = dt.Columns[“Element”];
dr[dc] = “Hydrogen”;
dc = dt.Columns[“Symbol”];
dr[dc] = “H”;
dc = dt.Columns[“AtomicMass”];
dr[dc] = 1.0078;
方法 4
// fill each cell using DataColumn dc and its ColumnName property
// which is identical to Method 3 but is
// included here for completeness
// Again, more applicable when using a foreach loop
DataColumn dc;
dc = dt.Columns[“AtomicNbr”];
dr[dc.ColumnName] = 1;
dc = dt.Columns[“Element”];
dr[dc.ColumnName] = “Hydrogen”;
dc = dt.Columns[“Symbol”];
dr[dc.ColumnName] = “H”;
dc = dt.Columns[“AtomicMass”];
dr[dc.ColumnName] = 1.0078;
// add the row to the table’s row collection
dt.Rows.Add(dr);
3. 这个场景可以很容易地扩展到一个更通用的过程,即向表中添加 n 行。例如,假设一个二维对象数组“ElementData”包含 n 行和 dt.Columns.Count 列,其中包含要添加到表中的数据。它可以按如下方式加载
DataRow dr;
int j;
for (int i=0; i < n; i++)
{
j = -1;
dr = dt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
j++;
// fill each cell, using the column dc as the index, from the
// previously defined two dimensional object array à ElementData
if (dc.DataType == typeof(System.String))
dr[dc] = (System.String)ElementData[i][j];
else
if (dc.DataType == typeof(System.Int32))
dr[dc] = (System.Int32)ElementData[i][j];
else
if (dc.DataType == typeof(System.Decimal))
dr[dc] = (System.Decimal)ElementData[i][j];
}
dt.Rows.Add(dr);
}
3.8 修改现有表行中的数据
有多种方法可以修改表中的数据,以下是基本机制的说明。其他技术将在以下章节中介绍。
// First create a DataRow variable
DataRow dr;
// Next assign the row in the Rows collection
// to be modified to dr, for example select row
with index = 0
dr = dt.Rows[0];
// Select the
// column within the row to be modified by specifying
// a column index and assign
the new value
dr[“AtomicNbr”] = 1.00781;
等效的替代编码方法如下
dt.Rows[0][“AtomicNbr”] = 1.00781;
或
dt.Rows[0][0] = 1.00781;
3.9 使用 LoadDataRow()
方法填充表
本节中的 LoadElementDataRow()
代码示例方法演示了如何使用 DataTable 的 LoadDataRow
方法将数据加载到表中,该方法接受一个包含行中每个单元格数据的对象。LoadDataRow
方法由 BeginLoadData()
和 EndLoadData()
方法括起来,这些方法用于关闭和打开事件通知以及与链接表相关的其他属性。使用这些方法可以防止事件处理程序进行不必要的处理,否则这些事件处理程序会被触发,这将在“事件处理程序”一节中讨论。此外,如果主键匹配,LoadDataRow
方法将修改现有行,否则将行添加到 Rows 集合中。有关行版本的讨论,请参阅“行版本”一节,其中讨论了表类管理的行不同版本,并提供了示例代码,说明了当表具有主键和不具有主键时 LoadDataRow
方法的不同行为。
private DataRow LoadElementDataRow(DataTable dt,
int AtomicNbr, string Element,
string Symbol, double AtomicMass)
{
// Turns off event notifications,
// index maintenance, and constraints
// while loading data
dt.BeginLoadData();
// Add the row values to the rows collection and
// return the DataRow. If the second
// argument is set to true, then dt.AcceptChanges() is called
//otherwise new rows are
// marked as additions and changes to existing rows are marked as
//modifications.
DataRow dr = dt.LoadDataRow(new object[]
{AtomicNbr, Element, Symbol, AtomicMass}
, false);
// Turns on event notifications, index maintenance, and constraints
// that were turned off
// with the BeginLoadData() method
dt.EndLoadData();
return dr; // returns the DataRow filled
// with the new values
}
3.10 检索表内容
GetTableData()
示例方法从输入表中检索列标签和行数据,并将其格式化为字符串,该字符串可用于打印、复制到剪贴板和导出到制表符分隔的文本文件。
private string GetTableData(DataTable dt)
{
StringBuilder TableData = new StringBuilder();
// retrieve header row column labels
TableData.AppendFormat(“Row”);
foreach (DataColumn dc in dt.Columns)
TableData.AppendFormat(“\t{0}”, dc.ColumnName);
TableData.AppendFormat(“\n”);
// retrieve rows
int j = -1;
foreach (DataRow dr in dt.Rows)
{
TableData.AppendFormat(“[{0}]”,++j);
foreach (DataColumn dc in dt.Columns)
{
TableData.AppendFormat(“\t{0}”, dr[dc] );
}
TableData.AppendFormat(“\n”);
}
return TableData.ToString();
}
当使用 Excel 电子表格或 DataGrid 以网格格式显示时,我们的元素表(包含一行)的输出字符串将如下所示。
行 |
原子序数 |
元素 |
符号 |
原子质量 |
[0] |
1 |
氢 |
H |
1.0078 |
3.11 行版本以及接受/拒绝更改
3.11.1 方法和枚举
这是一个重要的部分,需要理解,因为 Table 类维护行的不同状态和版本,这些状态和版本可用于提供回滚、撤消和事务日志记录功能。也就是说,此状态和版本信息提供了对表数据和 UI 策略的非常强大的程序化控制。
在讨论行状态和版本之前,需要定义 Table 和 Row 方法
表方法 |
描述 |
|
接受表的所有行更改。当调用 DataRow |
|
拒绝自上次调用 Table 或 DataRow |
|
返回一个包含所有已修改行的表。这在构建事务日志以满足政府和公司法规(例如 CFR21-11)时特别有用。 注意:如果在调用 |
行方法 |
描述 |
|
向行集合添加一行 |
|
在行集合的特定位置插入一行 |
|
从行集合中删除指定索引处的行 |
|
接受对行的所有更改,包括对单个单元格的更改,以及分别将行添加到表中和从表中删除。 |
|
拒绝更改行,恢复原始值 |
|
开始行编辑会话 |
|
取消行编辑会话并恢复所有以前的值 |
|
结束行编辑会话 |
Table 对象自动维护四种不同版本的行集合,这为编辑、删除和插入提供了广泛的编程控制。
DataRowVersion |
描述 |
|
此版本包含每个表行中包含的所有值的当前集合。当前集合和默认集合是相同的
|
|
默认行集合包含所有更改。每次创建新行时,新行都使用默认列值进行初始化。每次修改行中的单元格值时,修改都会反映在此表中。 |
|
顾名思义,此版本仅包含已提出更改的行,这些更改仅在调用 |
|
每次调用 |
RowState |
描述 |
|
当一行被添加或插入到 Rows 集合中,并且在调用 |
|
调用
|
|
在调用
|
|
调用 |
|
调用 |
DataTable dt = new DataTable("Elements");
DataColumn AtomicNbr = new DataColumn("AtomicNbr",
System.Type.GetType("System.Int32"));
AtomicNbr.DefaultValue=0;
dt.Columns.Add(AtomicNbr);
DataColumn Element = new DataColumn("Element",
System.Type.GetType("System.String"));
Element.DefaultValue= "Element";
dt.Columns.Add(Element);
DataRow dr;
3.11.2 示例 1 – 行状态
本节展示了不同的行状态及其条件。
dr = dt.NewRow();
dr[Element]="Hydrogen";
dr[AtomicNbr]= 1;
// NewRow Before Add: RowState=Detached
dt.Rows.Add(dr);
// NewRow After Add: RowState=Added
dt.Rows[0].AcceptChanges();
//NewRow After AcceptChanges: RowState=Unchanged
dt.Rows.RemoveAt(0);
// note that the row is marked as Detached when
// RemoveAt() or Remove() is used
//NewRow After RemoveAt: RowState=Detached
// Add the row back, accept the changes and then delete the row
// note that the row state is now marked as deleted when Delete()
// if Delete() is called prior to the row being
// added then the row is marked as Detached.
dt.Rows.Add(dr);
dt.AcceptChanges();
dr.Delete();
// NewRow After Delete: <RowState=Deleted>
以下代码示例将说明上述方法的功能。每段代码之后将有四张表,每种 DataRowVersion 各一张,显示该版本是否包含行以及如果包含,则显示其各自的值。
注意 |
|
3.11.3 示例 2 – 表的初始加载
dr = dt.NewRow();
dr[Element]="Hydrogen";
dr[AtomicNbr]= 1;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr[Element]="Helium";
dr[AtomicNbr]= 2;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr[Element]="Lithium";
dr[AtomicNbr]= 3;
dt.Rows.Add(dr);
dr = dt.NewRow();
// this row contains default values
dt.Rows.Add(dr);
第 0 行只有原始版本,并标记为已删除。当前版本和默认版本相同,四个新行标记为已添加。提议版本表不包含任何值。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
是 |
Added |
1 |
氢 |
[2] |
是 |
Added |
2 |
氦 |
[3] |
是 |
Added |
3 |
锂 |
[4] |
是 |
Added |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
是 |
Added |
1 |
氢 |
[2] |
是 |
Added |
2 |
氦 |
[3] |
是 |
Added |
3 |
锂 |
[4] |
是 |
Added |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Deleted |
0 |
元素 |
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
3.11.4 示例 3 – DataRow AcceptChanges
dt.Rows[0].AcceptChanges();
dt.Rows[1].AcceptChanges();
调用 Rows[0].AcceptChanges()
后,在上述版本表中标记为已删除的第 0 行已从所有版本中删除,所有其他行都有新的索引。下一个 AcceptChanges()
命令引用新排序的 Rows Collection 中的第 1 行。在这种情况下,在 Current、Default 和 Original 版本中,行状态标记为 Unchanged。在 Original 版本中只有一行,它对应于已接受的第 1 行。Proposed 版本表不包含任何值。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Added |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Added |
3 |
锂 |
[3] |
是 |
Added |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Added |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Added |
3 |
锂 |
[3] |
是 |
Added |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
否 |
|||
[3] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
3.11.5 示例 4 – Table AcceptChanges
dt.AcceptChanges();
在调用 Table.AcceptChanges()
之后,所有剩余的行在 Current、Default 和 Original 版本表中都被标记为 Unchanged,并且 Original 版本表与 Current 和 Default 版本表相同。Proposed 版本表不包含任何行。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
3.11.6 示例 5 – DataRow BeginEdit
dt.Rows[1].BeginEdit();
dt.Rows[1]["Element"]= "Helium";
dt.Rows[1]["AtomicNbr"]= 222;
上述代码开始对第 1 行进行编辑会话,新值反映在默认版本表中,并且值现在出现在提议版本表中。所有其他表版本条目保持不变。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
222 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
是 |
Unchanged |
222 |
氦 |
[2] |
否 |
|||
[3] |
否 |
3.11.7 示例 6 – DataRow CancelEdit
dt.Rows[1].CancelEdit();
CancelEdit()
命令将默认值恢复到原始状态,并清除提议版本表中第 1 行的提议值。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
3.11.8 示例 7 – DataRow BeginEdit – 示例 2
dt.Rows[3].BeginEdit();
BeginEdit()
方法使用默认值初始化 Proposed 行 3。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
是 |
Unchanged |
0 |
元素 |
3.11.9 示例 8 – DataRow 更改值 – 示例 2
dt.Rows[3]["Element"]="Carbon";
dt.Rows[3]["AtomicNbr"]= 12;
默认行和提议行 3 的值已更改为反映碳和 12。所有版本中的所有其他行保持不变。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
是 |
Unchanged |
12 |
碳 |
3.11.10 示例 9 – DataRow EndEdit – 修改的行
dt.Rows[3].EndEdit();
dt.Rows[0]["Element"] = "Oxygen";
dt.Rows[0]["AtomicNbr"] = 8;
// Add a new row.
dr = dt.NewRow();
dt.Rows.Add(dr);
调用 EndEdit()
后,第 3 行的 Current 和 Default 版本会更新并标记为 Modified。第 3 行的 Original 版本仍保留更改前的原始值,并标记为 Modified。
接下来的两行将“Oxygen”及其原子序数分配给第 0 行,这些值反映在 Current 和 Default 版本中,它们也被标记为 Modified。第 0 行的 Original 版本保持不变。
接下来的两行将一个新行添加到 Current 和 Default 版本表,并用默认值初始化并标记为 Added。请注意,新行不会出现在 Original 版本表中。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
8 |
氧 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Modified |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
8 |
氧 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Modified |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Modified |
0 |
元素 |
[4] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
3.11.11 示例 10 – DataRow 接受修改行的更改
dt.Rows[3].AcceptChanges();
对第 3 行的 DataRow AcceptChanges()
导致 Current 和 Default 版本表中对应的行被标记为 Unchanged,并且 Original 版本表现在包含相同的行值。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
8 |
氧 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
8 |
氧 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
3.11.12 示例 11 – DataRow RejectChanges
dt.Rows[0].RejectChanges();
调用第 0 行的 DataRow RejectChanges()
方法会使 Current 和 Default 表中相应的行值恢复到 Original 版本值,并且 Current、Default 和 Original 三个表中的第 0 行的 Row State 被标记为 Unchanged。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
3.11.13 示例 12 – 不带主键的表的 LoadDataRow
dt.BeginLoadData();
dt.LoadDataRow(new object[]{1,"Deuterium"}, false);
dt.EndLoadData();
如果一个表没有主键,那么 LoadDataRow 方法将创建一个新行并用对象数组中的值填充它。查看 Current 和 Default 版本表中的第 0 行和第 5 行,它们都具有相同的原子序数,但元素名称不同。此外,第 5 行被标记为“Added”。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
[5] |
是 |
Added |
1 |
氘 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Added |
0 |
元素 |
[5] |
是 |
Added |
1 |
氘 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Unchanged |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
否 |
|||
[5] |
否 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
|||
[5] |
否 |
3.11.14 示例 13 – 带主键的表的 LoadDataRow
// must delete the row with a duplicate
// AtomicNbr in order to create
// a primary key.
dt.Rows.RemoveAt(5);
dt.AcceptChanges();
// Create a primary key and load the new object array data.
dt.PrimaryKey = new DataColumn[] {dt.Columns["AtomicNbr"]};
dt.BeginLoadData();
dt.LoadDataRow(new object[]{1,"Deuterium"}, false);
dt.EndLoadData();
如果一个表有主键,那么 LoadDataRow
方法将修改行中的数据(如果主键匹配),否则它会将行追加到表中。查看 Current 和 Default 版本表中的第 0 行,元素名称已从 Hydrogen 更改为 Deuterium。在 Current、Default 和 Original 三个版本表中,第 0 行现在被标记为“Modified”。
当前版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
1 |
氘 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Unchanged |
0 |
元素 |
默认版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
1 |
氘 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Unchanged |
0 |
元素 |
原始版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
是 |
Modified |
1 |
氢 |
[1] |
是 |
Unchanged |
2 |
氦 |
[2] |
是 |
Unchanged |
3 |
锂 |
[3] |
是 |
Unchanged |
12 |
碳 |
[4] |
是 |
Unchanged |
0 |
元素 |
提议版本
行 |
包含版本 |
行状态 |
原子序数 |
元素 |
[0] |
否 |
|||
[1] |
否 |
|||
[2] |
否 |
|||
[3] |
否 |
|||
[4] |
否 |
3.11.15 获取版本和状态信息的示例代码
上述表格是使用以下过程生成的。
static void PrintRowVersions(DataTable dt)
{
DataRowVersion[] rowVer = new DataRowVersion[4];
rowVer[0] = DataRowVersion.Current;
rowVer[1] = DataRowVersion.Default;
rowVer[2] = DataRowVersion.Original;
rowVer[3] = DataRowVersion.Proposed;
StringBuilder TableData = new StringBuilder();
for(int i=0; i<rowVer.Length; i++)
{
// Print the value of each column in each row.
TableData.AppendFormat("{0} Version\n", rowVer[i].ToString());
// retrieve header row column labels
TableData.AppendFormat("Row\tHas Versions\tRow State");
foreach (DataColumn dc in dt.Columns)
TableData.AppendFormat("\t{0}", dc.ColumnName);
TableData.AppendFormat("\n");
int n=-1;
foreach(DataRow row in dt.Rows )
{
n++;
if (row.HasVersion(rowVer[i]) )
{
// Print the specified version of the row's value.
TableData.AppendFormat("[{0}]\tYes\t{1}",
n.ToString(), row.RowState.ToString());
foreach (DataColumn dc in dt.Columns)
{
TableData.AppendFormat("\t{0}", row[dc,rowVer[i]]);
}
TableData.AppendFormat("\n");
}
else
{
TableData.AppendFormat("[{0}]\tNo\t", n.ToString());
for(int j=0; j<dt.Columns.Count; j++)
TableData.AppendFormat("\t ");
TableData.AppendFormat("\n");
}
}
TableData.AppendFormat("\n");
}
// output string data to a text file using a StreamWriter
// StreamWriter sw = new StreamWriter("c:\RowVersions.txt");
sw.Write(TableData.ToString());
// sw.Close();
}
3.12 处理 DataTable 错误
可以通过检查表的 HasErrors
属性值来检查 DataTable 是否包含任何带有错误的行。以下代码演示了如何隔离带有错误的行及其列。
if (dt.HasErrors)
{ // Errors have occurred in rows in table dt
foreach (DataRow dr in dt.Rows)
{
if(dr.HasErrors)
{
// Row has errors
// GetColumnsInError() returns an array of
// DataColumns that contain errors
foreach(DataColumn dc in dr.GetColumnsInError())
{
// GetColumnError returns a description of the Column error
MessageBox.Show(dr.GetColumnError(dc.Ordinal));
}
}
}
}
3.13 DataTable 事件
以下代码提供了一个添加 DataTable 列更改事件处理程序的示例,处理程序中的代码说明了一些处理新列值的技术。
dt.ColumnChanged += new DataColumnChangeEventHandler
(this.SampleForm_ColumnChanged);
private void SampleForm_ColumnChanged(object sender,
System.Data.DataColumnChangeEventArgs e)
{
if(e.Column.Ordinal <op> …)
{
// could perform validation checks such as range values
// or formatting or other types of
//processing on the changed column.
}
if(e.Row.HasErrors)
{
// clear the error
e.Row.SetColumnError(e.Column, string.Empty);
// check to see if row has any more errors
DataColumn [] dcErrors = e.Row.GetColumnsInError();
// if there are no more errors then clear the row error flag.
if(dcErrors.Length == 0)
e.Row.ClearErrors();
}
}