7.5 ListView控件
ListView控件是从ASP.NET 3.5才开始引入的一个新的数据绑定列表控件。该控件可以绑定从数据源返回的数据项(数据绑定方法与DetailsView等数据控件一样)并显示它们,这些数据可以显示在多个页面。并且,可以逐个显示数据项,也可以对它们分组。
值得注意的是,这个控件本身不在运行期间产生任何HTML标记,而是依赖11个不同的控件模板,这些模板表示控件的不同区域和这些区域的可能状态。在这些模板中,可以在设计期间放置控件自动生成的标记,或开发人员创建的标记,但在这两种情况下,开发人员仍可以全面控制控件中各个数据项的标记以及整个控件的布局标记。另外,由于该控件能理解并处理数据的编辑和分页,所以可以让控件完成许多数据管理工作,开发人员可以把主要精力集中在数据的显示上。
与Repeater控件相似,该控件也适用于任何具有重复结构的数据。但与这些控件不同的是,ListView控件允许用户编辑、插入和删除数据,以及对数据进行排序和分页,所有这一切都无须编写任何额外的代码。
7.5.1 定义模板
ListView控件可支持11种模板,如表7-9所示。
下面的示例演示了项模板的基本结构:
<asp:ListView runat="server"ID="ListView1">
<LayoutTemplate>
<table runat="server"id="table1"border="1">
<tr runat="server"id="itemPlaceholder">
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr runat="server">
<td id="Td1"runat="server">
<asp:Label ID="Label1"runat="server"
Text='<%#Eval("employeename")%>'/>
</td>
<td id="Td2"runat="server">
<asp:Label ID="Label2"runat="server"
Text='<%#Eval("department")%>'/>
</td>
<td id="Td3"runat="server">
<asp:Label ID="Label3"runat="server"
Text='<%#Eval("address")%>'/>
</td>
<td id="Td4"runat="server">
<asp:Label ID="Label4"runat="server"
Text='<%#Eval("email")%>'/>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
如上面的代码所示,若要逐个显示项,必须先向LayoutTemplate模板中添加一个服务器端控件,并将该控件的ID属性设置为item-Placeholder。该控件只是其他模板(通常为ItemTemplate模板)的占位符。这样,该控件在运行时将被其他模板中的内容替换。
定义布局模板后,就可以添加Ite m-Template模板了,它通常包含用于显示数据绑定内容的控件。通过使用Eval方法将这些控件绑定到数据源中的值,也可以指定要用于显示每个项的标记。
上面的示例代码运行结果如图7-16所示:
图 7-16 ListView控件示例运行结果
在ASP.NET 4中,简化了ListView控件的使用,它可以不需要布局模板LayoutTemplate。即下面的示例代码运行结果与上面的示例代码运行结果相同:
<table border="1">
<asp:ListView runat="server"ID="ListView1">
<ItemTemplate>
<tr runat="server">
<td id="Td1"runat="server">
<asp:Label ID="Label1"runat="server"
Text='<%#Eval("employeename")%>'/>
</td>
<td id="Td2"runat="server">
<asp:Label ID="Label2"runat="server"
Text='<%#Eval("department")%>'/>
</td>
<td id="Td3"runat="server">
<asp:Label ID="Label3"runat="server"
Text='<%#Eval("address")%>'/>
</td>
<td id="Td4"runat="server">
<asp:Label ID="Label4"runat="server"
Text='<%#Eval("email")%>'/>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</table>
如果使用GroupTemplate模板,还可以选择对ListView控件中的项进行分组。对项分组通常是为了创建平铺的表布局。在平铺的表布局中,各个项将在行中重复GroupItemCount属性指定的次数。为创建平铺的表布局,布局模板可以包含ASP.NET Table控件以及将runat属性设置为"server"的HTML table元素。随后,组模板可以包含ASP.NET TableRow控件(或HTML tr元素)。而项模板可以包含ASP.NET TableCell控件(或HTML td元素)中的各个控件。如下面的示例代码所示:
<asp:ListView runat="server"ID="ListView1"GroupItemCount="2">
<LayoutTemplate>
<table runat="server"id="table1"border="1">
<tr runat="server"id="groupPlaceholder">
</tr>
</table>
</LayoutTemplate>
<GroupTemplate>
<tr runat="server"id="tableRow">
<td runat="server"id="itemPlaceholder"/>
</tr>
</GroupTemplate>
<ItemTemplate>
<td id="Td1"runat="server">
<asp:Label ID="Label1"runat="server"
Text='<%#Eval("employeename")%>'/>
</td>
<td id="Td4"runat="server">
<asp:Label ID="Label4"runat="server"
Text='<%#Eval("email")%>'/>
</td>
</ItemTemplate>
</asp:ListView>
如上面的代码所示,若要按组显示各项,可以在LayoutTemplate模板中使用一个服务器控件来充当组的占位符。例如,可以使用TableRow控件。同时需要将该占位符控件的ID属性设置为groupPlaceholder。在运行时,该占位符控件将被GroupTemplate模板中的内容替换。
随后,必须再添加一个占位符控件,并将其ID属性设置为itemPlaceholder。该控件只是其他模板(通常为ItemTemplate模板)的占位符。这样,该控件在运行时将被其他模板中的内容替换。该内容将重复ListView控件的GroupItemCount属性所指定的项次数。
最后,请添加一个ItemTemplate模板,并提供要在每个项的包含控件内显示的数据绑定内容。上面的示例代码运行结果如图7-17所示。
图 7-17 ListView控件分组显示示例运行结果
7.5.2 分页
若要使用户能够按页查看ListView控件中的数据,可以使用DataPager控件。DataPager控件可以位于LayoutTemplate模板内部,也可以位于ListView控件之外的页面上。如果DataPager控件不在ListView控件内,则必须将PagedControlID属性设置为ListView控件的ID。
DataPager控件支持内置的分页用户界面。可以使用NumericPagerField对象,此对象允许用户按页码选择数据页。此外,也可以使用NextPreviousPagerField对象。利用此对象,可以逐页浏览数据页,也可以直接跳到第一个或最后一个数据页。数据页的大小由DataPager控件的PageSize属性设置,单个DataPager控件中可以使用一个或多个页导航字段对象。
下面是演示一个在ListView控件的LayoutTemplate模板中使用DataPager控件分页的完整例子。
<asp:SqlDataSource ID="SqlDataSource1"runat="server"
ConnectionString=
"<%$ConnectionStrings:ASPNET4ConnectionString%>"
SelectCommand="SELECT*FROM[Employee]"></asp:SqlDataSource>
<asp:ListView runat="server"ID="ListView1"
DataSourceID="SqlDataSource1">
<LayoutTemplate>
<table runat="server"id="table1"border="1">
<tr runat="server"id="itemPlaceholder">
</tr>
</table>
<asp:DataPager runat="server"
ID="EmployeesDataPager"PageSize="2">
<Fields>
<asp:TemplatePagerField>
<PagerTemplate>
 
<asp:TextBox ID="CurrentRowTextBox"
runat="server"AutoPostBack="true"
Text="<%#Container.StartRowIndex+1%>"
Columns="1"Style="text-align:right"
OnTextChanged=
"CurrentRowTextBox_OnTextChanged"/>
to
<asp:Label ID="PageSizeLabel"
runat="server"Font-Bold="true"
Text="<%#Container.StartRowIndex+
Container.PageSize>
Container.TotalRowCount?
Container.TotalRowCount:
Container.StartRowIndex
+Container.PageSize%>"/>
of
<asp:Label ID="TotalRowsLabel"
runat="server"Font-Bold="true"
Text="<%#Container.TotalRowCount%>"/>
</PagerTemplate>
</asp:TemplatePagerField>
<asp:NextPreviousPagerField
ShowFirstPageButton="true"
ShowLastPageButton="true"
FirstPageText="|<<"LastPageText=">>|"
NextPageText=">"PreviousPageText="<"/>
</Fields>
</asp:DataPager>
</LayoutTemplate>
<ItemTemplate>
<tr id="Tr1"runat="server">
<td id="Td1"runat="server">
<asp:Label ID="Label1"runat="server"
Text='<%#Eval("employeename")%>'/>
</td>
<td id="Td2"runat="server">
<asp:Label ID="Label2"runat="server"
Text='<%#Eval("department")%>'/>
</td>
<td id="Td3"runat="server">
<asp:Label ID="Label3"runat="server"
Text='<%#Eval("address")%>'/>
</td>
<td id="Td4"runat="server">
<asp:Label ID="Label4"runat="server"
Text='<%#Eval("email")%>'/>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
其中,CurrentRowTextBox_OnTextChanged事件处理程序代码如下所示:
protected void CurrentRowTextBox_OnTextChanged(object sender,
EventArgs e)
{
TextBox t=((TxtBox)sender;
DataPager pager=
((DtaPager)ListView1.FindControl("EmployeesDataPager");
pager.SetPageProperties(Convert.ToInt32(t.Text)-1,
pager.PageSize, true);
}
示例代码运行结果如图7-18所示。
图 7-18 ListView控件分页显示示例运行结果
除此之外,还可以通过使用TemplatePager Field对象来创建自定义分页用户界面。在TemplatePagerField模板中,可以使用Container属性来引用DataPager控件。此属性可提供对DataPager控件的各个属性的访问,这些属性包括起始行索引、页面大小,以及当前绑定到ListView控件的总行数。
7.5.3 排序
通过在LayoutTemplate模板中添加一个按钮,并将该按钮的CommandName属性设置为“Sort”,就可以对ListView控件中显示的数据进行排序。该按钮的CommandArgument属性应设置为要用做排序依据的列名。重复单击“Sort”(排序)按钮可在排序方向Ascending和Descending之间切换。
在“Sort”(排序)按钮的CommandArgument属性中,可以指定多个列名。但是,ListView控件会向整个列表的列应用该排序方向。因此,只有列表的最后一列会应用该排序方向。例如,如果CommandArgument包含“employeeid, employeename”,则重复单击“Sort”(排序)按钮会产生某种类似于“employeeid, employeename ASC”或“employeeid, employeename DESC"的表达式。
下面继续为上面的分页示例添加一个排序功能,即按照“employeeid”进行排序。如下面的代码所示:
<asp:ListView runat="server"ID="ListView1"
DataSourceID="SqlDataSource1">
<LayoutTemplate>
<asp:LinkButton runat="server"ID="SortButton"
Text="排序"CommandName="Sort"
CommandArgument="employeeid"/>
<table runat="server"id="table1"border="1">
<tr runat="server"id="itemPlaceholder">
</tr>
</table>
……
</asp:ListView>
示例代码运行结果如图7-19所示。
图 7-19 ListView控件排序示例运行结果
除此之外,还可以通过处理ListView控件的Sorting事件来动态设置排序表达式,从而可以创建自定义排序。如下面的示例代码所示:
protected void ListView1_Sorting(object sender,
ListViewSortEventArgs e)
{
if(String.IsNullOrEmpty(e.SortExpression)){return;}
string direction="";
if(ViewState["SortDirection"]!=null)
direction=ViewState["SortDirection"].ToString();
if(direction=="ASC")
direction="DESC";
else
direction="ASC";
ViewState["SortDirection"]=direction;
string[]sortColumns=e.SortExpression.Split(',');
string sortExpression=sortColumns[0]+""+direction;
for(int i=1;i<sortColumns.Length;i++)
sortExpression+=","+sortColumns[i]+""+direction;
e.SortExpression=sortExpression;
}
7.5.4 编辑数据
如果要使用户能够编辑数据项,可以向ListView控件添加一个EditItemTemplate模板。在将一个项切换至编辑模式时,ListView控件将使用编辑模板显示该项。该模板应包含一些数据绑定控件,以便用户可以在其中编辑各个值。当然,需要向模板中添加一些按钮,以允许用户指定要执行的操作。例如,向项模板中添加“Delete”(删除)按钮,以允许用户删除该项等。
下面是演示一个ListView控件编辑数据的例子。
<asp:ListView ID="ListView1"runat="server"
OnItemEditing="ListView1_ItemEditing"
OnItemCanceling="ListView1_ItemCanceling"
OnItemUpdating="ListView1_ItemUpdating">
<LayoutTemplate>
<table>
<tr runat="server"id="itemPlaceholder">
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<%#Eval("employeeid")%>
</td>
<td>
<%#Eval("employeename")%>
</td>
<td>
<%#Eval("email")%>
</td>
<td>
<asp:Button ID="EditButton"runat="server"
Text="编辑"CommandName="Edit"/>
</td>
</tr>
</ItemTemplate>
<EditItemTemplate>
<tr>
<td>
<asp:Label ID="employeeid"runat="server"
Text='<%#Eval("employeeid")%>'/>
</td>
<td>
<asp:Label ID="employeename"runat="server"
Text='<%#Eval("employeename")%>'/>
</td>
<td>
<asp:TextBox ID="email"runat="server"
Text='<%#Bind("email")%>'/>
</td>
<td>
<asp:Button ID="UpdateButton"runat="server"
CommandName="Update"Text="修改"/>
<asp:Button ID="CancelButton"runat="server"
CommandName="Cancel"Text="取消"/>
</td>
</tr>
</EditItemTemplate>
</asp:ListView>
其中,按钮CommandName属性值可设置为:
❑Select:显示所选项的SelectedItemTemplate模板的内容。
❑Insert:在InsertItemTemplate模板中,指定应将数据绑定控件的内容保存在数据源中。
❑Edit:指定ListView控件应切换到编辑模式,并使用EditItemTemplate模板显示项。
❑Update:在EditItemTemplate模板中,指定应将数据绑定控件的内容保存在数据源中。
❑Delete:从数据源中删除项。
❑Cancel:取消当前操作。显示EditItemTemplate模板时,如果该项是当前选定的项,则取消操作会显示SelectedItemTemplate模板;否则将显示ItemTemplate模板。显示InsertItemTemplate模板时,取消操作将显示空的InsertItemTemplate模板。
相关的事件处理程序如下面的代码所示:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
Bind();
}
}
private void Bind()
{
ListView1.DataSource=
DbHelper.Instance.CreateDataTable(CommandType.Text,
"select*from employee");
ListView1.DataBind();
}
protected void ListView1_ItemEditing(object sender,
ListViewEditEventArgs e)
{
ListView1.EditIndex=e.NewEditIndex;
Bind();
}
protected void ListView1_ItemUpdating(object sender,
ListViewUpdateEventArgs e)
{
DbHelper.Instance.ExecuteNonQuery(CommandType.Text,
"update employee set email='"
+(((TxtBox)ListView1.Items
[e.ItemIndex].FindControl("email")).Text
+"'where employeeid="+Convert.ToInt32(
(((Lbel)ListView1.Items
[e.ItemIndex].FindControl("employeeid")).Text));
ListView1.EditIndex=-1;
Bind();
}
protected void ListView1_ItemCanceling(object sender,
ListViewCancelEventArgs e)
{
ListView1.EditIndex=-1;
Bind();
}
示例代码运行结果如图7-20所示。
图 7-20 ListView控件数据编辑示例运行结果
同样,如果要使用户能够插入新项,可以向ListView控件中添加一个InsertItem Template模板。与编辑模板一样,插入模板也应该包含允许输入数据的数据绑定控件。InsertItemTemplate模板呈现在所显示项的开始或末尾。通过使用ListView控件的InsertItemPosition属性,可以指定InsertItemTemplate模板的呈现位置。