(原创)代码分析-DataGrid实现增删(带提示)改和分页
或许大家会说,网上已经很多类似文章了,包括孟子的,为什么要再写一次?
我想我们不仅仅要会实现,更多的是需要理解。
下面先帖出代码,再分析一下其中的一些关键代码。
数据库表名:tb1,其中有3个字段,分别是ID自增的主键、vName varchar(50)、iAge int
(以下代码没有做任何错误捕获处理)
前台
<%@ Page language="c#" Codebehind="WebForm5.aspx.cs" AutoEventWireup="false" Inherits="csdn.WebForm5" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm5</title>
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
<meta content="C#" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<asp:textbox id="TextBox1" runat="server" Width="88px"></asp:textbox>
<asp:TextBox id="TextBox2" runat="server" Width="40px"></asp:TextBox>
<asp:Button id="Button1" runat="server" Text="添加"></asp:Button>
<asp:datagrid id="DataGrid1" runat="server" AutoGenerateColumns="False" DataKeyField="ID" AllowPaging="True"
PageSize="5" OnEditCommand="edit" OnCancelCommand="cancel" OnUpdateCommand="update">
<Columns>
<asp:TemplateColumn HeaderText="姓名">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"vName") %>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox id="name" Runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"vName") %>' Width="88px">
</asp:TextBox>
</EditItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="年龄">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"iAge") %>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox id="age" Runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"iAge") %>' Width="40px">
</asp:TextBox>
</EditItemTemplate>
</asp:TemplateColumn>
<asp:EditCommandColumn UpdateText="更新" CancelText="取消" EditText="编辑"></asp:EditCommandColumn>
<asp:ButtonColumn Text="删除" CommandName="del"></asp:ButtonColumn>
</Columns>
<PagerStyle Mode="NumericPages"></PagerStyle>
</asp:datagrid></form>
</body>
</HTML>
后台
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace csdn
{
/// <summary>
/// WebForm5 的摘要说明。
/// </summary>
public class WebForm5 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.TextBox TextBox1;
protected System.Web.UI.WebControls.TextBox TextBox2;
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.WebControls.DataGrid DataGrid1;
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
if(!IsPostBack)
{
SetBind();
}
}
protected void SetBind()
{
SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
SqlDataAdapter da=new SqlDataAdapter("select * from tb1",conn);
DataSet ds=new DataSet();
da.Fill(ds,"table1");
this.DataGrid1.DataSource=ds.Tables["table1"];
this.DataGrid1.DataBind();
}
Web 窗体设计器生成的代码
private void Button1_Click(object sender, System.EventArgs e)
{
SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
SqlCommand comm=new SqlCommand("insert into tb1 (vName,iAge) values (@vName,@iAge)",conn);
SqlParameter parm1=new SqlParameter("@vName",SqlDbType.NVarChar,50);
parm1.Value=this.TextBox1.Text;
SqlParameter parm2=new SqlParameter("@iAge",SqlDbType.Int);
parm2.Value=this.TextBox2.Text;
comm.Parameters.Add(parm1);
comm.Parameters.Add(parm2);
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
SetBind();
}
private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
if(e.CommandName=="del")
{
SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
SqlCommand comm=new SqlCommand("delete from tb1 where ID=@id",conn);
SqlParameter parm1=new SqlParameter("@id",SqlDbType.Int);
parm1.Value=this.DataGrid1.DataKeys[e.Item.ItemIndex];
comm.Parameters.Add(parm1);
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
SetBind();
}
}
private void DataGrid1_PageIndexChanged(object source, System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
this.DataGrid1.CurrentPageIndex=e.NewPageIndex;
SetBind();
}
protected void edit(object sender,DataGridCommandEventArgs e)
{
this.DataGrid1.EditItemIndex=e.Item.ItemIndex;
SetBind();
}
protected void cancel(object sender,DataGridCommandEventArgs e)
{
this.DataGrid1.EditItemIndex=-1;
SetBind();
}
protected void update(object sender,DataGridCommandEventArgs e)
{
SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);
SqlCommand comm=new SqlCommand("update tb1 set vName=@vName,iAge=@iAge where ID=@id",conn);
SqlParameter parm1=new SqlParameter("@vName",SqlDbType.NVarChar,50);
parm1.Value=((TextBox)e.Item.FindControl("name")).Text;
SqlParameter parm2=new SqlParameter("@iAge",SqlDbType.Int);
parm2.Value=((TextBox)e.Item.FindControl("age")).Text;
SqlParameter parm3=new SqlParameter("@id",SqlDbType.Int);
parm3.Value=this.DataGrid1.DataKeys[e.Item.ItemIndex];
comm.Parameters.Add(parm1);
comm.Parameters.Add(parm2);
comm.Parameters.Add(parm3);
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
this.DataGrid1.EditItemIndex=-1;
SetBind();
}
private void DataGrid1_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
foreach(DataGridItem di in this.DataGrid1.Items)
{
if(di.ItemType==ListItemType.Item||di.ItemType==ListItemType.AlternatingItem||di.ItemType==ListItemType.EditItem)
{
((LinkButton)di.Cells[3].Controls[0]).Attributes.Add("onclick","return confirm('delete?');");
}
}
//一般不这么写,看下面说明第六点
}
}
(1)
大家可以看到SetBind()中的一些代码,并没有显式的打开和关闭SqlConnection但是代码确实是运作的,原因在于SqlDataAdqpter的Fill方法会自动打开或者关闭连接(当然它会先检测是不是连接已经打开,如果已经打开的话不会重复打开导致出错)。
(2)
再看一下Page_Load()中
if(!IsPostBack)
{
SetBind();
}
很多初学者,不理解IsPostBack盲目的加或者不加这句话,其实加不加在于你的需求
IsPostBack为真的时候表示网页不是第一次加载也就是回传的情况
只有在!IsPostBack为真的时候用了SetBind();也就是绑定DataGrid的关键在于我们需要修改记录,这往往是很多网友在修改记录的时候发现向数据库写入的数据还是原来的TextBox中的内容。为什么会这样呢?在修改的时候我们先按下“编辑”这个按钮执行了protected void edit(object sender,DataGridCommandEventArgs e)里面的代码,在修改了TextBox中数据以后我们按下“更新”这个按钮,这个时候也是一次PostBack如果没有if(!IsPostBack)的话,其中的绑定代码执行了一次,也就是对模板列中的TextBox进行了重新赋值,然后转到protected void update(object sender,DataGridCommandEventArgs e)中的代码,你的代码是执行的,但是((TextBox)e.Item.FindControl("name")).Text已经不是你修改的东西而是原来的东西了。
(3)
修改的代码中parm3.Value=this.DataGrid1.DataKeys[e.Item.ItemIndex]
对于e.Item.ItemIndex就是点击的这个行(事件触发的行)的行好,如果要用这个作为主键来放进where子句update或者delete显然是不合适的,比如你点击的是第三行,数据库内这个记录的主键却是20或者是字符,因此我们需要为DataGrid指定一个DataKeyField(前台代码中)告诉它,我的表的主键就是这个字段,在绑定了以后DataGrid的DataKeys就存储数据了列表控件中每个记录的主键,所以取的时候用索引this.DataGrid1.DataKeys[e.Item.ItemIndex]来取就可以得到主键
(4)
看一下private void DataGrid1_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
中的一些代码,首先ItemCreated这个事件是在当在DataGrid控件中创建项时在服务器上发生,这个时候还没有绑定数据,所以如果在这个ItemCreated中去取DataGrid中的值你是得不到的。
foreach(DataGridItem di in this.DataGrid1.Items)
{
if(di.ItemType==ListItemType.Item||di.ItemType==ListItemType.AlternatingItem||di.ItemType==ListItemType.EditItem)
{
((LinkButton)di.Cells[3].Controls[0]).Attributes.Add("onclick","return confirm('delete?');");//这里的类型转换为LinkButton,在不指定ButtonColumn 的ButtonType的时候默认就是LinkButton,如果指定为PushButton,这里类型转换应该写Button而不是LinkButton否则会出错
}
}
这段代码为所有的删除按钮增加客户端的click事件,在循环所有的DataGrid1.Items的时候我们注意到,仅仅需要在ListItemType.Item和ListItemType.AlternatingItem中的第四列(Cells[3])中的第一个按钮控件(Controls[0])上添加属性,因为在DataGrid的Header或者Footer中是找不到这个删除按钮的(虽然他们也是DataGrid1.Items)。设想一下如果不写di.ItemType==ListItemType.EditItem会怎么样?结果就是在编辑的时候按删除那个时候是没有提示的,因为没有给编辑的行的按钮加脚本。说到这里想说一下对这个程序的改进,大家或许不想在编辑的时候允许删除操作(也就是点击了编辑按钮却没有点击取消或者更新按钮的时候是不运行删除操作),改进方法很简单:把e.CommandName=="del"这个判断改为e.CommandName=="del"&&e.Item.ItemType!=ListItemType.EditItem,就可以达到这个目的了。
还有之所以在这里没有用((LinkButton)di.Cells[3].FindControl(删除按钮的id))来找到这个按钮是因为这个按钮如果不写是 <asp:ButtonColumn Text="删除" CommandName="del"></asp:ButtonColumn>不能添加id的,如果这个按钮是放在模板列中的,就可以有id了。
(5)
在编辑的时候this.DataGrid1.EditItemIndex=e.Item.ItemIndex在取消编辑的时候this.DataGrid1.EditItemIndex=-1
当EditItemIndex!=-1的时候,DataGrid显示的东西不再是ItemTemplate中的东西而是EditItemTemplate中的东西,所以在取消编辑的时候设定-1就可以了,同样EditItemIndex和ItemIndex差不多是从0开始编号,表示的是编辑的这一行的索引号。
(6)
foreach(DataGridItem di in this.DataGrid1.Items)
{
if(di.ItemType==ListItemType.Item||di.ItemType==ListItemType.AlternatingItem||di.ItemType==ListItemType.EditItem)
{
((LinkButton)di.Cells[3].Controls[0]).Attributes.Add("onclick","return confirm('delete?');");
}
}
}
这段代码我为了演示如果不对ItemType进行判断会出错,所以对DataGrid1.Items进行了循环读取(可以通过调试看到当在设置了Footer以后DataGrid1.Items.Count>PageSize),一般情况下这么写就可以了
if(e.Item.ItemType==ListItemType.Item||e.Item.ItemType==ListItemType.AlternatingItem||e.Item.ItemType==ListItemType.EditItem)
{
((LinkButton)e.Item.Cells[3].Controls[0]).Attributes.Add("onclick","return confirm('delete?');");
}
暂时先说到这里,以后继续补充。