首页 > 代码库 > 控件绑定
控件绑定
1Repeater
复杂数据绑定控件
除了显示Text、Value这样简单的列表数据绑定控件之外,还有更复杂的数据绑定控件的要求,比如要将人员信息显示在界面上,包含姓名、年龄、照片等。这个时候就要使用Repeater、ListView等控件。
学HTML的时候是手写表格,但是项目中很多数据不是固定的,而是动态的。可以用Dom动态增加表格行,但是数据仍然是固定的,我们需要从数据库等地方取得动态的数据来显示。比如网站的友情链接列表就不是固定的,而是从数据库中动态读取动态生成的。
Repeater
Repeater(foreach)用于对绑定数据源中的数据进行遍历显示,每条数据以什么格式显示是由Repeater的<ItemTemplate>来决定的,模板会多次显示<ItemTemplate> </ItemTemplate>里的内容。
<%#eval_r(“Name”)%>表示在这个位置显示当前行的Name属性,注意调用Eval、Bind这些数据绑定方法的时候要用#。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate>name(age):<%#eval_r("Name") %>(<%#eval_r("Age") %>)<hr/></ItemTemplate>
</asp:Repeater>
因为Eval就是将属性显示到指定的位置,因此也可以显示到文本框中<ItemTemplate>姓名:<input type=”text” value=http://www.mamicode.com/’’/>
注意不要写成value=http://www.mamicode.com/” ”,因为中执行的是C#代码,’是字符,而不是字符串。
还可以用在服务器控件中
</div>
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate><input type="text" value=http://www.mamicode.com/‘<%#eval_r("Name") %>‘/>
<asp:TextBox runat="server" Text=‘<%#eval_r("Age") %>‘></asp:TextBox><hr/></ItemTemplate>
</asp:Repeater>
Repeater其他模板
1、<AlternatingItemTemplate >,设置隔行的不同显示风格,如果设定<AlternatingItemTemplate >,则奇数行用<ItemTemplate>模板,偶数行用<AlternatingItemTemplate >模板。隔行显示不同风格,是为了防止数据太多看串行了。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate>
name:<%#eval_r("Name") %>(<%#eval_r("Age") %>)
</ItemTemplate>
<AlternatingItemTemplate >
<div style="
name:<%#eval_r("Name") %>(<%#eval_r("Age") %>)
</div>
</AlternatingItemTemplate>
</asp:Repeater>
2、HeaderTemplate、FooterTemplate:头部、尾部的模板,分别显示在所有数据的前面和后面。可以使用头部和尾部的模板定义一个Table。
3、SeparatorTemplate:两项数据之间的分隔符,比如换行符。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate>
name:<%#eval_r("Name") %>(<%#eval_r("Age") %>)
</ItemTemplate>
<AlternatingItemTemplate >
<div style="
name:<%#eval_r("Name") %>(<%#eval_r("Age") %>)
</div>
</AlternatingItemTemplate>
<HeaderTemplate><p>人员数据</p></HeaderTemplate>
<FooterTemplate><p>制表人:zyang</p></FooterTemplate>
<SeparatorTemplate><hr/></SeparatorTemplate> //数据之间分割线
</asp:Repeater>
2Repeater案例和练习
案例:显示人员信息,姓名、年龄、照片。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate>
<tr><td><%#eval_r("Name") %></td><td><%#eval_r("Age") %></td><td><img alt="照片" src=http://www.mamicode.com/‘images/<%#eval_r("PicPath") %>‘ /></td></tr>
</ItemTemplate>
<HeaderTemplate><Table></HeaderTemplate>
<FooterTemplate></Table></FooterTemplate>
</asp:Repeater>
3Repeater练习点评1
案例:鼠标放到图片上,图片动态放大,其他还原。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<HeaderTemplate><table id="tableImgs"><tr></HeaderTemplate>
<ItemTemplate><td><img height="50" width="50" src=http://www.mamicode.com/‘images/<%#eval_r("PicPath") %>‘ /></td></ItemTemplate>
<FooterTemplate></tr></table></FooterTemplate>
</asp:Repeater>
<script type="text/javascript">
$("#tableImgs img").mouseenter(function () {
$(this).animate({ "height": "200", "width": "200" });
$("#tableImgs img").not($(this)).animate({ "height": "50", "width": "50" });
});
</script>
易错:在服务端控件中不能写’~/images/<%eval_r(“PicPath”)%>’,可以在页面中增加FormatImgURL方法,参数为object类型。对于服务端控件,只能ImageUrl=’<%eval_r(“PicPath”)%>’,不能再加另外的字符串。否则会找不到地址。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1">
<HeaderTemplate><table id="tableImgs"><tr></HeaderTemplate>
<ItemTemplate><td>
<asp:Image runat="server" Height="50" Width="50" ImageUrl=‘<%#FormatImgURL(eval_r("PicPath")) %>‘ />
</td></ItemTemplate>
<FooterTemplate></tr></table></FooterTemplate>
</asp:Repeater>
protected string FormatImgURL(object url)
{
return ResolveClientUrl("~/母版页/images/"+url);
}
5 Repeater练习点评
对于数据绑定的数据库中的数据,如果存在网址,最好存入的是完整的数据(加前缀),加上http://,如:http://www.baidu.com。否则需要在定义超链接<a>的href赋值之前,给其进行字符串连接。而对于服务端控件,则不能直接连接赋值。
6 Repeater的ItemDataBound事件
ItemDataBound
Repeater对于每行数据显示的时候都会调用ItemDataBound事件,在这个事件中可以对当前行进行处理,事件对象主要成员:
1、e.Item.ItemType为当前行的类型,ListItemType.Item为ItemTemplate行,AlternatingItem为AlternatingItemTemplate行,还有Header、Footer等取值,均是ListItemType类型的。
2、e.Item.ItemIndex当前行的序号。
3、e.Item. DataItem当前行绑定的对象,如果绑定的是数据集DataSet,则返回的数据需要转换为DataRowView类型使用。
如果要在ItemDataBound事件中对ItemTemplate模板中的控件做处理,则必须使用runat=”server”的ASP.Net控件或者HTML控件,为控件设置Id,然后用e.Item.FindControl根据Id来取得控件,注意在ASP.Net的模板中不能直接通过控件的Id来操作控件,必须用e.Item.FindControl找到控件才能操作。
案例:使年龄大于20的文本框变红。
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
onitemdatabound="Repeater1_ItemDataBound">
<ItemTemplate>
Name:<%#eval_r("Name") %>,<asp:TextBox ID="txtAge" runat="server" Text=‘<%#eval_r("Age") %>‘></asp:TextBox>
</ItemTemplate>
<SeparatorTemplate><br /></SeparatorTemplate>
</asp:Repeater>
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
//此处必须判断ListItemType.Item和ListItemType.AlternatingItem两种情况,否则只执行偶数行,不执行奇数行。
if (e.Item.ItemType == ListItemType.Item||e.Item.ItemType==ListItemType.AlternatingItem)
{
DataRowView rowView = (DataRowView)e.Item.DataItem;
var personRow = (theme.DAL.DataSetUsers.T_UsersRow)rowView.Row;
if (personRow.Age > 20)
{
TextBox txtAge = (TextBox)e.Item.FindControl("txtAge");
txtAge.BackColor = Color.Red;
}
}
}
7Repeater的ItemCommand
可以在模板中放置Button控件(Button、LinkButton、ImageButton),模板中的按钮一般不写Onclick事件响应,而是响应Repeater的ItemDataBound事件。
为Button控件设定CommandName、CommandArgument属性,然后在ItemDataBound事件读取e的CommandName、CommandArgument属性就可以获得发生事件的命令和行参数了。如果对数据进行了操作,则需要Repeater1.DataBind()来重新绑定,从数据库中刷新最新的数据。
aspx:
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
onitemdatabound="Repeater1_ItemDataBound"
onitemcommand="Repeater1_ItemCommand">
<ItemTemplate>
name:<%#eval_r("Name") %>,<asp:TextBox runat="server" ID="txtAge" Text=‘<%#eval_r("Age") %>‘></asp:TextBox><asp:Button runat="server" Text="涨一岁" CommandName="IncAge" CommandArgument=‘<%#eval_r("Id")%>‘ /><br />
//把每行数据的Id通过CommandArgument传入
</ItemTemplate>
</asp:Repeater>
cs:
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
//只执行CommandName=”IncAge”的按钮
if (e.CommandName == "IncAge")
{
int id = Convert.ToInt32(e.CommandArgument);
T_personTableAdapter adapter=new T_personTableAdapter();
//在DataSet中添加SQL查询:”update T_person set Age=Age+1 where Id=@Id;”
adapter.IncAgeById(id);
//DataBind()是为了从重新绑定数据,从而实现数据从Repeater中刷新数据库中的值
Repeater1.DataBind();
}
}
8Repeater综合案例
练习:人员管理程序(Id、用户名、启用状态),增加【禁用】当前行按钮,点击【禁用】,点击的时候提示是否真的要禁用,如果确定要禁用则将“启用状态”字段设置为“禁用”。禁用字段显示为红色。字段变为禁用后,行显示【启用】按钮,点击【启用】按钮将“启用状态”字段设置为“启用”。
在DataSet中添加两个update语句:
update T_UsersStatus set Status = ‘禁用’ where Id=@Id;→DisabledUserById(@Id).
update T_UsersStatus set Status = ‘启用’ where Id=@Id;→EnabledUserById(@Id).
在aspx中添加一个css,disabledRow:
<style type="text/css">
.disabledRow{background-color:Red;}
</style>
aspx代码:
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
onitemdatabound="Repeater1_ItemDataBound"
onitemcommand="Repeater1_ItemCommand">
<HeaderTemplate><table><tr><td>用户名</td><td>状态</td><td>操作</td></tr></HeaderTemplate>
<FooterTemplate></table></FooterTemplate>
<ItemTemplate><tr runat="server" id="trRow"><td><%#eval_r("UserName") %></td><td><%#eval_r("Status")%></td><td>
<asp:Button ID="btnEnabled" runat="server" Text="禁用" CommandName="Enabled" CommandArgument=‘<%#eval_r("Id") %>‘ OnClientClick=‘return confirm("真的要禁用吗?");‘/>
<asp:Button ID="btnDisabled" runat="server" Text="启用" CommandName="Disabled" CommandArgument=‘<%#eval_r("Id") %>‘ OnClientClick=‘return confirm("真的要启用吗?");‘/>
</td></tr></ItemTemplate>
</asp:Repeater>
cs代码:
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRowView rowView = (DataRowView)e.Item.DataItem;
var userRow = (theme.practice.DAL.UsersStatus.T_UsersStatusRow)rowView.Row;
if (userRow.Status == "禁用")
{
HtmlTableRow trRow=(HtmlTableRow)e.Item.FindControl("trRow");
trRow.Attributes["class"] = "disabledRow";
Button btnEnabled = (Button)e.Item.FindControl("btnEnabled");
Button btnDisabled = (Button)e.Item.FindControl("btnDisabled");
btnEnabled.Visible = false;
btnDisabled.Visible = true;
}
else if (userRow.Status == "启用")
{
HtmlTableRow trRow = (HtmlTableRow)e.Item.FindControl("trRow");
//trRow.Attributes["class"] = "";
Button btnEnabled = (Button)e.Item.FindControl("btnEnabled");
Button btnDisabled = (Button)e.Item.FindControl("btnDisabled");
btnEnabled.Visible = true;
btnDisabled.Visible = false;
}
else
{
throw new Exception("非法Status字段");
}
}
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Enabled")
{
int id = Convert.ToInt32(e.CommandArgument);
T_UsersStatusTableAdapter adapter = new T_UsersStatusTableAdapter();
adapter.DisabledUserById(id);
Repeater1.DataBind();
}
else if (e.CommandName == "Disabled")
{
int id = Convert.ToInt32(e.CommandArgument);
T_UsersStatusTableAdapter adapter = new T_UsersStatusTableAdapter();
adapter.EnabledUserById(id);
Repeater1.DataBind();
}
}
9ListView基础
Repeater一般只用来展示数据,如果要增删改查则用ListView更方便。使用向导(强类型数据)来使用ListView会自动生成很多模板,免去手写模板代码的麻烦,再进行手工调整即可。
首先设定数据源,然后点击智能提示中的”配置ListView”,选择一种布局和样式,然后更具需要勾选”启用编辑”、”启用删除”、”启用插入”、”启用分页”,会自动生成常用的模板。注意这只是提高开发效率的一个快捷方式,不是唯一途径。
LayoutTemplate为布局模板,布局模板中必须有一个ID为itemPlaceholder的服务端控件(4.0以后不需要),什么类型无所谓,不会被显示,itemPlaceholder前面就是相当于Repeater中的HeaderTemplate,itemPlaceholder后面就相当于Repeater中的FooterTemplate,因此ListView中没有这两个模板。
ItemTemplate是偶数行显示的模板;
AlternatingItemTemplate是奇数行显示模板,和Repeater一样;
EmptyDataTemplate为数据源没有数据时显示的内容,这样的话可以实现”没有查询结果”,”对不起,找不到您要找的数据”等;
InsertItemTemplate为插入数据界面的模板;
EditltemTemplate为编辑数据的模板;
SelectedItemTemplate为标记Selected的行的模板。
需要进行微调的地方:
1. 生成的样式要提到style中,不要内联样式。
2. ItemTemplate里面一般也没有必要用<asp:Label>展示只读数据,所以直接输出<%#eval_r(“Name”)%>。
3. LayoutTemplate中必须有一个id为ItemPlaceholder的服务端控件,之上为表头,之下为表尾。
4. LayoutTemplate表头内容要汉化;所有Template中的不需要用户所见的字段,比如Id,都要删掉。
10ListView事件
EditItemTemplate、InsertItemTemplate中控件的绑定表达式为Text=’<%#Bind(“Age”)%>’,因为Eval只是计算表达式的值输出,而Bind不仅可以将计算表达式的值输出,还有将用户填入的值更新到数据中,因此Eval是单向绑定,Bind是双向绑定。
通过每行的Insert、Delete、Edit、Cancel等Command进行增删改,这几个CommandName被ListView内部处理,不需要开发人员处理,因此自定义的CommandName不要和他们重复。ListView中可以像Repeater那样为行增加Command按钮,处理方法和Repeater一样,ListView也支持Repeater那样的ItemDataBound事件。
使用ListView与Repeater的不同地方:
1、 开始的时候,需要判断数据行的类型,e.Item.ItemType == ListViewItemType.DataItem;
2、 强类型DataSet中,取得对应的DataRowView,需要先强制转换成ListViewDataItem类型。
ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;
DataRowView rowView = (DataRowView)lvDataItem.DataItem;
3、在FindControl的时候注意AlternatingItemTemplate的问题。
例子:根据Age字段变色。
protected void ListView2_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;
DataRowView rowView = (DataRowView)lvDataItem.DataItem;
var userRow = (WebApplication1.母版页.DAL.ListViewDataSet.T_UsersRow)rowView.Row;
if (userRow.Age > 20)
{
HtmlTableRow trRow = (HtmlTableRow)e.Item.FindControl("trRow");
trRow.Attributes["class"] = "warning";
}
}
}
其中,需要修改AlternatingItemTemplate和ItemTemplate中<tr>为<tr runat = “server” id = “trRow”>。一个是奇数行,一个是偶数行,均需要修改,而且在模板中,不存在id重复的问题。
ListView中可以使用Validator(验证控件),只要将Validator放入相应的模板中,将Validator手动设为要验证的控件的Id,然后设定相应按钮、控件,Validator为同样的ValidationGroup属性,防止不同模板中的Validator互相干扰。一般设置Cancel按钮为CausesValidation = ”false”属性值,这样取消就不会影响到提交表单。
新增数据行的默认值:响应ListView的ItemCreated事件(每创建一行就会触发这个事件),当e.Item.ItemType为InsertItem的时候通过FindControl找到控件然后初始化。
例:给年龄默认值。
protected void ListView2_ItemCreated(object sender, ListViewItemEventArgs e)
{
if(e.Item.ItemType==ListViewItemType.InsertItem)
{
TextBox txtAge = (TextBox)e.Item.FindControl("AgeTextBox");
txtAge.Text = "20";
}
}
插入数据的初始化:注意和“新增数据行”不同,“插入数据的初始化”是在用户点击“插入按钮”之后执行。比如如果主键为Guid,则需要在数据插入数据库之前为主键赋值。响应ListView的ItemInserting事件(将一些插入数据库之前的对数据进行调整的代码放在这个事件里面)。
e.Values为所有字段的键值对,可以读取插入的值,也可以向字段中写值,这样就可以为Id赋值e.Values[“Id”] = Guid.NewGuid();在这个事件中对数据进行校验,可以通过e.Cancel = true来取消非法数据插入。
ObjectDataSource绑定Id为Guid类型的表的时候会生成一个“DataObjectTypeName = “System.Guid””,有问题,删掉就行,是一个Bug。
11ListView事件2
更新之前的处理:就像数据插入前可以在ItemInserting事件中处理一样,可以在ItemUpdating事件中对更新过程进行处理,e.ItemIndex可以取到当前更新行的行号,e.OldValues可以取到更新前得值,e.NewValues可以取到更新后的值,可以通过e.Cancel = true来取消非法数据插入。
12DropDownList的绑定
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属性。
2. 在插入数据的时候设定DropDownList对应的字段的值,响应ItemInserting事件,通过e.Item.FindControl找到DropDownList控件,然后通过e.Values设定值。
3. 在数据更新的时候设定DropDownList对应的字段的值,响应ItemUpdatating,通过ListViewDataItem dataItem。
在<EditItemTemplate>中摆放一个DropDownList控件。
<asp:DropDownList runat="server" ID="ddlGender">
<asp:ListItem Value="http://www.mamicode.com/Male">男</asp:ListItem>
<asp:ListItem Value="http://www.mamicode.com/Female">女</asp:ListItem>
</asp:DropDownList>
在更新的时候显示DropDownList,在ItemUpdating事件中响应。
protected void ListView2_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
DropDownList ddlGender = (DropDownList)ListView2.Items[e.ItemIndex].FindControl("ddlGender");
//这里只能通过ListView2.Items[e.ItemIndex].FindControl()找到控件,因为e.Item点不出来FindControl()
e.NewValues["Gender"] = ddlGender.SelectedValue;
}
在编辑的时候显示DropDownList,并通过数据库中存在的值,给DropDownList赋初值。
protected void ListView2_ItemCreated(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
DropDownList ddlGender = (DropDownList)e.Item.FindControl("ddlGender");
//因为ListViewItemType中只有三个选项,所以选取DataItem并不能准确定位到底是哪一个行,没有Repeater中设定的那么细致,所以这里如果没有获取到ddlGender,则说明不是编辑行。
if (ddlGender != null)
{
ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;
DataRowView rowView = (DataRowView)lvDataItem.DataItem;
//原理与上面相同。
if (rowView != null)
{
var userRow = (theme.ListView.DAL.ListViewDataSet.T_UsersRow)rowView.Row;
ddlGender.SelectedValue = http://www.mamicode.com/userRow.Gender;
}
}
}
}
13友情链接管理练习说明
需求:后台提供友情链接增删改的页面。友情链接字段:序号、网站名、友情链接类型(图片、文本)、Login地址、链接地址。要进行数据的非空校验等非法值校验,对数据的长度也要校验;序号必填且必须为整数,网站名必填,链接地址必填;当友情链接类型为文本超链接的时候Logo地址隐藏并且可以为空,当友情链接为图片超链接的时候Logo地址显示并且必填。
14友情链接练习点评
主要是为了处理DropDownList,如果除去DropdownList,这个程序就很简单了。在Updateting事件中,取DropDownList控件的方法不一样。主要代码如下:
protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
DropDownList ddlLinkType = (DropDownList)e.Item.FindControl("ddlLinkType");
ListViewDataItem lvDataItem = (ListViewDataItem)e.Item;
DataRowView rowView = (DataRowView)lvDataItem.DataItem;
if (ddlLinkType != null && rowView != null)
{
var linksRow = (FriendlyLink.DAL.FriendlyLink.T_LinksRow)rowView.Row;
ddlLinkType.SelectedValue = http://www.mamicode.com/linksRow.LinkType;
}
}
}
protected void ListView1_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
//必须绕一圈再能取到DropDownList,站在ListView的角度。
DropDownList ddlLinkType = (DropDownList)ListView1.Items[e.ItemIndex].FindControl("ddlLinkType");
e.NewValues["LinkType"] = ddlLinkType.SelectedValue;
}
protected void ListView1_ItemInserting(object sender, ListViewInsertEventArgs e)
{
//不需要绕圈子,直接从Insert行的角度去取DropDownList。
DropDownList ddlLinkType = (DropDownList)e.Item.FindControl("ddlLinkType");
e.Values["LinkType"] = ddlLinkType.SelectedValue;
}
在ListView中给每一行,添加JavaScript代码,需要在ItemCreated事件中更改,因为Insert行不走ItemDataBound事件。
protected void ListView1_ItemCreated(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem || e.Item.ItemType == ListViewItemType.InsertItem)
{
DropDownList ddlLinkType = (DropDownList)e.Item.FindControl("ddlLinkType");
if (ddlLinkType != null)
{
ddlLinkType.Attributes["onchange"] = "onLinkTypeChange(this)";
}
}
}
15入库单练习说明
需求:提供入库明细的增删改查页面。字段:Id(Guid类型)、类型(可选值:采购入库、盘盈入库、退货入库,值来自于另外一张入库单类型表)、入库日期(默认为当天)、单价、数量、金额。所有字段都不能为空,当用户输入单价或者数量之后自动计算金额(金额=单价*数量),考虑折扣因此金额可以不等于数量*单价,用户还可以修改金额。点击【删除】按钮的时候提示用户是否删除。如果数量为负值(红单),则此行显示为红色背景。
16入库单管理练习说明
17行命令处理
ListView的行按钮和Repeater一样,不同的是取当前行数据的方式。int index = ((ListViewDataItem)e.Item).DisplayIndex去取操作行的行号,ListView1.DataKeys[index].Value取出主键的值(DataKeys中存放了所有主键的值)。如果对数据进行了操作,最后要执行ListView .DataBind刷新数据。
案例:涨一岁按钮。
protected void ListView1_ItemCommad(object sender,ListViewCommandEventArgs e)
{
int index = ((ListViewDataItem)e.Item).DisplayIndex;
Guid id = (Guid)ListView1.DataKays[index].Value;
//以上两行代码实现了取出主键。
if(e.CommandName == “IncAge”)
{
T_personsTableAdapter adapter = new T_personsTableAdapter();
adapter.IncAge(id);
ListView1.DataBind();
}
}
ListView1.DataKays[index]还有一个Values方法,可以取出多个值,但是需要在ListView的DataKeyName属性中设定,默认只存Id,可以添加、修改,如果添加了多个值,则需要使用Values来取值。例:ListView1.DataKays[index].Values[“Id”];
排序:将LayoutTemplate中的表头放一个按钮控件,如:<asp:LinkButton runat = “server” CommandName = “Sort” CommandArgument=”Id” Text = “Id” />(可以用这个控件代替表头字段),其中CommandArgument的值是排序依据的字段,此处是依据Id排序;CommandName的值必须为Sort,这样标志这进行排序。只要是CommandName、CommandArgument对就行,展现成什么、显示在哪儿都可以。
这种内置的排序,是把所有的数据都遍历了再进行排序,所以效率是相当低的,在互联网应用程序中不推荐使用。
18ListView分页基础
DataPager
ListView搭配<DataPager>控件实现分页。有两种方式,一种是将DataPager声明到ListView中;另一种是DataPager、ListView没有嵌套关系,然后将DataPager的PagedControlID设定为要分页的ListView的ID。没有什么区别,一般用“配置ListView”自动生成的内置方式即可。DataPager的PageSize属性为一页得条数。
(*)理论上实现IPageableItemContainer接口的控件都可以使用DataPager进行分页,但是ASP.Net内置的控件目前只有ListView实现了这个接口。
DataPager中按钮显示风格由Fields中的字段设置,可以设置多个字段,分为“NextPreviousPageField”(上一页、下一页、首页、末页等)、“NumericPAgerField”(数字页号)、“TemplatePagerField”用模板自定义。代码中选择相应的Field,在【属性视图】中就可以快速修改它们的属性。
DataPager调整
相关单词:First:第一;Last:最后;Next:下一个;previous:上一个。
NextPreviousPagerField(上下页)主要属性:ButtonCssClass:按钮的样式;ButtonType:按钮渲染成什么(Button按钮、Link超链接、Image图片);FirstPageImageUrl:【第一页】按钮图片地址;FirstPateText:【第一页】按钮文本,这样可以实现上一页显示为“<”,最后一页显示为“>>”这样的效果;ShowFirstButton:是否显示【第一页】,其他按钮也有对应属性。
NumericPagerField(数字分页)主要属性:ButtonCount:最多显示数字的个数;ButtonType:按钮渲染成什么(Button按钮、Link超链接、Image图片);CurrentPageLabelCssClass当前页文本的样式;NumericButtonCssCalss数字按钮的样式。
如何实现|<</</页数/>/>>|这样的效果。顺序添加NextPreviousPagerField、NumericPagerField、NextPreviousPagerField,将第一个NextPreviousPagerField的First、Previous设置为可见,Last、Next设置为不可见,将最后一个NextPreviousPagerField的First、Previous设置为不可见,Last、Next设置为可见。
(*)实现输入页面编号点击跳转或者在下拉列表中选择页数要用TemplatePagerField。
19ListView高效分页
ListView默认的分页是先从数据源取得所有数据,然后再截取当前页面的部分,在数据量非常大的情况下效率非常低,因此默认分页基本上不能用。应该是只从数据源取得要显示的数据。
复习:SQL中语句中取得分页数据。SQL语句中获得每一行序号的方法:
select id,SiteName,LogoURL,Row_Number() over(order by Id) as rownum from T_Links;
Row_Number()函数是SQL2005之后提供的一个计算结果集行号的函数(不是表的行号),over中指定排序依据,Row_Number()从1开始。取得第11条至20条数据(条数从0开始)的方法,使用子查询用行号进行再次处理:
select * from
(select Id,SiteName,LogoURL,row_number() over(order by Id) as rownum
from T_Links ) as t
where t.rownum>11 and t.rownum<=20.
在强类型DataSet中增加取得所有数据条数的方法QueryCount:
select * from T_Links
,增加取得指定行数范围数据的方法GetPagedData:
select * from
(select …,row_number() over(order by …) as rownum from T_Links) as t
where t.rownum>@startRowIndex and t.rownum<=@startRowIndex+@maximumRows
由于数据集编辑器不支持ron_number()(不是运行时不支持,只是设计器不会自动帮我们生成一些东西),所以创建完成后需要手动在【GetPagedData属性】的Parameters属性中增加两个参数:StartRowIndex、maximumRows(参数名必须是这两个,这是由ObjectDatSource的StartRowIndexParamterName、MaximumRowsParamterName确定的,一般不需要修改),都是int32类型。
操作顺序:
先按正常的流程配置ObjectDataSource,让ListView自动生成Template,再修改ObjectDataSource的EnablePaging=”True”,SelectCountMethod设置为取得行数的方法。否则很多自动生成的东西都没有生成。
ObjectDataSource中EnablePaging属性设置为true,SelectCountMethod设置为QueryCount,SelectMethod设置为GetPagedData。
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
DeleteMethod="Delete" EnablePaging="True" InsertMethod="Insert"
OldValuesParameterFormatString="original_{0}" SelectCountMethod="QueryCount"
SelectMethod="GetPagedData"
TypeName="Paging.DAL.PagingTableAdapters.T_UsersTableAdapter"
UpdateMethod="Update">
详细查看Paging程序。
如果出错的话看看是不是没有放置内置的DataPager或者外置的DataPager的Page地ControlID没有指向ListView。
DatPager默认是用PostBack机制,显示不到地址中,不利于网络间共享,只要指定QueryStringField属性(比如Pagenum)就可以实现超连接形式的分页链接。
(*)GetPagedData中的StartRowIndex、maximumRows两个参数名其实是可以修改的,但是一般不推荐去修改它们。在【ObjectDataSource属性】中,有两个属性StartRowIndexParam、MaximunRowsParam分别对应着这两个参数。
20行的单独页编辑
ListView的数据编辑只适合字段比较少、比较简单的场合,复杂数据的编辑、插入、查看等要在单独页面中完成(FormView控件)。
创建一个单独的页面Edit*.aspx,然后在ListView页面中的编辑一个编辑的超链接,向Edit*.aspx传递?id=1&action=edit。页面顶端增加一个Edit*.aspx?action=addnew的超链接。
使用FormView控件进行单条数据的编辑,在Page_Load中判断action,然后使用FormView1.ChangeMode方法切换FormView的模式。
在元素插入、修改完成(Inserted、Updated事件)后重定向到列表页面。
当把<ListView>的属性设置为InsertItemPosition=”None”,则不显示插入模板,这样如果数据库为空,则能显示“EmptyTemplate”中的值。所以一般有插入模板的时候,是不会显示<EmptyTemplate>模板中的值的。
FormView用来进行单行的编辑、查看、新增,有编辑、查看、新增三个模板。
在FormView中可以自己写按钮来切换这三个模板。切换为新增模板,使用
FormView1.ChangeMode(FormViewMode.Insert);
FormView有三个模式:FormViewMode.Edit:编辑模式;FormViewMode.Insert:插入模式;FromViewMode.ReadOnly:只读模式。
强类型DataSet中增加一个GetDataById方法,在ObjectDataSource中选择这个方法为Select参数,参数源为QueryString(查询字符串,用于get传值),QueryStringField为id(表示传入Id字段的值)。
21行的单独页面编辑2
在单独页中需要调整的代码:
1、 删除Id,需要在ItemInserting事件中为Id赋值。(e.Values[“Id”] = Guid.NewGuid();//Guid算法)
2、 插入完成、更新完成以后重定向到ListUI界面。需要在ItemInserted和ItemUpdated事件中进行修改。(Response.Redirect(“ListUI.aspx”);)
JqueryUI.js在JQuery.js之后引入,否则会出错。
22行的单独页面编辑3
响应FormView的ItemCreated事件,用FormView1.CurrentMode判断当前渲染的模板(因为FormView同时只能渲染一个模板,不需要像ListView那样e.Item.ItemType)。然后用FormView1.FindControl找到控件(也不像ListView中用e.Item.FindControl();)。
插入的时候使用DropDownList,与在ListView控件中使用,思路是一样的。
23CKEditor的使用
CKEditor原名FckEditor,著名的HTML编辑器(常见的发帖控件),可以在线编辑HTML内容。
配置参考文档,主要将ckeditor中的(adapters、images、lang、plugins、skins、themes、cheditor.js、config.js、content.css)解压到js目录,在发帖页面引用ckeditor.js,然后设置多行文本框的class = “ckeditor”(服务端控件改CssClass=” ckeditor”),代码中仍然可以通过TextBox控件的Text属性来访问编辑器内容。
由于页面提交的时候asp.net会把副文本编辑器中的html内容当成攻击内容,因此需要在aspx中的Page标签中设置ValidateRequest=”false”来禁用攻击检测。(VS2010中还有更具报错信息修改WebConfig来禁用XSS)
24CKFinder的集成
CKFinder是一个CKEditor插件,用来为CKEditor提供文件的上传的功能。将bin/Release下的CKFinder.dll添加到项目的引用;将core、ckfinder.js、ckfinder.html、config.aspx解压到CKFinder自己的目录。按照文档修改CKEditor的config.js,将上传的处理程序设定为CKFinder,注意路径的问题。
因为上传文件是非常危险的动作,因此在文件上传的时候会进行权限校验。在config.ascx的CheckAuthentication方法中校验是否有上传权限,返回true表示有权限,返回false没有权限,一般修改成判断用户是否登录,并且登录用户是有上传权限的用户,可以用Session或者Membership来做。思考:如何实现只有指定IP地址的用户才能上传?
public override bool CheckAuthentication()
{
object obj = Session[“登录”];
if(obj != null && Convert.ToBoolean(obj) == true)
{
return true;
}
reture false;
}
在SetConfig函数中设置上传文件夹的位置BaseUrl、缩略图的位置,每种类型数据的上传路径、允许上传的文件类型AllowedExtensions等。
type.AllowedExtensions = new string[]{“bmp”,”gif”,”jpeg”};
25入库单管理练习点评及.Net4.0Bug解决
在服务端使用ClientID是无法找到正确的模板ID的。这是4.0的一个Bug,在3.5的环境下就不会存在这样的问题。
方法一:在服务端使用ClientID获得的ID与模板ID比较缺少的只是”ListView1_”,可以在服务端代码,获得控件ID的时候手动的添加这个字符串。
方法二:在模板控件中定义一个额外的属性“myid”,然后在JQuery中,使用属性选择器$(“input[myid=”+countMyId+”]”).val();得到控件的值。
方法三:修改ListView的ClientIDMode属性。
26综合案例企业网站1
27企业网站2
28企业网站练习点评
29企业网站练习点评2
30禁用ViewState
默认情况下ASP.Net是启用ViewState的,这样在页面中会生产很长的隐藏字段,ViewState对于需要PostBack处理的页面也可能有用,对于新闻展示页面完全没必要用ViewState。
禁用ViewState的方法:
页面整体禁用ViewState:在顶部Page中添加“EnableViewState=”False””。
指定控件禁用ViewState:在控件上“EnableViewState=”False””。
页面禁用ViewState以后并没有完全去掉ViewState,只要ViewState不是很大就可以。如果要求一点ViewState都不能有,那么则页面中不能有runat = “server”的form,如果页面中没有表单元素,把form完全去掉就可以。如果Button等服务端控件没有放到runat = “server”的form中,那么则是不可用的。
31缓存1(cache)
如果每次进入页面的时候都查询数据库生成页面内容的话,如果访问量非常大,则网站性能会非常差。而如果只有第一次访问的时候才查询数据库生成页面内容,以后都直接输出内容,则能提高系统性能,这样无论有多少人访问都只访问一次数据库,数据库压力不变,就算有很多人访问,只是增加了服务器的压力。
缓存是一种用空间换取时间的技术,存在于计算机中很多地方,用来将一些慢速设备中的常用数据保存在快速设备中,去取数据的时候直接从快速设备中取。比如CPU二级缓存、windows文件读取缓存。
缓存存在失效的问题:为了保证从缓存中读取数据和慢速数据中数据一致,则需要在慢速数据中对应的数据发生变化的时候,清除缓存中相应的数据。
换成是改进网站性能的一个手段,就像索引是改进数据库性能的一个手段一样。ASP.Net缓存主要分为:页面缓存、数据源缓存、数据缓存这三种主要类型。
32缓存2
页面缓存
给页面添加<%@OutputCache Duration=”15” VaryByParam = “none”%>标签就可以启动页面缓存,这样整个页面的内容都会被缓存,页面中的ASP.Net代码、数据源在缓存期间都不会被运行,而是直接输出缓存的页面内容。Duration表示缓存事件,以秒为单位,超过这个事件则缓存失效,再次生成以后会再缓存15秒,以此类推。在Page_Load处设置断点,修改数据库数据测试。
缓存是针对所有这个页面的访问者。这样1个访问者和1万个访问者,一次访问和100万次访问数据库的压力是一样的。
对于看新闻页面来讲,如果如上设置的话,则会缓存在第一个看到的新闻,因为?id=2、?id=3只是页面的不同参数而已,为了能让不同的新闻各自缓存,因此可以设置VaryByParam=”id”,表示对于不同的id参数进行单独缓存。如果有多个确定缓存的参数,则将参数名用分号隔开即可,比如:VaryByParam = “id,number”。
如果想让任何不同的查询字符串都创建不同的缓存,则设置VaryByParam=”*”,一般情况下设置”*”就足够。
在WebUserControl中也可以像页面缓存一样设置控件的缓存。
数据源缓存
设定ObjectDataSource的CacheDuration(缓存时间:秒),EnableCaching = true;。这样每隔CacheDuration指定的时间段才调用SelectMethod指定的方法来执行数据库查询,其他时候都是直接返回缓存的数据。
缓存固定的事件适用于首页、文章列表等访问频繁的页面,对于看帖页面则不适合,假设有100万个帖子,如果每隔帖子都是固定缓存1小时的话,假设一小时内有10万个帖子被看了,那么就要缓存十万个帖子,非常占用内容,因为“百年一看”的“坟贴”偶然被访问一次也缓存一个小时,占用内存。这时候可以采用“滑动窗口”策略,比如帖子缓存10分组,如果10分钟之内又访问了,则缓存的失效事件修改为从被访问这一刻起的10分钟之后,以此类推。这样经常访问的帖子就可以“长期缓存”,而不经常访问的帖子也不会因为偶然访问而长期占用缓存。设置方法,数据源:CacheExpirationPolicy = “Sliding”
33错误页1
当页面发生错误的时候,ASP.Net会将错误信息展示出来,这样一来不好看,二来会泄露网站的内部实现信息,给网站带来安全隐患,因此需要定制错误页,发生错误时显示开发人员定制的页面。404页面放点广告也好嘛,反正流量也浪费了。
配置web.config,配置custonErrors区域:
<custonErrors mode = “RemoteOnly” defaultRedirect = “GenericErrorPage.htm”>
<error statusCode = “403” redirect = “NoAccess.htm”>
<error statusCode = “404” redirect = “FileNotFound.htm”>
</custonErrors>
mode三个可选值:On,总是显示定制错误页面;Off,不显示定制错误页面,直接显示调用堆栈等异常信息;remoteOnly,对于本机的访问显示调用堆栈等异常信息,对于外部的用户显示定制错误页。一般设置为RemoteOnly,这样发生错误的话,管理员可以在服务器的浏览器中看到详细错误信息,普通用户看不到。学习掩饰的时候mode设置为On,否则看不到定制错误页。
也可以在定义错误页中判断Request.UserHostAddress来设置某些ip看到异常信息,可以读取Session,如果是管理员则看异常信息。
34错误页2
error子元素设定对于不同的状态码使用不同的错误页,很多网站都把404做一个特殊的错误页。没有单独设置的状态码错误则显示defaulyRedirect中指定的页面。
错误也既可以使用htm页面,也可以使用aspx页面。在aspx页面中可以用HttpContext.Current.Server.GetLastError()拿到异常对象。一般不要把异常信息显示给用户,而是使用后面讲的Log4Net等将异常记录到系统日志。
如果要在错误页面中拿到异常对象,需要在<custonErrors>中设置redirecrMode = “RespinseRewrite”,因为默认是客户端重定向,在错误页面中就拿不到异常对象了。