asp.net夜话之八:数据绑定控件(上)
        通过前面的例子我们看到每次我们要显示数据的时候都要通过一个循环来显示满足条件的数据,这是一个比较麻烦的过程,为此微软定义了一系列的控件专门用于显示数据的格式,通过这些控件可以以可视化的方式查看绑定数据之后的效果。这些控件称之为数据绑定控件。在asp.net中所有的数据库绑定控件都是从BaseDataBoundControl这个抽象类派生的,这个抽象类定义了几个重要属性和一个重要方法:
        DataSource属性:指定数据绑定控件的数据来源,显示的时候程序将会从这个数据源中获取数据并显示。
        DataSourceID属性:指定数据绑定控件的数据源控件的ID, 显示的时候程序将会根据这个ID找到相应的数据源控件,并利用这个数据源控件中指定方法获取数据并显示。
        DataBind()方法:当指定了数据绑定控件的DataSource属性或者DataSourceID属性之后,再调用DataBind()方法才会显示绑定的数据。并且在使用数据源时,会首先尝试使用DataSourceID属性标识的数据源,如果没有设置DataSourceID时才会用到DataSource属性标识的数据源。也就是说DataSource和DataSourceID两个属性不能同时使用。
        数据绑定控件的DataSource控件属性必须是一个可以枚举的数据源,如实现了ICollection、IEnumerable或IListSource接口的类的实例。
        今天要讲述的内容有:
DropDownList控件
ListBox控件
GridView控件
DataList控件
Repeater控件
FormView控件
DetailsView控件
 
DropDownList控件
        DropDownList控件是一个相对比较简单的数据绑定控件,它在客户端被解释成<select></select>这样的HTML标记,也就是只能有一个选项处于选中状态。
DropDownList控件常见属性:
        AutoPostBack属性:这个属性的用法在讲述基本控件的时候已经讲过,是用来设置当下拉列表项发生变化时是否主动向服务器提交整个表单,默认是false,即不主动提交。如果设置为true,就可以编写它的SelectedIndexChanged事件处理代码进行相关处理(注意:如果此属性为false即使编写了SelectedIndexChanged事件处理代码也不会马上起作用)。
DataTextField属性:设置列表项的可见部分的文字。
DataValueField属性:设置列表项的值部分。
Items属性:获取控件的列表项的集合。
SelectedIndex属性:获取或设置 DropDownList 控件中的选定项的索引。
SelectedItem属性:获取列表控件中索引最小的选定项。
SelectedValue属性:取列表控件中选定项的值,或选择列表控件中包含指定值的项。
        因为在实际开发中,用户希望直观地看见选中哪个选项,而在操作数据库的时候我们更希望直接以该值对应的编号来操作,利用DataTextField属性和DataValueField属性就可以很方便地做到这一点,这两个属性通常是数据源中的某个字段名(如果DataSource属性是DataTable或者是DataView的话)或者范型集合中实体的属性(如果DataSource属性是System.Collections.Generic.List<T>的话,则可以指定为T的属性)。
        下面是DropDownList控件的例子,依然用到的是第六章中创建的表(本代码采用了单页模式):
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
                if (!Page.IsPostBack)
                {
                        BindMonthList();
                        BindUserList();
                }
        }
        private void BindMonthList()
        {
                //因为所有的数组都是Array类的派生类
                //而Array类实现了IEnumerable和ICollection这两个接口,所以可以被当作数据源
                int[] monthList = new int[12];
                for (int i = 0; i <= 11; i++)
                {
                        monthList[i] = i + 1;
                }
                ddlMonthList.DataSource = monthList;
                ddlMonthList.DataBind();//注意不能缺少这一句,否则下拉列表中没有数据
        }
        private void BindUserList()
        {
                //实例化Connection对象
                SqlConnection connection = new SqlConnection("Data Source=(local);Initial Catalog=AspNetStudy;Persist Security Info=True;User ID=sa;Password=sa");
                //实例化Command对象
                SqlCommand command = new SqlCommand("select UserID,RealName from UserInfo", connection);
                SqlDataAdapter adapter = new SqlDataAdapter(command);
                DataTable data = new DataTable();
                adapter.Fill(data);
                ddlUserList.DataTextField = "RealName";//指定下拉列表中的文字显示部分
                ddlUserList.DataValueField = "UserID";//指定下拉列表中的值部分
                ddlUserList.DataSource = data;
                ddlUserList.DataBind();
        }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
        <title>DropDownList控件的例子</title>
</head>
<body>
        <form id="form1" runat="server">
        <div>
                <asp:DropDownList ID="ddlMonthList" runat="server">
                </asp:DropDownList>
                <asp:DropDownList ID="ddlUserList" runat="server">
                </asp:DropDownList></div>
        </form>
</body>
</html>
 
下面是运行效果:
 
 asp.net夜话之八:数据绑定控件(上)

 
        注意,第二个DropDownList控件绑定数据源时有两句话必不可少,就是:
 
ddlUserList.DataTextField = "RealName";//指定下拉列表中的文字显示部分
ddlUserList.DataValueField = "UserID";//指定下拉列表中的值部分
 
        如果缺少这两句,将会是下面的效果:
 
asp.net夜话之八:数据绑定控件(上)
 
        另外,DropDownList控件默认情况下是第一个选项处于选中状态,如果我们想在绑定数据之后让某个选项处于选中状态,可以利用它的Items属性,DropDownList控件的Items属性其实是一个ListItemCollection的实例,ListItemCollection类有两个重要方法:
public ListItem FindByText (string text):在选项集合中查找指定文字的选项。
public ListItem FindByValue (string value) :在选项集合中查找指定值的选项。
        利用这个属性,我们可以让某个选项在数据绑定后就处于选中状态。比如在上面的代码中我们希望绑定数据源后,让刘备处于选中状态,那么我们的BindUserList()的代码可以这么写:
 
private void BindUserList()
    {
        //实例化Connection对象
        SqlConnection connection = new SqlConnection("Data Source=(local);Initial Catalog=AspNetStudy;Persist Security Info=True;User ID=sa;Password=sa");
        //实例化Command对象
        SqlCommand command = new SqlCommand("select UserID,RealName from UserInfo", connection);
        SqlDataAdapter adapter = new SqlDataAdapter(command);
        DataTable data = new DataTable();
        adapter.Fill(data);
        ddlUserList.DataTextField = "RealName";//指定下拉列表中的文字显示部分
        ddlUserList.DataValueField = "UserID";//指定下拉列表中的值部分
        //DataTable类实现了IListSource接口
        ddlUserList.DataSource = data;
        ddlUserList.DataBind();
        //根据指定文字找到了对应的选项
        ListItem item = ddlUserList.Items.FindByText("刘备");
        //如果该选项不为null,则让该选项处于选中状态
        //如果不进行这个判断,而选项集合中没有对应的选项,则会抛出异常
        if (item != null)
        {
            item.Selected = true;
        }
    }
        下面是修改后的代码的运行结果:
 
asp.net夜话之八:数据绑定控件(上)
 
ListBox控件
        ListBox控件和DropDownList控件非常类似,ListBox控件是也是提供一组选项供用户选择的,只不过DropDownList控件只能有一个选项处于选中状态,并且每次只能显示一行(一个选项),而ListBox控件可以设置为允许多选,并且还可以设置为显示多行。
        除了与DropDownList具有很多相似的属性之外,ListBox控件还有以下属性:
Rows属性:设置ListBox控件显示的行数。
        SelectionMode属性:设置ListBox的选择模式,这是一个枚举值,它有Multiple和Single两个值,分别代表多选和单选,默认是Single,即同时只能有一个选项处于选中状态。如果要想实现多选,除了设置SelectionMode属性为Multiple外,在选择时需要按住Ctrl键。
        需要说明的是,因为ListBox允许多选,所以如果ListBox的SelectionMode属性为Multiple,那么SelectedIndex属性指的是被选中的选项中索引最小的那一个,SelectedValue属性指的是被选中的选项集合中索引最小的那一个的值。
 
        下面是ListBox的用法举例:
 
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
                if (!Page.IsPostBack)
                {
                        BindUserList();
                }
        }
        private void BindUserList()
        {
                //实例化Connection对象
                SqlConnection connection = new SqlConnection("Data Source=(local);Initial Catalog=AspNetStudy;Persist Security Info=True;User ID=sa;Password=sa");
                //实例化Command对象
                SqlCommand command = new SqlCommand("select UserID,RealName from UserInfo", connection);
                SqlDataAdapter adapter = new SqlDataAdapter(command);
                DataTable data = new DataTable();
                adapter.Fill(data);
                listUsers.DataTextField = "RealName";//指定下拉列表中的文字显示部分
                listUsers.DataValueField = "UserID";//指定下拉列表中的值部分
                //DataTable类实现了IListSource接口
                listUsers.DataSource = data;
                listUsers.DataBind();
        }
        protected void btnOK_Click(object sender, EventArgs e)
        {
                string selectedUserName = string.Empty;
                //遍历ListBox中的每一个选项
                foreach (ListItem item in listUsers.Items)
                {
                        //如果选项被选中
                        if (item.Selected)
                        {
                                selectedUserName += item.Value+",";
                        }
                }
                //如果至少有一个选项处于选中状态
                if (!string.IsNullOrEmpty(selectedUserName))
                {
                        //删除最后一个","符号
                        selectedUserNameselectedUserName = selectedUserName.Remove(selectedUserName.Length - 1);
                }
                Response.Write("您选择的用户编号有:" + selectedUserName);
        }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
        <title>ListBox控件用法的例子</title>
</head>
<body>
        <form id="form1" runat="server">
        <div>
                <asp:ListBox ID="listUsers" runat="server" SelectionMode="Multiple"></asp:ListBox>
                <asp:Button ID="btnOK" runat="server" OnClick="btnOK_Click" Text="确定" /></div>
        </form>
</body>
</html>
 
 
下面是运行情况:
 
asp.net夜话之八:数据绑定控件(上)
 
 
 
按住Ctrl键同时选择几个选项,然后点击“确定”按钮之后的结果:
 
asp.net夜话之八:数据绑定控件(上)
 
GridView控件
        GridView控件作为asp.net1.1下的DataGrid的替代品,它内置了表格呈现样式。GridView 控件用来在表中显示数据源的值。每列表示一个字段,而每行表示一条记录。GridView 控件支持下面的功能:
绑定至数据源控件,如 SqlDataSource。
内置排序功能。
内置更新和删除功能。
内置分页功能。
内置行选择功能。
以编程方式访问 GridView 对象模型以动态设置属性、处理事件等。
多个键字段。
用于超链接列的多个数据字段。
可通过主题和样式进行自定义的外观。
可以实现多种样式的数据展示。
        GridView控件主要有以下常见属性:
AllowPaging属性:设置是否启用分页功能。
AllowSorting 属性:设置是否启用排序功能。
AutoGenerateColumns 属性:设置是否为数据源中的每个字段自动创建绑定字段。这个属性默认为true,但在实际开发中很少会自动创建绑定列,我们总会根据一些情况让一些列不显示,比如显示用户列表的时候不会将用户密码显示出来,显示文章列表的时候不会将文章内容显示出来。
Columns属性:获取 GridView 控件中列字段的集合。
PageCount属性:获取在 GridView 控件中显示数据源记录所需的页数。
PageIndex属性:获取或设置当前显示页的索引。
PagerSetting属性:设置GridView的分页样式。
PageSize属性:设置GridView控件每次显示的最大记录条数。
        下图是将一个GridView控件拖到页面的情况:
 
asp.net夜话之八:数据绑定控件(上)
 
        点击“编辑列…”会出现下面的界面:
 asp.net夜话之八:数据绑定控件(上)
 
 
        从上面的图中我们可以看出在GridView中可以显示7中类型的字段,它们分别是:
BoundField:绑定字段,以文本的方式显示数据。
CheckBoxField:复选框字段,如果数据库是bit字段,则以此方式显示。
HyperLinkField:用超级连接的形式的显示字段值。
ImageField:用于显示存放Image图象的url字段数据,显示成图片效果。
ButtonField:显示按钮列。
CommandField:显示可执行操作的列,可以执行编辑或者删除等操作。可以设置它的ButtonType属性来决定显示成普通按钮、图片按钮或者超级链接。
TemplateField:自定义数据的显示方式,在这里我们可以使用我们所熟悉的HTML控件或者asp.net Web服务器控件。
        对于我们经常使用到的Users这个表,这次我们不再使用for循环来显示了,这次使用GridView控件来显示。GridView控件的字段大都有HeaderText这个属性,这个属性是用来设置数据的镖头的,如果我们不设置的话默认都是以数据库的相应字段作为表头。另外还有一个DataField属性,这个属性是用来设置要绑定显示的数据的属性或者列名。在这里我们希望在显示的时候给出一个链接,用户可以点击这个链接跳转到查看详细介绍的页面,并且我们还希望将用户的电子邮件显示成超级链接的方式。
        在这里还需要介绍一个属性:DataNavigateUrlFormatString,类似的还有DataTextFormatString,有时候在显示数据的时候我们并不希望仅仅将数据简简单单显示,还希望用一定的格式来显示,那么就可以设置这个属性,在显示的时候我们用到了一个HyperLinkField,用来显示一个超级链接,它的设置如下:
<asp:HyperLinkField DataNavigateUrlFields="UserId" DataNavigateUrlFormatString="ShowUser.aspx?UserId={0}"
                    DataTextField="RealName" HeaderText="查看" />
        我们看到DataNavigateUrlFormatString属性的值为"ShowUser.aspx?UserId={0}",而DataNavigateUrlFields属性的值为"UserId",也就是将来显示每行数据的时候都会将该行对应的“UserId”字段的值替换{0},类似于string.Format("ShowUser.aspx?UserId={0}",[“UserId”]的值)。
        对于电子邮件的显示,我们发现BoundField、CheckBoxField、HyperLinkField、ImageField和ButtonField及CommandField字段都较难实现或者根本不可能实现我们的要求,这时可以使用TemplateField这种类型的字段。
        在GridView控件中TemplateField字段中可以定义5中不同类型的模版,分别如下:
AlternatingItemTemplate:交替项模版,即偶数项中显示的内容,可以进行数据绑定。
EditItemTemplate:编辑项模版,当当前这条数据处于编辑状态的时候要显示的内容,可以进行数据绑定。
FooterTemplate:脚模版,即脚注部分要显示的内容,不可以进行数据绑定。
HeaderTemplate:头模版,即表头部分要显示的内容,不可以进行数据绑定。
ItemTemplate:项模版,处于普通项中要显示的内容,如果指定了AlternatingItemTemplate中的内容,则这里的设置是奇数项的显示效果。可以进行数据绑定。
        注意:可以不设置AlternatingItemTemplate,如果没有设置AlternatingItemTemplate,那么所有的数据项在非编辑模式下都按照ItemTemplate中的设置显示。
下面是用分页显示Users表中的数据的例子,对于奇数行用户的电子邮件仅仅显示电子邮件地址,偶数行的用户显示为超级链接。以下是代码:
 
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
                if (!Page.IsPostBack)
                {
                        //默认显示第一页,不过在GridView中第一页的页索引是0
                        //注意:在C#中集合里的索引也都是以0开始
                        BindGridView(0);
                }
        }
        //指定绑定页面的数据
        private void BindGridView(int pageIndex)
        {
                //实例化Connection对象
                SqlConnection connection = new SqlConnection("Data Source=(local);Initial Catalog=AspNetStudy;Persist Security Info=True;User ID=sa;Password=sa");
                //实例化Command对象
                SqlCommand command = new SqlCommand("select * from UserInfo", connection);
                SqlDataAdapter adapter = new SqlDataAdapter(command);
                DataTable data = new DataTable();
                adapter.Fill(data);
                #region 注意这部分代码可以在设计视图中设置,不必写在代码里
                gvUserList.AllowPaging = true;//设置允许自动分页
                //gvUserList.AutoGenerateColumns = false;//设置不允许自动绑定列
                gvUserList.PageSize = 5;//设置每页显示5条记录
                #endregion
                
                gvUserList.DataSource = data;
                gvUserList.PageIndex = pageIndex;//设置当前显示第几页
                gvUserList.DataBind();
        }
        //翻页事件
        protected void gvUserList_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
                //指定新页面,重新绑定数据
                BindGridView(e.NewPageIndex);
        }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
        <title>用GridView显示数据的例子</title>
</head>
<body>
        <form id="form1" runat="server">
        <div>
                 <asp:GridView ID="gvUserList" runat="server" AutoGenerateColumns="False" OnPageIndexChanging="gvUserList_PageIndexChanging">
                        <Columns>
                                <asp:BoundField DataField="UserId" HeaderText="编号" />
                                <asp:HyperLinkField DataNavigateUrlFields="UserId" DetailsViewDemo.aspx?UserId={0}"
                                        DataTextField="RealName" HeaderText="查看" />
                                <asp:BoundField DataField="UserName" HeaderText="用户名" />
                                <asp:BoundField DataField="RealName" HeaderText="真实姓名" />
                                <asp:BoundField DataField="Age" HeaderText="年龄" />
                                <asp:CheckBoxField DataField="Sex" HeaderText="男" />
                                <asp:BoundField DataField="Mobile" HeaderText="手机" />
                                <asp:TemplateField HeaderText="电子邮件">
                                        <AlternatingItemTemplate>
                                                <a href='emailto:<%#Eval("Email") %>'>发电子给<%#Eval("RealName") %></a>
                                        </AlternatingItemTemplate>
                                        <ItemTemplate>
                                                <%#Eval("Email") %>
                                        </ItemTemplate>
                                </asp:TemplateField>
                        </Columns>
                </asp:GridView>
        
        </div>
        </form>
</body>
</html>
 
        下面是显示效果:
 
asp.net夜话之八:数据绑定控件(上)
 
        点击第二页的效果:
 
asp.net夜话之八:数据绑定控件(上)
 
        对于GridView的用法笔者要说明几点:
        1、上面的代码采用了默认的自定义分页,这种分页每次翻页的时候都会将所有的数据全部从数据中取出来,根据当前页索引和每页要显示的记录条数决定要显示哪些记录,其它的数据会被丢弃掉,在数据量比较大的时候会导致性能低下。对于表中有大量数据的情况,需要自已写分页方法,每次只从数据库中取出需要显示的数据,提高网站的性能,并且根据当前页索引显示页面跳转导航链接,关于如何分页查询在本系列文章中ADO.NET部分笔者已经讲过,这里不再赘述。
        2、当DataSource中没有任何记录时,GridView默认是没有任何显示的。我们还可以给GridView添加一种效果,即当GridView中没有任何数据时给用户提示。这个可以通过给GridView添加EmptyDataTemplate模版,定义没有数据数据时显示的内容,对上面的GridView我们给它添加如果没有数据的功能。在<asp:GridView></asp:GridView>添加如下内容:<EmptyDataTemplate>温馨提示:当前没有任何记录哦。</EmptyDataTemplate>,这时GridView的定义如下:
 
<asp:GridView ID="gvUserList" runat="server" AutoGenerateColumns="False" OnPageIndexChanging="gvUserList_PageIndexChanging">
                        <Columns>
                                <asp:BoundField DataField="UserId" HeaderText="编号" />
                                <asp:HyperLinkField DataNavigateUrlFields="UserId" DataNavigateUrlFormatString="ShowUser.aspx?UserId={0}"
                                        DataTextField="RealName" HeaderText="查看" />
                                <asp:BoundField DataField="UserName" HeaderText="用户名" />
                                <asp:BoundField DataField="RealName" HeaderText="真实姓名" />
                                <asp:BoundField DataField="Age" HeaderText="年龄" />
                                <asp:CheckBoxField DataField="Sex" HeaderText="男" />
                                <asp:BoundField DataField="Mobile" HeaderText="手机" />
                                <asp:TemplateField HeaderText="电子邮件">
                                        <AlternatingItemTemplate>
                                                <a href='emailto:<%#Eval("Email") %>'>发电子给<%#Eval("RealName") %></a>
                                        </AlternatingItemTemplate>
                                        <ItemTemplate>
                                                <%#Eval("Email") %>
                                        </ItemTemplate>
                                </asp:TemplateField>
                        </Columns>
                        <EmptyDataTemplate>
                                温馨提示:当前没有任何记录哦。
                        </EmptyDataTemplate>
                </asp:GridView>
 
        然后将我们的查询
SQL语句改为"select * from UserInfo where UserId>100000",这时的效果如下:
 
asp.net夜话之八:数据绑定控件(上) 
 
3、如果当前的项显示样式不符合我们的要求的时候,我们可以设置项的显示样式,如下图:
 
asp.net夜话之八:数据绑定控件(上)
 
4、
注意:在本例中数据绑定时都加了if (!Page.IsPostBack)这种判断,这样做的目的是不会因为某个控件的提交页面导致整个页面的重新绑定,绝大部分情况下这种重新数据绑定是没有必要的、而且会影响网站的效率的。在实际开发中,各位朋友要区分这一情况。