在Visual Studio.net中,可以非常方便地通过创建数据源->拖拽数据源上的字段到窗体上,这样的操作来快速完成数据访问功能的实现,微软同时还在数据控件的属性编辑器上,提供了灵活的更改操作,以便我们在设计时,根据需要灵活调整某个控件的绑定字段,让我回忆一下这个过程:
1、创建数据源:
这个时候,我们通过解决方案浏览器,可以看到,向导为项目添加了一个JWInfoDataSet.xsd,这是一个XML架构文件,包含了DataSet的架构信息,其中JWInfoDataSet.Designer.cs是DataSet所对应的类文件中,而JWInfoDataSet.xsc和JWInfoDataSet.xss则包含了DataSet在设计器中的布局信息:
2、绑定控件,Visual Studio的设计器,提供了拖拽方式自动产生绑定的操作:
首先,我们可以选择绑定后,显示的控件:
默认情况下是通过DataGridView来显示的,我们可以选择详细信息,还可以为每一个表字段选择显示的控件:
选择好以后,只要把对应的字段拖拽到窗体上(也可以整个表拖拽,这样就选择了全部表字段),就可以自动完成字段绑定了:
开发环境会自动创建相应的控件,完成数据的绑定工作,这个时候,我们观察窗体上,多处了与拖拽的数据表字段对应的显示控件,而且在窗体设计器的下方多出了这些控件,也就是说拖拽动作自动生成了DataSet、BindingSource、TableAdapter、tableAdapterManager和BindingNavigator控件,后面我们再讨论这些控件:
3、接下来我们介绍如果在属性设计器中灵活调整数据控件的绑定字段:
首先选中我们要调整其绑定字段的控件,按下F4快捷键,打开属性设置窗口:
展开DataBindings,可以看到Text属性是一个下拉选择框,点击后,我们看到可以在这里灵活调整控件绑定的字段:
- 可以选择设计器生成的BindingSource中字段
- 可以选择其他数据源下的项目数据源
- 可以选择其他数据源下的窗体数据源实例
进一步展开后,我们发现由于我们拖拽的时候,只拖拽了班级表,所以我们在自动绑定(BindingSource)中,只能选择将控件关联到班级表的字段,如果我们需要绑定到学生表的字段,那么我们需要选择其他数据源来实现:
这里的项目数据源,是我们通过数据源创建向导创建数据源后,开发工具自动为项目生成的,是项目级别的,所有的窗体都可以共享:
而Form1列表实例则是属于窗体级别的,是我们从数据源浏览器上拖拽时,开发环境为窗体自动生成的,这是它的定义:
private JWInfoDataSet jWInfoDataSet;
也就是说,这个数据源其他窗体时无法访问的。
接下来,我们看看这几个控件:
jWInfoDataSet是一个DataSet控件,这个控件大家都比较能理解,相当于一个内存数据库,可以保护DataTable、DataRelation等信息。
BindingSource控件是用来绑定数据表的,我们看看自动生成的代码,就能得到清晰的了解:
private System.Windows.Forms.BindingSource 班级BindingSource;
//
// 班级BindingSource
//
this.班级BindingSource.DataMember = "班级";
this.班级BindingSource.DataSource = this.jWInfoDataSet;
也就是说,BindingSource通过设定DataSource和DataMember两个属性,完成绑定工作,以上诉代码为例,DataSource设置为我们创建的jWInfoDataSet,然后设置DataMember为班级,这样就绑定到了班级表,接着我们看到有这样的代码:
this.班级BindingNavigator.BindingSource = this.班级BindingSource;
这就是自动生成的另外一个控件BindingNavigator,BindingSource完成数据绑定后,通过公开MoveFirst、MovePrevious、MoveNext、MoveLast等方法给BingdingNavigator使用,BindingNavigator控件上有很多按钮,这些按钮可以浏览记录,可以添加和删除记录,每一个按钮都是ToolStrpItem的实例,BindingNavigator控件监听这些按钮的Click事件,然后对应地调用其所关联的BindingSource的方法,例如当用户单击了BindingNavigator的MoveFirstItem属性所对应的按钮时,BindingNavigator就会自动调用BindingSource的MoveFirst方法。
最后还有一个TableAdapter控件,这个控件和DataAdapter类似,可以获取数据,然后填充数据集。我们看看自动生成的相关代码:
private DataBindingDemo.JWInfoDataSetTableAdapters.班级TableAdapter 班级TableAdapter;
this.班级TableAdapter = new DataBindingDemo.JWInfoDataSetTableAdapters.班级TableAdapter();
注意这里的代码,这个自动生成的TableAdapter是一个强类型的TableAdapter!
//
// 班级TableAdapter
//
this.班级TableAdapter.ClearBeforeFill = true;
ClearBeforeFill属性可以控制填充数据前,是否先清除掉数据集中已有的数据。
最后,在窗体加载时,TableAdapter完成了数据的加载:
this.班级TableAdapter.Fill(this.jWInfoDataSet.班级);
看到这里,我们会不免心生疑问,都没有为TableAdapter做什么设置的操作,仅仅通过定义和实例化,就能直接用来填充数据集了,要理解这其中的奥秘,我们需要研究一下我们通过数据源创建向导创建的数据源,开发环境为它生成了哪些代码:
首先,我们看到在JWInfoDataSet.Designer.cs文件中,定义了一个“数据集名称+TableAdapters”的命名空间,为什么是TableAdapter呢?因为每个不同的表都会有一个与其对应的TableAdapter,所以就会有很多的TableAdapter。然后我们看到了与班级表对应的TableAdapter:班级TableAdapter的定义,通过该类的几个字段成员_adapter、_connection、_transaction、_commandCollection,我们知道,原来我们在上面看到的简单定义后直接填充数据的效果,需要这里的代码来实现支持,不难想象,这四个成员完成了连接数据库、执行sql命令,然后通过SqlDataAdapter的Fill方法承担起来上面简洁代码的实现支持。再仔细研究这个班级TableAdapter的定义:
其中有一个InitAdapter的方法,完成了TableAdapter的初始化工作:设置连接的表,以及字段的对应关系,还有查询、删除、更新命令的语句。
public virtual int Fill(JWInfoDataSet.班级DataTable dataTable) {
this.Adapter.SelectCommand = this.CommandCollection[0];
if ((this.ClearBeforeFill == true)) {
dataTable.Clear();
}
int returnValue = this.Adapter.Fill(dataTable);
return returnValue;
}
最后我们看到了它的Fill方法,这个方法名执行Select命令,然后通过SqlDataAdapter完成对DataTable表的填充工作。
仔细分析之下,我们看到班级TableAdapter的Fill方法,需要传入一个JWInfoDataSet.班级DataTable类型的数据表来接收数据,这也是一个强类型,所谓的强类型,就是说这个DataTable或者说TableAdapter,不再是.net提供的通用的类了,不能与任意的表来关联,只能关联到特定类型的表,例如班级表。我们可以看看JWInfoDataSet.班级DataTable类的定义:
首先自动生成的代码注释,也告诉我们这是一个强类型,这个类在我们拖拽后,自动产生,根据我们拖拽的字段,这里对应有相应的定义。
再来看看它的添加行的方法定义:
方法的名字是Add班级Row,为什么这么写呀,因为是强类型呀,不能添加其他的Row,必须是班级的Row,班级的Row必须由班级编号,班级名称、部门编号,专业代码四个列组成,而且顺序还不能乱!
如果继续往下分析班级Row,可以看到这也是一个强类型:
这个类封装了一个班级表中的行信息。
剩下最后一个控件没有分析了,那就是tableAdapterManager,这实际上可以理解为一个管理TableAdapter的类,因为会有很多个TableAdapter,然后我们在修改记录后,可以通过一个地方,完成所有的更新:
this.tableAdapterManager.UpdateAll(this.jWInfoDataSet);