GridView实战一:自定义分页、排序、修改、插入、删除

  1. 云栖社区>
  2. 博客>
  3. 正文

GridView实战一:自定义分页、排序、修改、插入、删除

肥仔john 2016-04-20 11:36:11 浏览2237
展开阅读全文

前言:

  在某次公司面试时被问到对GridView操作的熟悉程度,在那之前一直用Repeater内嵌table标签对GridView操作确实很 少,于是最近在项目的后台上对GridView进行了一番实操,本文和后面的另一篇GridView实战二:使用ObjectDataSource数据源 控件均是这段时间的一些总结。

  GridView优点就是集数据绑定、分页、排序、删、改于一身,提高了开发效率;缺点嘛,就是运行效率低,并且它本身不带添加功能。于是GridView用于后台开发是一个不错的选择,而前台页面还是少用为妙咯。

 

实战一:

  1.本次实战效果图如下:

图一.展示状态

图二.编辑状态

图三.添加记录状态

2.具体代码

.aspx页面

<asp:GridView runat="server" ID="gv" AutoGenerateColumns="false"
             AllowPaging="true" PageSize="1" AllowSorting="true" DataKeyNames="ID"
             OnRowDataBound="gv_OnRowDataBound" OnRowDeleting="gv_OnRowDeleting"
             OnRowEditing="gv_OnRowEditing" OnRowCancelingEdit="gv_OnRowCancelingEdit"
             OnRowUpdating="gv_OnRowUpdating" OnSorting="gv_OnSorting">
             <HeaderStyle BackColor="graytext" />
            <Columns>
                <asp:TemplateField>
                    <HeaderStyle Width="20%" />
                    <HeaderTemplate>
                        <asp:LinkButton runat="server" ID="lbtnSortName" Text="Name" CommandName="Sort" CommandArgument="Name">
                        </asp:LinkButton>
                    </HeaderTemplate>
                    <ItemTemplate><%#Eval("Name") %></ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox runat="server" ID="tbxName" Text='<%#Eval("Name") %>'></asp:TextBox> 
                        <asp:RegularExpressionValidator runat="server" ID="revName" ControlToValidate="tbxName"
                             ValidationExpression="[a-zA-Z]+" ErrorMessage="Please input your English name!" Display="Dynamic">
                        </asp:RegularExpressionValidator>
                        <asp:RequiredFieldValidator runat="server" ID="rfvName" ControlToValidate="tbxName"
                            ErrorMessage="Please input your name" Display="Dynamic">
                        </asp:RequiredFieldValidator> 
                    </EditItemTemplate>
                </asp:TemplateField> 
                <asp:TemplateField>
                    <HeaderStyle Width="10%"/>
                    <HeaderTemplate>
                        <asp:LinkButton runat="server" ID="lbtnSortSex" Text="Sex" CommandName="Sort" CommandArgument="Sex">
                        </asp:LinkButton> 
                    </HeaderTemplate> 
                    <ItemTemplate>
                        <asp:RadioButtonList Enabled="false" runat="server" ID="rblSexShow" RepeatDirection="Horizontal" 
                            RepeatColumns="2">
                        </asp:RadioButtonList>
                    </ItemTemplate> 
                    <EditItemTemplate>
                         <asp:RadioButtonList runat="server" ID="rblSexEdit" RepeatDirection="Horizontal" RepeatColumns="2">
                         </asp:RadioButtonList>
                    </EditItemTemplate> 
                </asp:TemplateField>
                <asp:TemplateField>
                    <HeaderStyle Width="20%"/>
                    <HeaderTemplate>
                        <asp:LinkButton runat="server" ID="lbtnSortCountry" Text="Country" CommandName="Sort" CommandArgument="Country">
                        </asp:LinkButton>
                    </HeaderTemplate> 
                    <ItemTemplate><%#Eval("Country")%></ItemTemplate> 
                    <EditItemTemplate>
                        <asp:DropDownList runat="server" ID="ddlCountry"></asp:DropDownList>
                    </EditItemTemplate> 
                </asp:TemplateField>
                <asp:TemplateField>
                    <HeaderStyle Width="20%"/>
                    <HeaderTemplate>Hobby</HeaderTemplate>
                    <ItemTemplate><%#Eval("Hobby") %></ItemTemplate> 
                    <EditItemTemplate>
                        <asp:CheckBoxList runat="server" ID="cbxlHobby" RepeatDirection="Horizontal" RepeatColumns="5">
                        </asp:CheckBoxList>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField ItemStyle-HorizontalAlign="Center">
                    <HeaderStyle Width="10%"/>
                    <HeaderTemplate>
                        <asp:LinkButton runat="server" ID="lbtnShowAdd" Text="Add" OnClick="lbtnShowAdd_OnClick">
                        </asp:LinkButton>
                    </HeaderTemplate>
                    <ItemTemplate>
                        <asp:LinkButton runat="server" ID="lbtnEdit" Text="Edit" CommandName="Edit" /> 
                        <asp:LinkButton runat="server" ID="lbtnDelete" Text="Delete" CommandName="Delete" />
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:LinkButton runat="server" ID="lbtnSubmit" Text="Update" CommandName="Update" /> 
                        <asp:LinkButton runat="server" ID="lbtnCancel" Text="Cancel" CommandName="Cancel" />
                    </EditItemTemplate> 
                </asp:TemplateField>
            </Columns>
            <PagerSettings Visible="true" />
            <PagerStyle Font-Size="12px"/>
            <PagerTemplate>
                <div style="float:left;margin-left:15px;color:#999;line-height:20px">
                    当前第<%#this.gv.PageIndex+1 %>/<%#this.gv.PageCount %>页
                </div>
                <div style="float:right;margin-right:15px;color:#999;line-height:20px">页</div>
                <div style="float:right">
                    <asp:DropDownList runat="server" ID="ddlPaging" AutoPostBack="true"
                        OnSelectedIndexChanged="ddlPaging_OnSelectedIndexChanged">
                    </asp:DropDownList>
                </div>
                <div style="float:right;color:#999;line-height:20px">跳转到第</div>
            </PagerTemplate>
            <EmptyDataTemplate>
                <div><b>Name:</b>
                    <asp:TextBox runat="server" ID="tbxName"></asp:TextBox>
                    <asp:RegularExpressionValidator runat="server" ID="revName" ControlToValidate="tbxName"
                         ValidationExpression="^[a-zA-Z]+$" Display="Dynamic" ErrorMessage="Please input your English name!">
                    </asp:RegularExpressionValidator>
                   <asp:RequiredFieldValidator runat="server" ID="rfvName" ControlToValidate="tbxName"
                        ErrorMessage="Please input your name" Display="Dynamic">
                    </asp:RequiredFieldValidator> 
                </div>
                <div><b>Sex:</b>
                    <asp:RadioButtonList runat="server" id="rblSex" RepeatColumns="2" RepeatDirection="Horizontal">
                        <asp:ListItem Value="M" Text="Male" Selected="True"></asp:ListItem>
                        <asp:ListItem Value="F" Text="Female"></asp:ListItem>
                    </asp:RadioButtonList>
                </div>
                <div><b>Country:</b>
                    <asp:DropDownList runat="server" ID="ddlCountry">
                    </asp:DropDownList>
                    <asp:RequiredFieldValidator runat="server" ID="rfCountry" ControlToValidate="ddlCountry"
                        ErrorMessage="Please Choose Country" Display="Dynamic">
                    </asp:RequiredFieldValidator>
                </div>
                <div><b>Hobby:</b>
                    <asp:CheckBoxList runat="server" ID="cbxlHobby" RepeatColumns="5" RepeatDirection="Horizontal">
                    </asp:CheckBoxList>
                </div>
                <div>
                    <asp:LinkButton runat="server" ID="lbtnSubmit" Text="Submit" OnClick="lbtnSubmit_OnClick" />
                    <asp:LinkButton  runat="server" ID="lbtnCancel" Text="Cancel" OnClick="lbtnCancel_OnClick"
                        CausesValidation="false"/>
                </div>
            </EmptyDataTemplate> 
        </asp:GridView>

说明:

1.显示状态时:对于只显示文字串的用<%#Eval("字段名")%>直接绑定,而对于单选组(性别)的内容就放在GridView的OnRowDataBound来绑定。

2.编辑状态时:复选组、单选组合下拉列表都在OnRowDataBound来绑定;这里没有用数据源控件,所以用<%#Bind("字段 名")%>和<%#Eval("字段名")%>没区别,<%#Bind("字段名")%>的双向通讯不起作用。

3.新增状态:因为GridView自身附带新增记录的功能,所以选择在EmptyDataTemplate中实现新增的功能(借鉴其他同行的做法!);因为使用了验证控件,所以把取消按钮(操作不验证合法性)设为不触发验证。

4.分页功能:本例是将分页功能放置到gridview的PagerTemplate中实现。这里有两个注意点:

a.DropDownList设置AutoPostBack为true;

b.因为要触发DropDownList的OnSelectedIndexChanged事件,所以viewstate要启用。触发 OnSelectedIndexChanged事件的条件是postback的selectedIndex和原始值不同,当viewstate启用时原始 值就是viewstate中保存的值,当viewstate禁用时就是控件初始化时的selectedIndex或第一个选项。因此如果禁用了 ViewState那么当选择回控件初始化时的选项时就不会触发OnSelectedIndexChanged事件了。

c.GridView的OnRowDataBound中每次postback都重新初始化DropDownList。因为DropDownList 包含在GridView中是动态生成的,当PostBack时GridView并不会恢复其中的动态内容;如果把分页功能放在GridView以外实现, 那么动态生成的时DropDownList的选项,就Postback时不用再初始化了。

d.因为DropDownList位于GridView里面,所以当把某个ListItem的enable设为false时,该选项就不生成(连 Html代码都没了),如果放在GridView外只是显示为不可用而已,原因不明。为实现添加状态初始画面中存在默认不可选的listitem效果,用 了html的disabled属性来设置。

 

.aspx.cs代码

public partial class _Default : System.Web.UI.Page 
{
    private DataManager dm = new DataManager();

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            this.gv.DataSource = dm.GetDt();
            this.gv.DataBind();

            //记录最近一次排序方向
            ViewState["Direction"] = SortDirection.Descending;
        }
    }

    protected void gv_OnRowDataBound(object sender, GridViewRowEventArgs e)
    {
        DataRowView drv = e.Row.DataItem as DataRowView;

        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            //显示时
            if (this.gv.EditIndex == -1)
            {
                //设置性别
                RadioButtonList rbl = e.Row.FindControl("rblSexShow") as RadioButtonList;
                rbl.Items.Add(new ListItem("Male", "M"));
                rbl.Items.Add(new ListItem("Female", "F"));
                if ((drv["Sex"] as string).ToLower().Equals("m"))
                    rbl.Items[0].Selected = true;
                else
                    rbl.Items[1].Selected = true;
            } 
            //修改时:
            else if(e.Row.RowIndex == this.gv.EditIndex)
            {
                //性别
                RadioButtonList rbl = e.Row.FindControl("rblSexEdit") as RadioButtonList;
                rbl.Items.Add(new ListItem("Male", "M"));
                rbl.Items.Add(new ListItem("Female", "F"));
                if ((drv["Sex"] as string).ToLower().Equals("m"))
                    rbl.Items[0].Selected = true;
                else
                    rbl.Items[1].Selected = true;
                //国籍
                DropDownList ddlCountry = e.Row.FindControl("ddlCountry") as DropDownList;
                DataTable countryDt = dm.GetCountry();
                ListItem li = null;
                for(int i=0;i<countryDt.Rows.Count;++i)
                {
                    string cn = countryDt.Rows[i]["cn"] as string;
                    li = new ListItem(cn, cn);
                    if (cn.Equals(drv["Country"] as string))
                        li.Selected = true;
                    ddlCountry.Items.Add(li);
                }
                //兴趣
                CheckBoxList cbl = e.Row.FindControl("cbxlHobby") as CheckBoxList;
                DataTable hobbyDt = dm.GetHobby();
                string hobbys = drv["Hobby"] as string;
                ListItem hobbyLi = null;
                string hstr = string.Empty;
                for (int i = 0; i < hobbyDt.Rows.Count; i++)
                {
                    hstr = hobbyDt.Rows[i]["hobby"] as string;
                    hobbyLi = new ListItem(hstr, hstr);
                    if (hobbys.IndexOf(hstr)>=0)
                        hobbyLi.Selected = true;
                    cbl.Items.Add(hobbyLi);
                }
            }
        }
        else if (e.Row.RowType == DataControlRowType.Pager)
        {
            //绑定分页控件
            DropDownList ddlPaging = e.Row.FindControl("ddlPaging") as DropDownList;
            for (int i = 0; i < this.gv.PageCount; i++)
            {
                ddlPaging.Items.Add(new ListItem(Convert.ToString(i + 1), Convert.ToString(i)));
            }
            ddlPaging.SelectedIndex = this.gv.PageIndex;
        }
        else if (e.Row.RowType == DataControlRowType.EmptyDataRow)
        {
            //添加记录画面
//国籍
            DropDownList ddlCountry = e.Row.FindControl("ddlCountry") as DropDownList;
            DataTable countryDt = dm.GetCountry();
            ListItem li =  new ListItem("Please Select","");
            li.Attributes["disabled"] = "disabled";
            li.Selected = true;
            ddlCountry.Items.Add(li);
            for (int i = 0; i < countryDt.Rows.Count; ++i)
            {
                string cn = countryDt.Rows[i]["cn"] as string;
                li = new ListItem(cn, cn);
                ddlCountry.Items.Add(li);
            }
            //兴趣
            CheckBoxList cbl = e.Row.FindControl("cbxlHobby") as CheckBoxList;
            DataTable hobbyDt = dm.GetHobby();
            ListItem hobbyLi = null;
            string hstr = string.Empty;
            for (int i = 0; i < hobbyDt.Rows.Count; i++)
            {
                hstr = hobbyDt.Rows[i]["hobby"] as string;
                hobbyLi = new ListItem(hstr, hstr);
                cbl.Items.Add(hobbyLi);
            }
        }
    }

    /// <summary>
/// 分页控件的OnSelectedIndexChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void ddlPaging_OnSelectedIndexChanged(object sender, EventArgs e)
    {
        this.gv.PageIndex = (sender as DropDownList).SelectedIndex;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 删除记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void gv_OnRowDeleting(object sender, GridViewDeleteEventArgs e)
    {
        dm.DelRecord(Convert.ToInt32(this.gv.DataKeys[e.RowIndex].Value));
        this.gv.PageIndex = 0;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 修改记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void gv_OnRowEditing(object sender, GridViewEditEventArgs e)
    {
        this.gv.EditIndex = e.NewEditIndex;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 取消修改
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void gv_OnRowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    {
        this.gv.EditIndex = -1;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 更新记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void gv_OnRowUpdating(object sender, GridViewUpdateEventArgs e)
    {
        GridViewRow gvr = this.gv.Rows[e.RowIndex];
        int id = Convert.ToInt32(this.gv.DataKeys[e.RowIndex].Value);
        string name = (gvr.Cells[0].FindControl("tbxName") as TextBox).Text;
        string sex = (gvr.Cells[1].FindControl("rblSexEdit") as RadioButtonList).SelectedValue;
        string country = (gvr.Cells[2].FindControl("ddlCountry") as DropDownList).SelectedValue;
        System.Text.StringBuilder hobbys = new System.Text.StringBuilder();
        foreach (ListItem li in (gvr.Cells[3].FindControl("cbxlHobby") as CheckBoxList).Items)
        {
            if (li.Selected)
                hobbys.Append(li.Value+",");
        }
        if (hobbys.Length >= 2)
            hobbys.Remove(hobbys.Length - 1, 1);
        dm.UpdateRecord(id, name, country, sex, hobbys.ToString());

        this.gv.EditIndex = -1;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 提交新记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void lbtnSubmit_OnClick(object sender, EventArgs e)
    {
        LinkButton lbtnSubmit = sender as LinkButton;
        GridViewRow tr = lbtnSubmit.NamingContainer as GridViewRow;
        string name = (tr.Cells[0].FindControl("tbxName") as TextBox).Text;
        string sex = (tr.Cells[0].FindControl("rblSex") as RadioButtonList).SelectedValue;
        string country = (tr.Cells[0].FindControl("ddlCountry") as DropDownList).SelectedValue;
        System.Text.StringBuilder hobbys = new System.Text.StringBuilder();
        foreach (ListItem li in (tr.Cells[0].FindControl("cbxlHobby") as CheckBoxList).Items)
        {
            if (li.Selected)
                hobbys.Append(li.Value + ",");
        }
        if (hobbys.Length >= 2)
            hobbys.Remove(hobbys.Length - 1, 1);
        dm.AddRecord(name,country,sex,hobbys.ToString());

        this.gv.PageIndex = 0;
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 退出添加新记录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void lbtnCancel_OnClick(object sender, EventArgs e)
    {
        this.gv.DataSource = dm.GetDt();
        this.gv.DataBind();
    }

    /// <summary>
/// 显示添加画面
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void lbtnShowAdd_OnClick(object sender, EventArgs e)
    {
        this.gv.DataSource = null;
        this.gv.DataBind();
    }

    /// <summary>
/// 排序
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
    protected void gv_OnSorting(object sender, GridViewSortEventArgs e)
    {
        DataTable dt = dm.GetDt();
        string direction = string.Empty;
        if (e.SortDirection == (SortDirection)Enum.Parse(typeof(SortDirection),Convert.ToString(ViewState["Direction"])))
            direction = "asc";
        else
            direction = "desc";
        dt.DefaultView.Sort = string.Format("{0} {1}", e.SortExpression, direction);
        ViewState["Direction"] = (direction.Equals("asc") ? SortDirection.Descending : SortDirection.Ascending);
        this.gv.DataSource = dt;
        this.gv.DataBind();
    }
}

说明:

1.显示、编辑、新增时数据绑定都集中在gv_OnRowDataBound方法中,通过e.Row.RowType来分别处理。

2.排序:通过DataTable.DefaultView来实现,用一个ViewState变量存放最近一次的排序方向的反方向,来使每次排序的方向都不同。GridView的SortDirection为只读属性,所以别指望设定它来实现排序哦!

3.modify、update、delete和cancel按钮的实现利用GridView预设的CommandName来处理

 

CommandName 值

说明

“Cancel”

取消编辑操作并将 GridView 控件返回为只读模式。引发 RowCancelingEdit 事件。

“Delete”

删除当前记录。引发 RowDeleting 和 RowDeleted 事件。

“Edit”

将当前记录置于编辑模式。引发 RowEditing 事件。

“Page”

执行分页操作。将按钮的 CommandArgument 属性设置为“First”、“Last”、“Next”、“Prev”或页码,以指定要执行的分页操作类型。引发 PageIndexChanging 和 PageIndexChanged 事件。

“Select”

选择当前记录。引发 SelectedIndexChanging 和 SelectedIndexChanged 事件。

“Sort”

对 GridView 控件进行排序。引发 Sorting 和 Sorted 事件。

“Update”

更新数据源中的当前记录。引发 RowUpdating 和 RowUpdated 事件。

 

当然也可以自己写OnCommand的处理代码来处理,就是烦一点。

 

数据操作类:


public class DataManager
{
    private static DataTable dt = null;//用户记录
    private static DataTable countryDt = null;//国籍
    private static DataTable hobbyDt = null;//兴趣

    public DataManager()
    {
        if (dt == null)
        {
            dt = new DataTable();
            dt.Columns.Add("ID");
            dt.Columns.Add("Name");
            dt.Columns.Add("Sex");
            dt.Columns.Add("Country");
            dt.Columns.Add("Hobby");

            //Default Data
            dt.Rows.Add(new object[] { 1,"Mary","F","China","Cooking,Music"});
            dt.Rows.Add(new object[] { 2, "John", "M", "China", "Tennis" });
        }

        if (countryDt == null)
        {
            countryDt=new DataTable();
            countryDt.Columns.Add("cn");
            
            //Default Data
            countryDt.Rows.Add(new object[] { "China" });
            countryDt.Rows.Add(new object[] { "French" });
            countryDt.Rows.Add(new object[] { "America" });
            countryDt.Rows.Add(new object[] { "Afria" });
            countryDt.Rows.Add(new object[] { "Japan" });
        }

        if (hobbyDt == null)
        {
            hobbyDt = new DataTable();
            hobbyDt.Columns.Add("hobby");

            //Default Data
            hobbyDt.Rows.Add(new object[] { "Cooking" });
            hobbyDt.Rows.Add(new object[] { "Music" });
            hobbyDt.Rows.Add(new object[] { "Reading" });
            hobbyDt.Rows.Add(new object[] { "Movies" });
            hobbyDt.Rows.Add(new object[] { "Tennis" });
        }
    }

    public DataTable GetDt()
    {
        return dt;
    }

    public bool DelRecord(int id)
    {
        DataRow[] drs = dt.Select("ID=" + id);
        bool result = false;
        if (drs.Length == 1)
        {
            dt.Rows.Remove(drs[0]);
            result = true;
        }

        return result;
    }

    public bool UpdateRecord(int id, string name, string country, string sex, string hobby)
    {
        DataRow[] drs = dt.Select("ID=" + id);
        bool result = false;
        if (drs.Length == 1)
        {
            drs[0]["Name"] = name;
            drs[0]["Country"] = country;
            drs[0]["Hobby"] = hobby;
            drs[0]["Sex"] = sex;
            result = true;
        }

        return result;
    }

    public bool AddRecord(string name, string country, string sex, string hobby)
    {
        DataRow[] drs = dt.Select();
        Array.Sort<DataRow>(drs, new Comparison<DataRow>(SortHandler));
        int newId = 1+Convert.ToInt32(drs[0]["ID"]);
        dt.Rows.Add(new object[] {newId,name,sex,country,hobby });

        return true;
    }

    private int SortHandler(DataRow arg1, DataRow arg2)
    {
        if (Convert.ToInt32(arg1["ID"]) == Convert.ToInt32(arg2["ID"]))
            return 0;
        else if (Convert.ToInt32(arg1["ID"]) < Convert.ToInt32(arg2["ID"]))
            return 1;
        else
            return -1;
    }

    public DataTable GetCountry()
    {
        return countryDt;
    }

    public DataTable GetHobby()
    {
        return hobbyDt;
    }
}

这里没什么好说的,就是AddRecord方法中用到了Array.Sort方法,这个方法我用得很少,不过觉得挺好用的,定义一个方法作为第二个参数传入就可以了。

 

总结:

  不适用数据源控件的优点是,新增记录时可以马上看到新增的记录;缺点是即使实现了分页功能,但每次还是从数据库中读取全部数据,效率低。而使用 ObejctDataSource来绑定就可以解决从数据库读取全部数据的问题,并且附带数据缓存(应用程序级缓存)的功能,省心多了。但却又因 ObjectDataSource不支持重新绑定,而使新增记录时不能马上看到新增的记录的缺点。

  关于ObjectDataSource配合GridView的使用将在《GridView实战二:使用ObjectDataSource数据源控件》讲述。

网友评论

登录后评论
0/500
评论
肥仔john
+ 关注