ADO.NET 初学者指南






4.79/5 (247投票s)
2004年10月6日
19分钟阅读

1940854

38473
ADO.NET 初学者指南。
引言
这是一个简单的 ADO.NET 数据库应用程序,它从数据库表中返回结果,将输出写入 DataGrid
和 TextBox
,并使用 Button
(First、Previous、Next、Last)来导航记录。
在收到用户的许多回复和建议后,我更改了一些地方,使代码更具可读性。像其他人一样,我也在 MSDN 库中搜索了最常用的方法,以捕获 Windows Forms 中的按键(Up、Down、Esc...),并将其包含在代码中,因为有些用户迫切地向我寻求此功能。您还可以找到其他请求的方法,以获取 DataGrid
中单元格/行的内容。
我选择了 Microsoft Access 数据库(如 ADO.NET OleDb 对象所示),因为它易于使用,并且您不需要运行 Microsoft SQL Server。但是,如果您使用 Microsoft SQL Server 数据库(如 ADO.NET SQL 对象所示),ADO.NET 会经过高度优化。
我现在已经为这个项目(个人地址簿)添加了第二部分,在ADO.NET 数据库操作初学者指南中,您可以在 TextBox
es 中进行数据操作(Delete、Save/Update、Add)。
目录
- 什么是 ADO.NET?
- 连接到 ADO.NET 数据库
- 使用 DataSet 填充记录
- 使用 DataAdapter 将数据加载到 DataSet 中
- 在 DataGrid 中显示数据 / 两表之间的数据关系
- TextBoxes 的数据绑定
- 使用 CurrencyManager
- 使用 Next、Previous、Last、First 按钮浏览记录
- 如何在 DataGrid 中捕获按键(Up、Down、Esc、...)
本项目使用 Visual Studio .NET 2003 和 Microsoft Access 在 Windows XP Pro 上开发。
1. 什么是 ADO.NET?
ADO.NET 是 .NET (Dot Net) 平台的新数据库技术,它建立在 Microsoft ActiveX® Data Objects (ADO) 之上。
ADO 是一种与语言无关的对象模型,是 Microsoft 统一数据访问策略的基石。
ADO.NET 是 .NET Compact Framework 的一个组成部分,提供对关系数据、XML 文档和应用程序数据的访问。ADO.NET 支持各种开发需求。您可以创建数据库客户端应用程序和由应用程序、工具、语言或 Internet 浏览器使用的中间层业务对象。
ADO.NET 定义了 DataSet
和 DataTable
对象,这些对象经过优化,可用于在内网和互联网之间(包括通过防火墙)移动非连接数据集。它还包括传统的 Connection
和 Command
对象,以及一个名为 DataReader
的对象,它类似于只进、只读的 ADO recordset
。如果您创建一个新应用程序,您的应用程序在大多数时候都需要某种形式的数据访问。
ADO.NET 在 Microsoft .NET 平台中提供数据访问服务。
您可以使用 ADO.NET 通过新的 .NET Framework 数据提供程序访问数据,这些提供程序是
- SQL Server 数据提供程序 (
System.Data.SqlClient
) - OLEDB 数据提供程序 (
System.Data.OleDb
) - ODBC 数据提供程序 (
System.Data.Odbc
) - Oracle 数据提供程序 (
System.Data.OracleClient
)
ADO.NET 是一组向 .NET 开发人员公开数据访问服务的类。ADO.NET 类位于 System.Data.dll 中,并与 System.Xml.dll 中的 XML 类集成。
ADO.NET 类有两个核心组件:DataSet
和 .NET Framework 数据提供程序。
数据提供程序 是一组组件,包括
- Connection 对象 (
SqlConnection
,OleDbConnection
,OdbcConnection
,OracleConnection
) - Command 对象 (
SqlCommand
,OleDbCommand
,OdbcCommand
,OracleCommand
) - DataReader 对象 (
SqlDataReader
,OleDbDataReader
,OdbcDataReader
,OracleDataReader
) - 和 DataAdapter 对象 (
SqlDataAdapter
,OleDbDataAdapter
,OdbcDataAdapter
,OracleDataAdapter
)。
DataSet
对象表示数据的非连接缓存,它由 DataTable
和 DataRelation
组成,这些对象表示命令的结果。
ADO.NET 对象模型
2. 连接到 ADO.NET 数据库
在处理数据库之前,您必须(此处)添加 OleDb
.NET 数据提供程序命名空间,方法是在代码模块的开头放置以下内容
using System.Data.OleDb;
对于 SqlClient
.NET 数据提供程序命名空间也是如此
using System.Data.SqlClient;
using
语句应放置在代码的第一位。
现在,我们必须声明一个连接字符串,指向 MS Access 数据库 "PersonDatabase.mdb"。
public string
conString=@"Provider=Microsoft.Jet.OLEDB.4.0;" +
@" DataSource=..\\..\\PersonDatabase.mdb";
数据库应位于指定的路径中,否则您应相应地更改路径。
下一步是创建一个 OleDbConnection
对象。然后我们将连接字符串传递给这个 OleDbConnection
对象。您现在可以编写代码来创建一个新的 ADO.NET Connection 对象,以便连接到 OLE DB 提供程序数据库。
OleDbConnection con = new OleDbConnection(conString);
如果您不介意输入很多,您也可以显式引用声明的对象。
System.Data.OleDb.OleDbConnection con =
new System.Data.OleDb.OleDbConnection(conString);
这是连接到数据库的代码片段
//using declaration for OLE DB
using System.Data.OleDb;
//specify the ConnectionString property
public string conString=
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=..\\..\\PersonDatabase.mdb";
//Initializes a new instance of the OleDbConnection
OleDbConnection con = new OleDbConnection(conString);
// open the database connection with the property settings
// specified by the ConnectionString "conString"
con.Open();
在许多早期应用程序中,趋势是在应用程序启动时打开连接,直到应用程序终止才关闭连接。打开和关闭数据库连接是一个昂贵且耗时的操作。大多数数据库对其允许的并发连接数都有限制。
例如:每个连接都会消耗数据库服务器上的某些资源,并且这些资源不是无限的。大多数现代 OLE DB 提供程序(包括 SQL Server 提供程序)都实现了连接池。如果您创建数据库连接,它们将保存在一个池中。当您需要应用程序的连接时,OLE DB 提供程序会从池中提取下一个可用的连接。当您的应用程序关闭连接时,它会返回到池中,并可供下一个需要连接的应用程序使用。
这意味着打开和关闭数据库连接不再是昂贵的操作。如果您关闭连接,这并不意味着您已断开与数据库的连接。它只是将连接返回到池中。如果您打开连接,这意味着它只是从池中获取一个已打开的连接。许多 ADO.NET 书籍建议不要长时间保持连接。因此,您应该
- 在需要时打开连接,并且
- 完成任务后立即关闭它
例如:这是获取数据库连接的另一种方式
// setup the global SqlConnection object and constr in your class
private SqlConnection con = null;
private string constr ="Integrated Security=SSPI;" +
"Initial Catalog=Northwind;" +
"Data Source=SONY\\MYSQLSERVER;";
private void fnGetConnection()
{
try
{
// setup the database connection
con = new SqlConnection(constr);
con.Open();
}catch (Exception ex) {
MessageBox.Show("Error in connection : "+ex.Message);
}finally {
// dispose of open objects
if (con != null)
con.Close();
} //finally
}
例如:您想打开连接,填充 DataSet
,然后关闭连接。如果连接失败,您想获取错误消息。
try
{
con.Open();
dadapter.Fill(dataset1);
con.Close();
} catch (Exception ex) {
MessageBox.Show("Error in retrieving data: " + ex.Message);
}
例如:如果您想保存已更改的数据,那么您只需打开连接,更新数据,然后关闭连接并接受更改。如果失败,则显示错误消息,拒绝更改,然后关闭连接。
try
{
DataSet changes = dataset.GetChanges();
con.Open();
datapter.Update(changes);
con.Close();
dataset1.AcceptChanges();
}catch (Exception ex) {
MessageBox.Show("ErrorR: " + ex.Message);
dataset1.RejectChanges();
con.Close();
}
3. DataSet
DataSet
类似于非连接 Recordset
对象的数组。它支持非连接数据访问和操作,允许更大的可伸缩性,因为您不再需要一直连接到数据库。DataSet
是已下载并缓存在客户端系统中的提取数据的副本。
DataSet
对象由两个对象组成
DataTableCollection
对象包含null
或多个DataTable
对象(列、行、约束)。DataRelationCollection
对象包含null
或多个DataRelation
对象,这些对象在两个DataTable
对象之间建立父/子关系。
//Create a DataSet
DataSet dset = new DataSet();
DataSet
有两种类型
- 类型化 DataSet
- 非类型化 DataSet
类型化 DataSet 派生自基 DataSet
类,然后使用 XML 架构文件(.xsd 文件)中的信息来生成一个新类。架构中的信息(表、列等)被生成并编译到这个新的 DataSet
类中,作为一组一流的对象和属性。类型化数据集 更易于阅读。Visual Studio 代码编辑器也支持 IntelliSense。在编译时,它具有类型检查,因此在将值分配给 DataSet
成员时错误更少。因此,使用类型化 DataSet 具有许多优点。
示例:以下代码访问 Customers
表第一行中的 CustomerID
列。
string str;
str=dset.Customers[0].CustomerID;
不使用设计器手动创建类型化 DataSet
- 在 XSD 架构文件所在的位置调用命令提示符 (
cmd
)。 - 使用 XSD.EXE 实用程序为类型化
DataSet
创建类。
xsd.exe /d /l:cs mydataset.xsd /n:mynamespace
/d : you create a DataSet.
/l:cs - set the language as C#.
/n:mynamespace - the class should use the namespace "mynamespace".
使用这些参数的 XSD.EXE 的输出将是一个 .cs 类文件 (mydataset.cs)。
使用 csc.exe 编译该类。
csc.exe /t:library mydataset.cs /r:System.dll /r:System.Data.dll
/r:System.XML.dll /out:bin/mydataset.dll /t:library
编译为库组件 (DLL)。
/r
: - 指定您需要引用的程序集/out
: - 将编译后的程序集保存在当前目录的 bin 子目录中
非类型化 DataSet 未由架构定义,相反,您必须自己添加表、列和其他元素,方法是在设计时设置属性或在运行时添加它们。典型场景:如果您事先不知道您的程序与返回 DataSet
的组件交互的结构是什么。
上面用于非类型化 DataSet 的等效代码是
string str;
str=(string)dset.Tables["Customers"].Row[0].["CustomerID"];
DataSet
是一个容器;因此,您必须用数据填充它。
您可以通过多种方式填充 DataSet
- 通过使用 DataAdapter 对象和 Fill 方法
例如
string strCon = @"Data Source=SONY\MYSQLSERVER;" + "Initial Catalog=Northwind;Integrated Security=SSPI"; string strSql="select * from customers"; SqlConnection con=new SqlConnection(strCon); con.Open(); SqlDataAdapter dadapter=new SqlDataAdapter(); dadapter.SelectCommand=new SqlCommand(strSql,con); DataSet dset=new DataSet(); dadapter.Fill(dset); con.Close(); this.dataGrid1.DataSource=dset;
- 通过编程方式创建 DataTable、DataColumn 和 DataRow 对象
创建
DataTable
并使用列和约束定义其结构后,您可以向表中添加新数据行。例如
DataSet dset; DataTable dtbl; DataRow drow; //create a new row drow=dtbl.NewRow(); //manipulate the newly added row using an index or the column name drow["LastName"]="Altindag"; drow[1]="Altindag"; //After data is inserted into the new row, the Add method is used //to add the row to the DataRowCollection dtbl.Rows.Add(drow); //You can also call the Add method to add a new row by passing in an //array of values, typed as Object dtbl.Rows.Add(new object[] {1, "Altindag"});
- 将 XML 文档或流读入 DataSet
以下代码创建一个
SqlConnection
对象,该对象打开与 Pubs 数据库的连接,创建一个 SQL 查询以 XML 形式获取Authors
表的数据,并创建一个新的SqlCommand
对象。创建新的DataSet
后,它使用ExecuteXmlReader
方法将XmlReader
对象传递给DataSet
的ReadXml
方法,该方法允许DataSet
从XmlReader
填充自身。最后,代码将DocumentContent
属性设置为DataSet
的GetXml
方法的结果。XML 使用 XSL 转换文档 authors.xsl(包含在项目中)来格式化 XML 控件显示的 XML 内容。例如
protected System.Web.UI.WebControls.Xml XmlDisplay; string strCon = @"Data Source=SONY\MYSQLSERVER;" + "Initial Catalog=pubs;Integrated Security=SSPI"; SqlConnection con=new SqlConnection(strCon); con.Open(); try { string strSql="select * from FROM authors FOR XML AUTO, XMLDATA"; SqlCommand cmd=new SqlCommand(strSql, con); DataSet dset=new DataSet(); dset.ReadXml(cmd.ExecuteXmlReader(),XmlReadMode.Fragment); XmlDisplay.DocumentContent = dset.GetXml(); }finally { con.Close(); }
- 使用 Merge 方法合并(复制)另一个 DataSet 的内容
您可以合并两个具有大致相似架构的
DataSet
对象。通常,您可以在客户端应用程序上使用合并,将数据源的最新更改合并到现有DataSet
中。这允许客户端应用程序拥有一个包含数据源最新数据的刷新DataSet
。例如
dataset1.Merge(dataset2);
4. DataAdapter
DataAdapter
对象就像一座桥梁,通过其 SELECT
和动作查询命令将数据库和 Connection 对象与 ADO.NET 管理的 DataSet
对象连接起来。它指定要移入和移出 DataSet
的数据。通常,这采用对 SQL 语句或存储过程的引用形式,这些语句或存储过程被调用以读写数据库。
DataAdapter
提供了四个属性,允许我们控制如何更新服务器
SelectCommand
UpdateCommand
InsertCommand
DeleteCommand
这四个属性设置为在操作数据时使用的 Command
对象。
DataAdapter
包括三个主要方法
Fill
(用数据填充DataSet
)FillSchema
(查询数据库以获取更新所需的架构信息)Update
(更改数据库时,DataAdapter
调用DeleteCommand
、InsertCommand
和UpdateCommand
属性)
例如
当我们调用 DataAdapter
的 Fill
方法从数据源检索数据并将其填充到 DataSet
中时,将使用 SelectCommand
属性中的 Command
对象。DataAdapter
是位于我们的 DataSet
和数据源之间的守门员。
//Create an instance of a OleDbDataAdapter
//by passing OleDbConnection object and select.. query
OleDbDataAdapter dAdapter = new
OleDbDataAdapter ("select * from PersonTable", con );
//fill the DataSet with records from the table "PersonTable"
dAdapter.Fill(dSet,"PersonTable");
这是本项目中用于获取数据连接、DataSet
和 DataAdapter
的方法。您可以在文件 "DataAccessTierClass.cs" 中找到此方法。
public bool fnGetDataConnection()
{
try {
con =new OleDbConnection(conString);
dAdapter=new OleDbDataAdapter("select * from PersonTable", con);
dSet=new DataSet();
//refreshes rows in the DataSet
dAdapter.Fill(dSet,"PersonTable");
}catch(Exception ex) {
MessageBox.Show("Error : "+ex.Message);
//connectection failed
return false;
}//try-catch
//connection ok!
return true;
}
5. 在 DataGrid 中显示数据 / 两表之间的数据关系
Windows Forms DataGrid
控件以一系列行和列显示数据。Windows Forms DataGrid
控件为 ADO.NET DataSet
提供用户界面。它显示表格数据并允许更新数据源。当您将 DataGrid
控件设置为有效数据源时,该控件将根据数据的形状自动填充,创建列和行。
您可以使用 DataGrid
控件显示单个表或一组表之间的层次关系。如果您想使用 DataGrid
控件,DataGrid
应该通过以下方式绑定到数据源
- 设计时使用
DataSource
和DataMember
属性或 - 运行时使用
SetDataBinding
方法
这是本项目中我用于将 DataSet
绑定到 DataGrid
控件的方法
this.dataGrid1 DataSource = datc.dSet.Tables["PersonTable"];
您一次只能在 DataGrid
中显示一个表。
如果您在表之间定义了父子关系,您可以在相关表之间导航以选择要在 DataGrid
控件中显示的表。
例如
dset.Relations.Add("CustomerOrders",
dset.Tables["customers"].Columns["CustomerID"],
dset.Tables["orders"].Columns["CustomerID"]);
//now here you can use one of the following
this.dataGrid1.DataSource=dset.Tables["customers"];
或者
this.dataGrid1.SetDataBinding(dset,"customers");
customers
:父表orders
:子表Orders
中的CustomerID
是一个外键,引用Customers
表中的CustomerID
主键。
这是一个典型示例,说明如何在 DataGrid
控件上使用表 "Customers
" 和 "Orders
" 之间的父子关系。
此示例中的 DataRelation
允许您在 DataSet
中从一个 DataTable
("Customers
") 导航到另一个 DataTable
("Orders
")。DataSet
类可以包含 null
或多个 DataTable
对象。"Customers
" 和 "Orders
" DataTable
包含一个名为 "CustID
" 的列,它是这两个 DataTable
对象之间的链接。
要运行和测试此示例,请创建一个新项目,将 Button
(此处:button1
)和 DataGrid
(此处:dataGrid1
)拖放到 Form
上,并复制以下代码片段(fnGetConnectionString()
,button1_Click
),并且您还需要运行 SQL Server 2000 或 MS-Access。
public string fnGetConnectionString()
{
//it gives back the connection string :
// change for you the Data Source=.... accordingly
//for MS-Access
//return "Provider=Microsoft.Jet.OLEDB.4.0;
// Data Source=..\\..\\Northwind.mdb";
//for SQLSERVER2000
return "data source=SONY\\MYSQLSERVER;initial" +
" catalog=Northwind;integrated security=SSPI;";
}
private void button1_Click(object sender, System.EventArgs e)
{
// for SQLServer2000
DataSet dset=new DataSet();
string strCustomers="select * from customers";
string strOrders="select * from orders";
SqlConnection sqlcon=new SqlConnection(fnGetConnectionString());
SqlDataAdapter dadapter=new SqlDataAdapter(strCustomers,sqlcon);
dadapter.Fill(dset,"Customers");
dadapter=new SqlDataAdapter(strOrders,sqlcon);
dadapter.Fill(dset,"Orders");
// Add the relation to the DataSet.
dset.Relations.Add("Customer Orders",
dset.Tables["Customers"].Columns["CustomerID"],
dset.Tables["Orders"].Columns["CustomerID"]);
//Display data in the DataGrid
//both work fine
this.dataGrid1.DataSource=dset.Tables["Customers"];
//this.dataGrid1.SetDataBinding(ds,"Customers");
//for MS-Access
/*
//create a DataSet object which will contain the following 2 DataTables
DataSet dset=new DataSet();
string strCustomers="select * from customers";
string strOrders="select * from orders";
OleDbConnection con=new OleDbConnection(fnGetConnection());
OleDbDataAdapter dadapter=new OleDbDataAdapter(strCustomers,con);
//fill the DataSet with the records from the Customers table
dadapter.Fill(dset,"Customers");
dadapter=new OleDbDataAdapter(strOrders,con);
//fill the DataSet with the records from the Orders table
dadapter.Fill(dset,"Orders");
//establish the relation between the 2 DataTable objects
dset.Relations.Add("Customer Orders",
dset.Tables["Customers"].Columns["CustomerID"],
dset.Tables["Orders"].Columns["CustomerID"]);
//both work fine
this.dataGrid1.DataSource=dset.Tables["Customers"]; //show the data in DataGrid
//this.dataGrid1.SetDataBinding(ds,"Customers");
*/
}
现在,如果您通过任何机制更新绑定 DataSet
中的数据,DataGrid
控件将反映更改。如果 DataGrid
及其表样式和列样式将 ReadOnly
属性设置为 false
,则可以通过 DataGrid
控件更新 DataSet
中的数据。DataGrid
最常见的四个有效数据源是
DataTable
类DataView
类DataSet
类DataViewManager
类
此应用程序首次发布时,我收到用户的电子邮件,询问如何获取您单击的 DataGrid
单元格的内容,或如何获取您单击的 DataGrid
行内容。所以现在,我有一种方法可以做到这一点,并且不想对您隐瞒。
// you click in the cell or the rows
// of the DataGrid and get the content of it
private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e)
{
/* be warned: if you click the last cell on the Datagrid, you get
an unhandled exception of type 'System.ArgumentOutOfRangeException.
because there is no further columns after the last column(Country).
To avoid this, I tried a different way: in a try-catch, get the right
cell content. if the last column cell clicked, display the exception
and the cell content one before. But you can comment
the MessageBox.Show line if not wished */
//get the row number on the DataGrid
int iRownr=this.dataGrid1.CurrentCell.RowNumber;
//get the column number on the DataGrid
int iColnr=this.dataGrid1.CurrentCell.ColumnNumber;
//get the content of the cell in the clicked cell on the Datagrid
object cellvalue1=this.dataGrid1[iRownr, iColnr];
object cellvalue2=null;
//get the next cell content in the same row
try {
cellvalue2=this.dataGrid1[iRownr, iColnr+1];
//display (cellvalue1+cellvalue2) in TextBox "textBox1"
this.textBox1.Text=cellvalue1.ToString()+" "+cellvalue2.ToString();
} catch(Exception ex) {
//the exception occurs here because we increment iColnr+1
MessageBox.Show("No further columns after the last " +
"column(Country) -->> "+ex.Message,"STOP");
cellvalue2=this.dataGrid1[iRownr, iColnr-1];
//display this time (cellvalue2+cellvalue1) in TextBox "textBox1"
this.textBox1.Text=cellvalue2.ToString()+" "+cellvalue1.ToString();
}//catch
}
6. TextBoxes 的数据绑定
数据绑定是指将数据源的一些元素与应用程序的一些图形元素绑定的能力。
Windows Forms 中的数据通过调用 DataBindings
进行绑定。Windows Forms 允许您轻松地绑定到几乎任何包含数据的结构。
Windows Forms 控件支持两种类型的数据绑定
- 简单数据绑定
- 复杂数据绑定
简单数据绑定允许您在控件中显示单个数据元素,例如 DataSet
表中的列值。您可以将控件的任何属性简单绑定到数据值。简单数据绑定可以在设计时使用控件的 DataBindings
属性或在运行时动态执行。这是 TextBox
控件或 Label
控件等通常只显示单个值的控件的典型绑定类型。
例如
// Simple DataBinding for TextBox "textBox1"
textBox1.DataBindings.Add("Text", dataset, "studentTable.studentID");
上面的控件 "textBox1
" 通过 BindingContext
对象绑定到 DataSet
(dataset
) 上 "studentTable
" 表的 "studentID
" 列。
复杂数据绑定是控件绑定到多个数据元素的能力,通常是数据库中的多个记录,或绑定到任何其他类型可绑定数据元素的多个实例。DataGrid
、ListBox
和 ErrorProvider
控件支持复杂数据绑定。
典型场景
您希望在列表框中显示产品名称,然后在 TextBox
中检索您选择的产品的 ProductID
。
例如
您可以使用 DataSource
和 DataMember
属性添加复杂数据绑定。
datagrid1.DataSource = dSet;
// Use the DataMember property to specify the DataTable.
datagrid1.DataMember = "PersonTable";
这是本项目中用于绑定所有 TextBox
es 的方法
private void fnGetDataBindingForTextBoxes()
{
this.textboxFirstname.DataBindings.Add("Text",
datc.dSet.Tables["PersonTable"],"FirstName");
this.textboxLastname.DataBindings.Add("Text",
datc.dSet.Tables["PersonTable"],"LastName");
this.textboxTitle.DataBindings.Add("Text",
datc.dSet.Tables["PersonTable"],"Title");
this.textboxCity.DataBindings.Add("Text",
datc.dSet.Tables["PersonTable"],"City");
this.textboxCountry.DataBindings.Add("Text",
datc.dSet.Tables["PersonTable"],"Country");
}
7. 使用 CurrencyManager
如果您希望使数据绑定控件相互同步(这意味着显示来自同一记录的数据),则使用 CurrencyManager
对象。例如:如果您想向窗体添加一个 TextBox
控件,并将其绑定到 DataSet
(例如 dSet
)中表的列(例如 Customers.FirstName
),该控件将与此窗体的 BindingContext
对象通信。反过来,BindingContext
对象将与 TextBox
控件绑定的数据的特定 CurrencyManager
对象通信。
每个 Windows Form 都有一个 BindingContext
对象,它跟踪 Windows Form 上的所有 CurrencyManager
对象。CurrencyManager
跟踪数据源中的位置。当您将数据对象绑定到控件(即 TextBox
)时,会自动分配一个 CurrencyManager
对象。如果您将多个控件绑定到同一个数据源,它们将共享同一个 CurrencyManager
。
在您使用 ADO.NET 数据库(连接和关闭数据库)并显示记录(例如,在 DataGrid
中)的正常情况下,您永远不需要 CurrencyManager
对象。但是,如果您想知道数据结构(例如,数据库中的表)中的确切位置(就像我做的那样),您必须使用 CurrencyManager
对象,因为 CurrencyManager
为此目的具有 Position
属性。例如,您可以在 Next 或 Previous 或 First 或 Last 按钮中操作 Position
属性,我已经在我的程序中这样做了。
例如
如果您想知道 DataTable
中有多少条记录,您只需查询 BindingContext
对象的 Count
属性。
this.BindingContext[dataset1,"PersonTable"].Count - 1 ;
如果您想从 BindingContext
对象获取当前位置
this.BindingContext[dataset1, "PersonTable"].Position + 1;
数据绑定后,您调用并初始化表的 CurrencyManager
。这是我用于初始化表 "PersonTable
" 的 CurrencyManager
的方法
public void fnSetCurrencyManager()
{
currManager = (CurrencyManager)this.
BindingContext [ datc.dSet.Tables["PersonTable"]] ;
}
8. 使用 Next、Previous、Last、First 按钮浏览记录
一旦数据填充到 DataGrid
中,您就可以使用 Next、Previous、Last、First 按钮,或通过单击 DataGrid
的行,或通过使用箭头键(向上箭头和向下箭头)来浏览记录。
如果 DataGrid
当前正在显示数据,则不会为导航键引发任何标准键盘事件。您仍然可以使用向上和向下箭头键在 DataGrid
中导航,但是,因为我没有实现它,您不会在 StatusBar
中获得记录位置。
为了捕获 DataGrid
上的按键,您必须重写处理命令键的 ProcessCmdKey
方法。您可以在第 9 节中找到此方法。
我还包含了两种新方法,以便在使用 Next、Previous、Last 或 First 按钮时突出显示 DataGrid
中的记录,因为如果您单击此类按钮,通常不会在 DataGrid
中突出显示该行。默认情况下,如果您单击 DataGrid
行,该行将使用 BackColor
和 ForeColor
突出显示。
第一种方法
fnSelectUnselectLastFirstRow(int posi)
如果您单击 First 或 Last 按钮,第一个或最后一个记录将在 DataGrid
中被选中并突出显示。为此,您调用 fnSelectUnselectLastFirstRow()
方法,并为第一个记录传递参数 0
(零)(fnSelectUnselectLastFirstRow(0);
),以及为最后一个记录传递 (this.currManager.Count-1)
。
fnSelectUnselectLastFirstRow(this.currManager.Count-1);
这是我为此目的使用的第一种方法
private void fnSelectUnselectLastFirstRow (int posi)
{
//unselect the last selected/highlighted row
this.dataGrid1.UnSelect(this.dataGrid1.CurrentRowIndex);
//select the last or first row
this.dataGrid1.Select(posi);
}
第二种方法
fnSelectUnselectCurrentRow(int num1, int num2)
如果您单击 Next 或 Previous 按钮,下一个或上一个记录将在 DataGrid
中被选中并突出显示。为此,您调用方法 fnSelectUnselectCurrentRow();
并为下一个记录传递参数 (1,-1)
(fnSelectUnselectCurrentRow(1,-1);
),或为上一个记录传递 (-1,1)
(fnSelectUnselectCurrentRow(-1,1);
)。
这是为此目的使用的第二种方法
private void fnSelectUnselectCurrentRow(int num1, int num2)
{
//get the current row index
this.iRowIndex=this.dataGrid1.CurrentRowIndex;
//increment or decrement the index by (num1,bum2)1,-1 or -1,1 depending on
//Next or Previous button because we want to select next or previous row
//if num1 is +1 you clicked Next so select next row
//if num1 -1 you clicked Previous so select previous row
//I use in both select and unselect plus(+)
// it´s like in math: e.g.7+(-1)=7-1= 6 or7+(+1)=7+1=8
this.iRowIndex=this.iRowIndex+num1;
//select the current row
this.dataGrid1.Select(this.iRowIndex);
//increment or decrement the index by -1 or 1
//so that we can unselect the previous row
this.iRowIndex=this.iRowIndex+num2;
//unselect the previous row
this.dataGrid1.UnSelect(this.iRowIndex);
}
现在回到启用和禁用按钮
当您单击 First 按钮时,位置将设置为 0
(零),因为第一行从零开始。
currManager.Position=0;
和
- 禁用 First 和 Previous 按钮,因为数据源中没有上一个记录。
- 启用 Next 和 Last 按钮,因为还有后续记录。
当您单击 Next 按钮时,数据中的 position
增加 1
并移动到下一行。
currManager.Position +=1;
和
- 只要有后续记录,就启用 First 和 Previous 按钮。
- 否则,禁用 Next 和 Last 按钮,这意味着您已到达记录的末尾。
当您单击 Previous 按钮时,数据中的 position 减少 -1
并移动到上一行。
currManager.Position -=1;
和
- 只要有后退记录,就启用 Next 和 Last 按钮。
- 否则,禁用 First 和 Previous 按钮,这意味着您已到达记录的开头。
当您单击 Last 按钮时,数据中的 position 设置为最后一个记录(行)。
this.currManager.Position=this.currManager.Count-1;
和
- 禁用 Next 和 Last 按钮,因为不再有后续记录。
- 否则,启用 First 和 Previous 按钮,以便您可以向后导航。
为了启用和禁用按钮,我使用带有四个参数的函数/方法 fnEnableDisableButtons
(两个 Button
,用于 StatusBar
的 string
,用于 true
=启用,false
=禁用 的 bool
)。
private void fnEnableDisableButtons(Button bt1, Button bt2, string str, bool b)
{
bt1.Enabled=b;
bt2.Enabled=b;
this.statusBar1.Text=str;
}
9. 如何在 DataGrid 中捕获按键(Up、Down、Esc、NumLock...)
每次您在 DataGrid
中按下 Up、Down、NumLock 和 Esc 键时,我都会在 statusBarPanel1
和 statusBarPanel2
中显示文本,但您不会看到记录编号,因为我认为这会有点令人困惑且编码过多。
像许多用户一样,我也寻找了一种在 DataGrid
中捕获按键的方法,并首先在 MSDN 库中找到了它。所以我决定将其包含在代码中,以便用户可以使用它。对于大多数情况,标准的 KeyUp
、KeyDown
和 KeyPress
事件可以捕获和处理按键。但是,并非所有控件都在所有条件下为所有按键引发标准的 KeyUp
、KeyDown
事件。DataGrid
控件就是其中之一。
如果没有将数据分配给网格,箭头键(LEFT、RIGHT、UP 和 DOWN)只会引发 KeyUp
事件。如果 DataGrid
显示数据,则不会为导航键引发任何标准键盘事件。DataGrid
是此功能最常请求的控件。您还可以截获组合键,包括 CTRL 和 ALT。此技术不捕获 Print Screen 键。为了在 Windows Forms 控件中捕获按键,您可以重写 ProcessCmdKey
方法,我在其中只更改了 StatusBarPanel
的 Text
。
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
{
switch(keyData)
{
case Keys.Down:
this.statusBarPanel2.Text="Down";
this.statusBarPanel1.Text = "Trapped keystrokes on DataGrid...";
break;
case Keys.Up:
this.statusBarPanel2.Text="Up";
this.statusBarPanel1.Text ="Trapped keystrokes on DataGrid...";
break;
case Keys.NumLock:
this.statusBarPanel2.Text="NumLock";
this.statusBarPanel1.Text ="Trapped keystrokes on DataGrid...";
break;
case Keys.Escape:
this.statusBarPanel2.Text="Escape";
this.statusBarPanel1.Text ="Trapped keystrokes on DataGrid...";
//invoke the method "fnExitUniversal" from the class "ExitClass"
ExitClass ec=new ExitClass();
ec.fnExitUniversal();
break;
/*
case Keys.Tab:
this.statusBarPanel1.Text="Tab Key Captured";
break;
case Keys.Control | Keys.M:
this.statusBarPanel1.Text="<CTRL>+ M Captured";
break;
case Keys.Alt | Keys.Z:
this.statusBarPanel1.Text="<ALT> + Z Captured";
break;
*/
} //switch
} //if
return base.ProcessCmdKey(ref msg,keyData);
}
结论
我试图向初学者展示 ADO.NET 的基础知识,以及如何在数据库应用程序中使用 ADO.NET,并尽可能保持代码的可读性。此外,我还尝试(根据用户的要求)展示一些关于 DataGrid
控件的有趣技巧和方法。
该项目现在有第二部分(个人地址簿):ADO.NET 数据库操作初学者指南,您可以在 TextBox
中进行数据操作(删除、保存/更新、添加)。
我希望它可以帮助您(一点点)了解 ADO.NET 是什么,并且您可以在这里找到一些对您的项目有用的东西。
编程愉快!
历史
- 2005年9月12日:初始版本
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。