C#与SQL连接:GridView控件对数据库的操作
gridview和datagrid的异同
gridview 是 datagrid的后继控件,在.net framework 2 中,虽然还存在datagrid,但是gridview已经走上了历史的前台,取代datagrid的趋势已是势不可挡。gridview和datagrid功能相似,都是在web页面中显示数据源中的数据,将数据源中的一行数据,也就是一条记录,显示为在web页面上输出表格中的一行。
gridview相对于datagrid来说,具有如下优势,功能上更加丰富,因为提供了智能标记面板(也就是show smart tag)更加易用方便,常用的排序、分页、更新、删除等操作可以零代码实现!具有pagertemplate属性,可以自定义用户导航页面,也就是说分页的控制更加随心所欲。gridview和datagrid在事件模型上也多有不同之处,datagrid控件引发的都是单个事件,而gridview控件会引发两个事件,一个在操作前发生,一个在操作后发生,操作前的事件多位***ing事件,操作后的事件多位***ed事件,比如sorting 事件和sorted 事件,rowdeleting和rowdeleted事件。
gridview操作初步
1、显示数据源中的数据
从toolbox中选取gridview控件拖到页面上,然后点击右键,选择show smart tag,在choose data source中选择 new data source, 出现data source configuration wizard,选择连接字符串,可以选择已经存放在web.config中的connectionstring ,然后可以选择是使用存储过程,还是从表或视图中选择数据。
在这一步,左侧的where语句可以指定查询条件,点击where,出现add where clause ,选择要设定条件的列,操作符是等于还是like还是其它,然后选择source,也就是说要限定的条件从哪里取值,可以是control、session、form、cookie、querystirng等,如果选择control,那么需要在右侧,选中是那个控件,然后还可以设定默认值,设定完后系统自动生成sql 表达式和值的表达式,此时点击add按钮,完成条件的添加,where cluase下出现刚刚添加的条件。如果没有点击add,很容易设定了条件,但是因为没有添加到where子句中,所以不起作用。
在这一步,左侧的order by,可以让我们设定排序列,就是我们取出的记录要按照什么派逊,可以设定三个列,是升序还是降序。
在这一部,左侧的advanced,可以设定advanced sql generation options,这里可以生成这个查询的insert、update、delete语句,当然,前提是您选择的字段中必须包含了主键。当您想在gridview中不编写任何代码实现对表格的编辑、删除等操作时,就必须在配置数据源时,在这里生成insert、update、delete这些语句。 gridview中编辑删除等操作的零代码,就是根据配置数据源时自动生成的这些语句来完成数据源的更新删除等操作的。
在最后一步,您可以测试一下您刚刚生成的查询是否正确,最后点击完成,数据已经出现在页面上了,按ctrl+f5运行。
恭喜您!您已经会使用asp.net来显示数据库中的数据了。
2、让gridview可以分页
gridview把数据显示出来了,但是那么多条记录罗列到一页上是不合适的,我们应该对数据进行分页。还记得在asp时代,分页是多么的麻烦,需要编写很多的代码,而且各种分页组件也应运而生。而在gridview中,您会发现,分页是如此的简单,只需要您轻点鼠标,在show smart tag中,选中enable paging,表格的分页操作变完成了,是不是so easy呢。
并不是什么数据源都可以让gridview实现自动分页的,比如如果datasourcemode是datareader,就无法实现自动分页。而且只有objectdatasource是界面级别的支持分页,类似我们常用的sqldatasource都是先提取所有的记录,然后只显示本页需要显示的记录,然后抛弃其余的记录,都有点浪费资源的啦!
当gridview的allowpaging属性设置为true的时候,我们实现了分页,我们还可以对分页进行一些个性化的设置。常用的属性包括:pageindex――设置数据显示的当前页面,默认是0,也就是数据的首页。pagesize ――也就是一页显示多少条记录,默认为10条。在pagersettings中,还可以对分页的导航按钮进行详细设置,在mode属性中,可以设置:numeric――默认的,分页用数字表示,1,2,3……。nextprevious、nextpreviousfirstlast、numericfirstlast均可顾名思义,显示上一页、下一页、首页、末页等。当mode设定不是numeric时,那么可以通过设定firstpagetext、lastpagetext等属性来实现分页导航时,到首页、末页、下页、上页时显示的文字提示。
如果想实现分页界面的完全自动控制,还可以点击gridview右键,选择编辑模版-pagertemplate来实现,在模版中加入若干个button控件,然后将button控件的commandname属性设置为page,将commandargument属性分别设置为first、last、prev、next或者一个数字,即可实现分页操作。
3、gridview中的编辑、删除、排序
数据从数据源中提取出现,显示在网页上后,我们如果需要对其中的数据进行编辑、更新、删除等操作,还是不需要编写任何代码,利用gridview内置的功能即可实现。
在智能标记中,点击编辑列,在avaliable fields中,选择comandfield,然后双击edit,update,cancel和delete,我们就为gridview添加了编辑和删除功能。如果在配置数据源的时候,我们已经生成了insert、update、delete这些语句,那么我们现在就可以执行程序。点击页面上的edit,出现update和cancel按钮,同时当前行除了主键以外的列,数值都放在了一个文本框中,可以进行编辑,然后点击update即可保存。点击delete,删除当前行记录。您是不是已经被gridview强大的功能折服了呢?
在show smart tag中,选择enable sorting,这时所有列的header都变成了一个超链接,其实这些都是一个linkbutton控件,运行代码,在网页生成的数据表中,点击第一行中的列名,即可按照当前列进行排序,再次点击则反向排序。
如果您只需要对其中的几列进行排序,可以在智能标记中,选择编辑列,选中要排序的列,然后在右侧的属性中找到sortexpression属性,然后从下拉框中选择根据哪个字段排序,一般当然是当前字段咯,完成排序的设置。如果您不需要这一列参与排序,那么只需要把此列的sortexpression属性后面的值删除,也就是说设置成空字符串即可。试一试,是不是排序已经尽在掌握之中了呢?
gridview中的自定义列
gridview可以根据数据源自动生成列,但是如果我们需要自定义列的显示方式,让gridview的列完完全全的由我们自己来控制,我们就需要用到一种特殊的列――templatefield。因为gridview生成的列都是一个字段一列,如果我们需要把两个字段合并为一列显示呢?我们可以使用模板列。我们可以指定包含标记和控件的模版,自定义列的布局和行为,我们可以新建一个模版列,也可以直接把已经生成的列转换为模版列来进行个性化的设置。
在gridview上单击右键,选择编辑模版,在弹出菜单中选择要编辑的列,出现列模版的编辑画面。其中headertemplate――自定义列的标头部分显示的内容,footertemplate――脚注部分显示哪谌荩?/span>itemtemplate――是打开网页后此列数据显示的内容,edititemtemplate――此列处于编辑状态时如何显示,alternatingitemtemplate――交替项显示的内容,也就是说为了显示效果,可以隔行分别以不同的风格显示。
example 1:
我们现在假设有一个表,其中有一个字段是username,我们现在产生一个自定义列,自定义列中包含此人的照片,同时我们假定照片的路径为image/username.jpg。我们首先右键点击gridview,在智能标记中,选择编辑列,添加一个模版列,然后编辑模版中的itemtemplate,加入一个image控件,然后右键点击image控件,选择edit databindings,在imageurl中设置field binding,首先我我要bound to 数据源中的某列,因为所有图片的路径和格式是相同的,唯有名字不同而已,所以我们这里选中username字段,在format中,我们要自己定义其格式,输入image/{0}.jpg, {0}代表的就是上面绑定的字段,下面有一个two way databinding 的复选框,就是是否双向绑定的意思,如果单向绑定,一般采用eval,也就是说数值只从数据源传到页面上,如果双向绑定,也就是采用bind的话,对数据的修改可以回传到数据源之中。
在web页面执行时,不同的行因为username不同,图片的名字也会做出相应的替换。点击确定,然后执行当前网页,我们就可以看到在我们的自定义列中显示出了用户的照片。
example 2:
在数据库中,存储性别的时候,一般采用bit数据类型,存储为true或者false,在gridview自动生成列的时候,一般使用checkedboxfield 列来显示bit类型的数据,显示在网页上就是一个单选框,如果选中,也就是checked了,就是男的,否则就是女的。这样看起来,很不直观,下面我们通过自定义列将性别显示为男、女。
首先在gridview的show smart tag中,选择编辑列,然后双击templatefields ,添加了一个模版列,确定后,点击右键选择编辑模版,选中刚添加的列。在itemtemplate中添加一个droplistdown控件,然后编辑它的数据绑定,edit databinding,把selectedvalue属性绑定到性别列。
在droplistdown控件中选择 edit item,就是编辑其下拉列表的项,我们添加两个item,一个的text属性是男,value设置为true,一个的text属性设置为女,value属性设置为false。到这里,你明白了么?因为dropdownlist控件的显示文本和其值是可以不一样的,我们用数据绑定取到了性别这一列的值,true或者false,然后反映到dropdownlist控件上,如果值为true,因为text属性为男的item的value为true,所以我们现在运行网页,在新添加的列中,显示的不再是单选框或者true、false这些没有含义的东西,而是以下拉列表的方式显示出了当前用户是 男还是女。
自定义列中的数据更新
假设数据库中有一个"权限"字段,值为0代表未审核用户,为1代表一般用户,为9则代表管理员用户。根据前面所说的自定义列的办法,通过对droplistdown的绑定,在网页中显示权限为 "管理员",而不是显示数字9。问题产生了,如果我们调整用户权限的话,比如把一般用户更改为管理员,在编辑模版中的用户权限的下拉列表,如何把它的值返回给数据源来完成更新操作。
我们在edititemtemplate中设置的droplistdown控件,必须选中了two way databinding,也就是数据双向帮定,这样才能返回数据。前面我们谈到,在gridview中,事件不是单个的,是两个,一个是发生前,一个是发生后,因为我们需要在数据更新前把下拉列表的权限值传送出去,所以我们需要对gridview1_rowupdating 进行编码,编码如下:
protected void gridview1_rowupdating(object sender, gridviewupdateeventargs e)
{
//当前编辑的是哪行?
int index = gridview1.editindex; //取得当前编辑行的gridviewrow对象
gridviewrow gvr = gridview1.rows[index]; //在当前行中,寻找droplistdown控件
dropdownlist dp = (dropdownlist)gvr.findcontrol("editdrop"); //将droplistdown的值赋给newvalues集合中的权限字段。
e.newvalues["rights"] = dp.selectedvalue;
}
rowdatabound事件
在创建gridview控件时,必须先为gridview的每一行创建一个gridviewrow对象,创建每一行时,将引发一个rowcreated事件;当行创建完毕,每一行gridviewrow就要绑定数据源中的数据,当绑定完成后,将引发rowdatabound事件。如果说我们可以利用rowcreated事件来控制每一行绑定的控件,那么我们同样可以利用rowdatabound事件来控制每一行绑定的数据,也就是让数据如何呈现给大家。
还举同样的例子,在数据表中,存在性别列,上面我们用droplistdown控件的databounding来表示出了中文的性别,但是毕竟不太美观,我们现在可以利用label控件和rowdatabound事件来实现完美的中文性别显示。rowdatabound,
首先,还是把性别列,设置为模板列,并添加一个label控件,将label控件绑定到数据源的性别段,然后我们在gridview控件属性的事件列表中双击rowdatabound,生成如下事件:
example:
protected void gridview1_rowdatabound(object sender, gridviewroweventargs e)
{
//判断当前行是否是数据行
if (e.row.rowtype == datacontrolrowtype.datarow)
{
//用findcontrol方法找到模板中的label控件
label lb1= (label)e.row.findcontrol("label1");
//因为rowdatabound是发生在数据绑定之后,所以我们可以
//判断label绑定的数据,如果是true,就更改其text属性为男
if (lb1.text== "true")
lb1.text = "男";
else
lb1.text = "female";
}
}
rowtype
rowtype可以确定gridview中行的类型,rowtype是玫举变量datacontrolrowtype中的一个值。rowtype可以取值包括 datarow、footer、header、emptydatarow、pager、separator。很多时候,我们需要判断当前是否是数据行,通过如下代码来进行判断 :
if (e.row.rowtype == datacontrolrowtype.datarow) rowdeleting和rowdeleted事件
rowdeleting发生在删除数据之前,rowdeleted发生在删除数据之后。
使用rowdeleting事件,可以在真正删除前再次确认是否删除,可以通过设置gridviewdeleteeventargs.cancel=true来取消删除;也可以用于判断当前数据库记录数,如果只剩一条记录且数据库不能为空则提示并取消删除操作。
使用rowdeleted事件,可以在删除后,通过gridviewdeletedeventargs的exception属性判断删除过程中是否产生异常,如无异常,则可以显示类似于” 1 records deleted” 之类的提示信息。
example:
protected void gridview1_rowdeleting(object sender, gridviewdeleteeventargs e)
{
//取得当前行号,并取得当前行的gridviewrow对象
int index=e.rowindex ;
gridviewrow gvr=gridview1.rows[index]; //取得当前行第二个单元格中的文字
str1 = gvr.cells[1].text; //进行提示
message.text ="您将删除一个用户,其姓名为"+str1 ;
}
protected void gridview1_rowdeleted(object sender, gridviewdeletedeventargs e)
{
//如果没有产生异常,则提示成功删除,否则提示删除失败
if (e.exception == null)
message.text += "< br>您成功删除了"+str1 ;
else
message.text += "删除失败,请联系管理员";
}
rowediting事件
在gridview中的行进入编辑模式之前,引发rowediting事件,如果您需要在编辑记录前进行某些预处理,可以在这里操作。如果想取消对当前行的编辑,可以把gridviewediteventargs 对象的 cancel 属性设置为 true即可。
example:
protected void gridview1_rowediting(object sender, gridviewediteventargs e)
{
//用neweidindex取得当前编辑的行号,然后获取gridviewrow对象
gridviewrow gvr = gridview1.rows[e.neweditindex];
//判断,如果当前编辑行姓名栏为admin用户,则取消对当前行的编辑
if (gvr.cells[1].text =="admin")
e.cancel = true;
}
rowupdating和rowupdated事件
rowupdating事件发生在更新数据源之前,rowupdated发生在更新数据源之后。
我们可以在记录更新前利用rowupdating做一些预处理工作,比如修改密码时,因为密码在数据库中不是明文存储,进行了hash,所以在更新密码前,应该生成其hash值,再进行更新操作。rowupdated则可以检验更新是否成功。
example:
protected void gridview1_rowupdating(object sender, gridviewupdateeventargs e)
{
gridviewrow gvr = gridview1.rows[gridview1 .editindex ]; //寻找输入密码的控件
textbox tb1 = (textbox)gvr.findcontrol("tb_password"); //将此控件中的文本hash后,把password存入newvalues这个字典中
e.newvalues ["password"] =tb1.text .gethashcode().tostring () ;
}
protected void gridview1_rowupdated(object sender, gridviewupdatedeventargs e)
{
//如无异常,则更新成功
if (e.exception == null)
message.text += "更新成功!";
}
keys、oldvalues、newvalues集合
keys字典中一般存放的是数据源中的主键字段的key和value的对应值,如果主键由多个字段组成,那么keys为每个键字段添加其字段名称和值。oldvalues中存放的是要更新的行的字段名和原始值,每个字段为其中的一项。newvalues中存放的是要更新的行的字段名和修改后的值,每个字段为其中的一项。注意,主键字段只存放于keys集合中。
这三个集合中的每一项都是dictionaryentry类型的对象,我们可以用dictionaryentry.key来确定一个项的字段名称,用dictionaryentry.value来确定某项的值。
在上面的例子中,为了把密码明文加密后再存入数据库,我们利用了newvalues字段,重新设置key为password的项的值。为了保证安全性,我们在更新数据前对newvalues中的所有值进行html编码:
example1:
protected void gridview1_rowupdating(object sender, gridviewupdateeventargs e)
{
//遍历newvalues,取得其中每一对dictionaryentry对象
foreach (dictionaryentry de in e.newvalues)
//de.key就是字段名,如果此处单独更新某字段的话,也可以直接填写字段名,
//比如 e.newvalues[“password”]
e.newvalues[de.key] = server.htmlencode(de.value.tostring());
}
example2:
protected void gridview1_rowupdating(object sender, gridviewupdateeventargs e)
{
//分别利用keys、oldvalues、newvalues取得主键名、原始数据和更新后数据
message .text = e.keys["username"] + "的email地址从" + e.oldvalues["email"] + "变更为" + e.newvalues["email"];
}
gridview控件操作方面的知识就介绍到这里。