蓝色信仰

博客园 首页 新随笔 联系 订阅 管理

14 数据绑定控件
从数据绑定开始用WebApplication,否则会有很多麻烦。
数据绑定分为数据源和数据绑定控件两部分,数据绑定控件通过数据源来获得数据,通过数据源来隔离数据提供者和数据使用者,数据绑定控件通过数据源来对数据进行修改,数据源有SqlDataSource、AccessDataSource、ObjectDataSource、LinqDataSource、EntityDataSource、XmlDataSource等(SiteMapDataSource是SiteMap专用数据源),由于大部分项目都不会页面直连数据库(因为违反最基本的分层原则),所以SqlDataSource、AccessDataSource不会使用,LinqDataSource、EntityDataSource也是只有在很极端的采用Linq、EF的项目中才会用,XmlDataSource是处理XML数据是才可能会用。ObjectDataSource是Web开发中应用最广的数据源,也能很容易的进行数据库切换。
数据绑定控件有列表数据绑定控件(DropDownList、RadioButtonList、ListBox、CheckBoxList、BulletedList等)和复杂控件(DataGrid、GridView、DetailsView、FormView、ListView、Repeater、DataList等,GridView等都是ListView子集)。复杂控件中DataGrid已经不推荐使用,Repeater是最轻量级的组件,在互联网的前台用的最多,ListView是ASP.Net 3.5中新增的控件,ListView是GridView、DetailsView、FormView、Repeater、DataList等这些控件的大一统者,那些控件的优点ListView全都有,会了ListView那些控件也就会用了,因此数据绑定控件主要讲列表数据绑定控件、Repeater和ListView,项目中会用到FormView、GridView。

14.1 ObjectDataSource
ObjectDataSource用来将一个类做为数据源,TypeName属性为数据源类的全名,有DeleteMethod、InsertMethod、SelectMethod、UpdateMethod等几个属性,分别为类中删除、插入、查询、更新数据的方法名,这些方法可能有参数,参数的值是通过DeleteParameters、UpdateParameters、InsertParameters等嵌套节点设置的。
手工编写ObjectDataSource太麻烦,使用可视化界面来完成。将ObjectDataSource拖放到界面上,在右上角的智能标志上选择“配置数据源”即可进行配置。数据源类一般用数据集就可以,新建一个数据集,将表拖进来生成DataTable、Adapter等,生成完成后,在ObjectDataSource的“配置数据源”中就可以看到Adapter类,选中类,选择【下一步】,分别选择对应的获得、删除、更新、插入数据的方法。

14.2 列表绑定控件
DropDownList显示来自于ObjectDataSource的数据,选择数据源(DataSourceID属性)为刚才的ObjectDataSource,并且设定显示字段(DataTextField)和值字段(DataValueField)即可。RadioButtonList、ListBox、CheckBoxList、BulletedList等也都是这么用。
手工设定绑定,除了可以给控件的DataSourceID属性设置一个数据源的方式进行数据绑定(推荐),还可以在代码中通过代码设置绑定(旧版本的ASP.Net只能这样绑定,新版本中不推荐)。
BulletedList2.DataSource = new int[] {3,5,6,8,9};
BulletedList2.DataBind();
• 用代码绑定可以将任何实现了IEnumerable接口的对象绑定到数据绑定控件。ListBox2.DataSource = new object[]{3,5,6};ListBox2.DataBind();
• 由于数据绑定控件默认会将数据保存在ViewState中,因此不会每次刷新页面都会重新加载数据,只有第一次需要加载(!IsPostBack)
• 由于代码绑定在禁用ViewState的情况下有很多麻烦事,因此推荐用DataSourceID的方式,控件会自己来判断是否应该重新取得数据。
DropDownList原有“请选择性别”和数据绑定项的共存:AppendDataBoundItems="true"
    <asp:DropDownList ID="DropDownList1" runat="server"
        DataSourceID="ObjectDataSource1" DataTextField="CURRENTNO"
        DataValueField="TYPE" AppendDataBoundItems="True">
<asp:ListItem Selected="True">--请选择--</asp:ListItem> <!--可以在控件上选择“编辑项”后添加-->
    </asp:DropDownList>
14.3 复杂数据绑定控件
除了显示Text、Value这样简单的列表数据绑定控件之外,还有更复杂的数据绑定控件的要求,比如要将人员信息显示在界面上,包含姓名、年龄、照片等。这时候就要使用Repeater、ListView等控件。
学HTML的时候是手写表格,但是项目中很多数据不是固定的,而是动态的。可以用Dom动态增加表格行,但是数据仍然是固定的,我们需要从数据库等地方取得动态的数据来显示。比如网站的友情链接列表就不是固定的,而是从数据库中动态读取动态生成的。

14.3.1 Repeater
Repeater(foreach)用于对绑定数据源中的数据进行遍历显示,每条数据以什么格式显示是由Repeater的<ItemTemplate>来决定的,模板会多次显示<ItemTemplate>姓名:<%#Eval("Name")%><b>年龄:<%#Eval("Age")%></b><br /></ItemTemplate>
<%#Eval("Name")%>表示在这个位置显示当前行的Name属性,注意调用Eval、Bind这些数据绑定方法的时候要用#。
因为Eval就是将属性显示到指定的位置,因此也可以显示到文本框中<ItemTemplate>姓名:<input type="text" value='<%#Eval("Name") %>' /></ItemTemplate>
注意不要写成value="<%#Eval('Name') %>" 因为<%%>中的是C#代码,''是字符,而不是字符串
还可以用在服务器控件中<asp:TextBox Text='<%#Eval("Name") %>' runat="server"></asp:TextBox>

使用方法:
在页面上添加ObjectDataSource并设置数据源,在页面上添加Repeater并设置数据源为刚添加的ObjectDataSource。源码中相应生成源码如下:
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
            DeleteMethod="Delete" InsertMethod="Insert"
            OldValuesParameterFormatString="original_{0}" SelectMethod="GetData"
            TypeName="WebApplication.DataSet1TableAdapters.KEYRANGETableAdapter"
            UpdateMethod="Update">
            <DeleteParameters>
                <asp:Parameter Name="Original_TYPE" Type="String" />
            </DeleteParameters>
            <InsertParameters>
                <asp:Parameter Name="TYPE" Type="String" />
                <asp:Parameter Name="NAME" Type="String" />
                <asp:Parameter Name="STARTNO" Type="String" />
                <asp:Parameter Name="ENDNO" Type="String" />
                <asp:Parameter Name="CURRENTNO" Type="String" />
                <asp:Parameter Name="STATUS" Type="String" />
                <asp:Parameter Name="RESERVED" Type="String" />
            </InsertParameters>
            <UpdateParameters>
                <asp:Parameter Name="NAME" Type="String" />
                <asp:Parameter Name="STARTNO" Type="String" />
                <asp:Parameter Name="ENDNO" Type="String" />
                <asp:Parameter Name="CURRENTNO" Type="String" />
                <asp:Parameter Name="STATUS" Type="String" />
                <asp:Parameter Name="RESERVED" Type="String" />
                <asp:Parameter Name="Original_TYPE" Type="String" />
            </UpdateParameters>
        </asp:ObjectDataSource>
        <asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<!--在这里手工添加ItemTemplate的相关设置-->
        </asp:Repeater>

在Repeater中添加代码后:
        <asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
            <ItemTemplate>名称:<%#Eval("NAME") %>,开始:<%#Eval("STARTNO") %>,结束:<%#Eval("ENDNO") %>,当前:<%#Eval("CURRENTNO") %><br></ItemTemplate>
        </asp:Repeater>

Repeater其他模板:
1、<AlternatingItemTemplate>,设置隔行的不同显示风格,如果设定<AlternatingItemTemplate>,则奇数行用<ItemTemplate>模板,偶数行用<AlternatingItemTemplate>模板
<AlternatingItemTemplate><asp:TextBox BackColor="Red" ID="TextBox2" Text='<%#Eval("Name") %>' runat="server"/></AlternatingItemTemplate> 。设置隔行变色是为了防止数据太多看串行。
2、HeaderTemplate、FooterTemplate:头部、尾部的模板,分别显示在所有数据的前面和后面。
3、SeparatorTemplate :两项数据之间的分隔符,比如换行符

$(“tableImgs img”).mouseenter(function() {
$(this).animate({“width”:”300”, “height”:”200”});
// 排除掉自己,设置其他img的长和宽
$(“#tableImgs img”).not($(this)).animate({“width”:”150”, “height”:”100”});
});

问题:在服务端控件中不能写'~/images/<%#Eval("PicPath")%>',在页面中增加FormatImgURL方法,参数为object类型。对于服务端控件,只能ImageUrl='<%#Eval("PicPath") %>',不能ImageUrl='images/<%#Eval("PicPath") %>'。能不用服务端控件就不用。

14.3.2 ItemDataBound
对于每行数据显示的时候都会调用ItemDataBound事件,在这个事件中可以对当前行进行处理,事件对象主要成员:
• 1、e.Item.ItemType为当前行的类型,Item为ItemTemplate行、AlternatingItem为AlternatingItemTemplate行,还有Header、Footer等取值。
• 2、ItemIndex当前行的序号
• 3、DataItem当前行绑定的对象。

如果要在ItemDataBound事件中对ItemTemplate模板中的控件做处理,则必须使用runat=server的ASP.Net控件或者HTML控件,为控件设置Id,然后用FindControl根据Id来取得控件。注意在ASP.Net的模板中不能直接通过控件的Id来操作控件,必须用FindControl找到控件才能操作。

案例:年龄大于30行的文本框变为红色;年龄大于30行的行变为红色。
        protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            DataRowView rowView = (DataRowView)e.Item.DataItem;
            var personRow = (DataSet1.PERSONRow)rowView.Row;
            if (personRow.AGE > 30)
            {
                TextBox txtAge = (TextBox)e.Item.FindControl("txtAge");
                txtAge.BackColor = Color.Red;
            }
        }

练习:显示友情链接列表,字段:网站名、超链接、友情链接类型(文本、图片)、Logo图片地址。只显示文本友情链接。每行3个友情链接。div+css来布局,float:left;width:33%。
    <style type="text/css">
        UL{list-style-type:none;}
        #links LI{float:left;width:33%;}
    </style>

14.3.3 ItemCommand
可以在模板中放置Button控件(Button、LinkButton、ImageButton),模板中的按钮一般不写OnClick事件响应,而是响应Repeater的ItemDataBound事件。
为Button控件设定CommandName、CommandArgument属性,然后在ItemDataBound事件读取e的CommandName、CommandArgument属性就可以获得发生事件的命令和行参数了。如果对数据进行了操作,则需要Repeater1.DataBind()来重新绑定,从数据库中刷新最新的数据。

案例:涨一岁,给被点击的行的年龄增加1。
        <asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
            onitemdatabound="Repeater1_ItemDataBound"
            onitemcommand="Repeater1_ItemCommand">
            <ItemTemplate>
                <asp:TextBox ID="txtInc" runat="server"></asp:TextBox>
                <asp:Button ID="Button1" runat="server"CommandName="Inc" CommandArgument='<%#Eval("TYPE") %>'Text="增加1" />
                <br>
            </ItemTemplate>
        </asp:Repeater>

        protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
        {
            if (e.CommandName == "Inc")
            {
                TextBox txtInc = (TextBox)e.Item.FindControl("txtInc");
                long val=0;
                if(txtInc.Text.Trim()!="")
                    val = Convert.ToInt64(txtInc.Text);
                val++;
                txtInc.Text = val.ToString();

                //Repeater1.DataBind();// 数据改变时,强制刷新数据
            }
        }

注意:即使没有设置AlternatingItemTemplate偶数行也被识别为ItemType=AlternatingItem。
…_ItemDataBound(…)
{
// Header 和 Footer 也会触发ItemDataBound事件
If (e.Item.ItemType==ListItemType.Item || e.Item.ItemType==ListItemType.AlternatingItem)
{
  …
}
}

14.4 ListView
Repeater一般只用来展示数据,如果要增删改查则用ListView更方便。使用向导(强类型数据)来使用ListView会自动生成很多模板,免去手写模板代码的麻烦,再进行手工调整即可。

首先设定数据源,然后点击智能提示中的“配置ListView”,选择一种布局和样式,然后根据需要勾选“启用编辑”、“启用删除”、“启用插入”、“启用分页”,就会自动生成常用的模板。注意这只是提高开发效率的一个快捷方式,不是唯一的途径。
LayoutTemplate为布局模板,布局模板中必须有一个ID为itemPlaceholder的服务端控件(4.0以后不需要),什么类型无所谓,不会被显示,itemPlaceholder前面就是相当于Repeater中的HeaderTemplate,itemPlaceholder后面就是相当于Repeater中的FooterTemplate,因此ListView中没有这两个模板。
ItemTemplate是每一项的模板,AlternatingItemTemplate是隔行显示的模板,和Repeater一样。EmptyDataTemplate为数据源没有数据的时候显示的内容(Insert也算数据),这样的话可以实现“没有查找结果”、“对不起,找不到您要找的数据”等,InsertItemTemplate为插入数据界面的模板,EditItemTemplate为编辑数据的模板,InsertItemTemplate,为插入数据的模板,SelectedItemTemplate为标记为Selected的行的模板。
1、生成的样式要提到style中,不要内联样式。
2、ItemTemplate里面一般也没必要用<asp:Label展示只读数据,所以直接输出<%# Eval("Name") %>
3、LayoutTemplate中必须有一个id为itemPlaceholder的服务端控件,之上为头,之下为尾。
4、LayoutTemplate表头内容要汉化,所有Template中的不需要显示的字段,比如Id,都要删掉。

EditItemTemplate、InsertItemTemplate中控件的绑定表达式为Text='<%# Bind("Age"),因为Eval只是计算表达式的值输出,而Bind不仅可以计算表达式的值输出,还可以将用户填入的值更新到数据中,因此Eval是单向绑定,Bind是双向绑定。

通过每行的Insert、Delete、Edit、Cancel等Command进行增删改,这几个CommandName被ListView内部处理,不需要开发人员处理,因此自定义的CommandName不要和他们重复。ListView中可以像Repeater那样为行增加Command按钮,处理方法和Repeater一样,ListView也支持Repeater那样的ItemDataBound事件。和Repeater的不同点:
• 1、判断数据行的类型e.Item.ItemType == ListViewItemType.DataItem(编辑模板也放在这里)
• 2、取得行对应的DataRowView:
ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;
DataRowView rowView = (DataRowView)lvDataItem.DataItem;
• 3、在FindControl的时候注意AlternatingItemTemplate的问题。

ListView中可以使用Validator,只要将Validator放入相应的模板中,将Validator手动设为要验证的控件的Id,然后设定相应按钮、控件、Validator为同样的ValidationGroup,防止不同模板中的Validator互相干扰。将Cancel按钮的CausesValidation="false"

新增数据行的默认值:响应ListView的ItemCreated事件(每一行的在页面上的创建都会触发这个事件),当e.Item.ItemType为InsertItem的时候通过FindControl找到控件然后初始化。比如给年龄默认值。
ObjectDataSource绑定Id为Guid类型的表的时候会生成一个“DataObjectTypeName="System.Guid" ”,有问题,删掉就行。
插入数据的初始化:注意和“新增数据行”不同,“插入数据的初始化”是在用户点击“插入按钮”之后执行。比如如果主键为Guid,则需要在数据插入数据库之前为主键赋值。响应ListView的ItemInserting事件(将一些插入数据库之前的对数据进行调整的代码),e.Values为所有字段的键值对,可以读取插入的值,也可以向字段中写值,这样就可以为Id赋值e.Values["Id"]=Guid.NewGuid()。在这个事件中对数据进行校验,可以通过e.Cancel = true来取消非法数据插入。
更新之前的处理:就像数据插入前可以在ItemInserting事件中处理一样,可以在ItemUpdating事件中对更新过程进行处理,e.ItemIndex可以取到当前更新行的行号,e.OldValues可以取到更新前的值,e.NewValues可以取到更新后的值,可以通过e.Cancel = true来取消非法数据插入。同理是ItemDeleting

14.5 DropDownList的绑定
补充,有的模板中可以绑定
ListView中是无法像TextBox等控件那样将DropDownList的选中值绑定到数据的字段的,必须编程处理。例子,人员的性别(男、女、保密),三个值固定写在DropDownList中。
• 1)在显示数据的时候DropDownList显示数据的值。在ItemTemplate中加入DropDownList,设定DropDownList Enabled="false",这样就是只读的。在ItemDataBound事件中e.Item.FindControl()来找到DropDownList控件,然后ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;        DataRowView rowView = (DataRowView)lvDataItem.DataItem;取到DataRowView进而取到DataRow,读取数据的值,然后赋值给DropDownList的SelectedValue属性。也会同步的处理EditTemplete的展示,由于ListView的ItemType处理的不明确,因此需要判断绑定的DataItem数据、FindControl的值是否为空。
• 2)在插入数据的时候设定DropDownList对应的字段的值,响应ItemInserting事件,通过e.Item.FindControl找到DropDownList控件,然后通过e.Values设定值
• 3)在数据更新的时候设定DropDownList对应的字段的值,响应ItemUpdating事件,通过ListViewDataItem dataItem

14.6 行命令按钮
ListView的行按钮和Repeater一样,不同的是取当前行数据的方式。int index = ((ListViewDataItem)e.Item).DisplayIndex取出操作行的行号,ListView1.DataKeys[index].Value取出主键的值,如果对数据进行了操作,最后要对ListView执行DataBind刷新数据。由ListView的DataKeyNames属性决定存储哪些字段值为主键,可以多个主键(和数据库主键没有直接关系),所以有Values。
排序:将LayoutTemplate中的表头用<asp:LinkButton runat="server" CommandName="Sort" Text="Id" CommandArgument="Id" />代替,其中CommandArgument的值为排序字段。只要是CommandName、CommandArgument对就行,展现成什么、显示在哪儿都可以。

14.7 ListView分页
14.7.1 DataPager
ListView搭配DataPager控件实现分页。有两种使用方式,一种是将DataPager声明到ListView中;一种是DataPager、ListView没有嵌套关系,然后将DataPager的PagedControlID设定为要分页的ListView。没有什么区别,一般用“配置ListView”自动生成的内置方式即可。 DataPager的PageSize属性为一页的条数。
(*)实现IPageableItemContainer 接口的控件都可以使用DataPager进行分页,但是ASP.Net内置的控件目前只有ListView实现了这个接口。
DataPager中按钮显示风格由Fields中的字段设置,可以放置多个字段,分为“NextPreviousPagerField”(上一页、下一页、首页、末页等)、“NumericPagerField”(数字页号)、“TemplatePagerField”用模板自定义。代码中选择相应的Field,在属性视图中就可以快速修改它们的属性。

相关单词:First:第一;Last:最后;Next:下一个;Previous:上一个。
NextPreviousPagerField主要属性:ButtonCssClass:按钮的样式;ButtonType,按钮渲染成什么(Button按钮、Link超链接、Image图片);FirstPageImageUrl,【第一页】按钮图片地址;FirstPageText,【第一页】按钮文本,这样可以实现上一页显示为“<”,最后一页显示为“>>|”这样的效果;ShowFirstButton,是否显示【第一页】,其他按钮也有对应属性。
NumericPagerField主要属性:ButtonCount,最多数字的个数;按钮渲染成什么(Button、Link、Image);CurrentPageLabelCssClass当前页文本的样式;NumericButtonCssClass数字按钮的样式。
如何实现|<</</页数/>/>>|这样的效果。顺序添加NextPreviousPagerField、NumericPagerField、NextPreviousPagerField,将第一个NextPreviousPagerField的First、 Previous设置为可见, Last、 Next设置为不可见,将最后一个NextPreviousPagerField的First、 Previous设置为不可见,Last、 Next设置为可见。
(*)实现输入页面编号点击跳转或者在下拉列表中选择页数要用TemplatePagerField。

14.7.2 高效率分页
ListView默认的分页是先从数据源取得所有数据,然后再截取当前页面的部分,在数据量非常大的情况下效率非常低,因此默认分页基本不能用。应该是只从数据源取得要显示的数据。
复习:SQL中语句中取得分页数据。SQL语句中获得每一行序号的方法: SELECT Id, SiteName, LogoURL,Row_Number() over(order by Id) rownum FROM T_Links,Row_Number()函数是SQL2005之后提供的一个计算结果集行号的函数(不是表的行号),over中指定排序规则, Row_Number()从1开始。取得第12条至20数据(条数从0开始)的方法,使用子查询用行号进行
再次处理:
Select  * from (SELECT Id, SiteName, LogoURL,Row_Number() over(order by Id) rownum FROM T_Links) t where t.rownum>11 and t.rownum<=20。

在强类型DataSet中增加取得所有数据条数的方法QueryCount,增加取得指定行数范围数据的方法GetPagedData:select * from
(SELECT ......,Row_Number() over(order by ...) rownum FROM T_Links) t where t.rownum>@startRowIndex and t.rownum<。
由于数据集编辑器不支持(不是运行时不支持,只是设计器不会自动帮我们生成一些东西) Row_Number() ,所以创建完成后需要手动在GetPagedData属性的Parameters中增加两个参数:startRowIndex、maximumRows(参数名必须是这两个,这是由ObjectDataSource的StartRowIndexParamterName、MaximumRowsParamterName确定的,一般不需要改。),都是Int32类型。
ObjectDataSource中EnablePaging属性设置为true,SelectCountMethod设置为QueryCount,SelectMethod设置为GetPagedData。
如果出错的话看看是不是没有放置内置的DataPager或者外置的DataPager的PagedControlID没有指向ListView。
先按照正常的流程配置ObjectDataSource,让ListView自动生成Template,再修改ObjectDataSource的EnablePaging="True",SelectCountMethod设置为取得行数的方法。

DataPager默认是用PostBack机制,显示不到地址中,不利于网友间共享,只要指定QueryStringField属性(比如pagenum)就可以实现超链接形式的分页链接。
ListViewEdit.aspx?Id=22&action=edit
编辑(action=edit)、新增(action=addnew)、查看(action=view)。
字段非常多,ListView只能展示关键性字段,其他字段也在ListViewEdit.aspx来查看。
<a href="ListViewEdit.aspx?action=addnew">新增</a>
<tr><td><a href="ListViewEdit.aspx?Id=22&action=edit">编辑</a><a href="ListViewEdit.aspx?Id=22&action=view">查看</a></td><td>tom</td></tr>
InsertItemPosition="None"不显示插入模板,这样也能显示出来“EmptyTemplate”中的值。
FormView用来进行单条的编辑、查看、新增,有编辑、查看、新增三个模板。
使用 FormView1.ChangeMode(FormViewMode.Insert)切换显示模式
强类型DataSet中增加GetDataById方法,然后配置ObjectDataSource,让Select方法使用GetDataById方法,传递参数传递的是Id参数的值,参数源“QueryString”,QueryStringField:id。

14.8 单独页面编辑
ListView的在位编辑只适合字段比较少、比较简单的场合,复杂数据的编辑、插入、查看等要在单独页面中。
创建一个单独的页面Edit***.aspx,然后在ListView页面中的编辑放一个编辑的超链接,向Edit***.aspx传递?id=1&action=edit。页面顶端增加一个Edit***.aspx?action=addnew的超链接。
使用FormView控件进行单条数据的编辑,在Page_Load中判断action,然后使用FormView1.ChangeMode方法切换FormView的模式。
强类型DataSet中增加一个GetDataById方法,在ObjectDataSource中选择这个方法为Select参数,参数源为QueryString,QueryStringField为id。
在元素插入、修改完成(Inserted、Updated事件)后重定向到列表页面。

1、删掉Id,需要在Inserting为Id赋值(e.Values["Id"] = Guid.NewGuid())
2、插入完成、更新完成以后重定向到ListUI。ItemUpdated、ItemInserted中Redirect
FormView中的DropDownList的绑定,思路和Listview一样。把InType改造成DropDownList。
JQueryUI的js 在JQuery之后引入
响应FormView的ItemCreated事件,用FormView.CurrentMode判断当前渲染的模板(因为FormView同时只能渲染一个模板,所以不需要像ListView那样e.Item.ItemType)。然后用FormView.FindControl找到控件(也不像Listview中用e.Item.FindControl)

posted on 2012-03-13 16:34  蓝色信仰  阅读(403)  评论(0编辑  收藏  举报