惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - Valens

IIS无法启动:存储空间不足解决办法 做程序员的,平时眼睛总对着代码,很累!看看美女放松放松哦! - Valens - 博客园 How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA Mark:使用线程处理(C# 编程指南) - Valens 交集:通过使用默认的相等比较器对值进行比较生成两个序列的交集 LINQ to SQL StepByStep (1) - Valens 就事网上线了 - Valens About .net Format - Valens 【技巧】asp的找不到文件的问题 【翻译】父窗口关闭的时候子窗口也同时关闭 - Valens - 博客园 【翻译】Visual Studio Smart Tag Expansion 小技巧 【翻译】使用ASP.NET 2.0记录错误 - Valens ListView提示和技巧 LINQ的标准查询操作符 轻松Web调试、扩展Reflector及更多内容 构建不带Web窗体的Web应用程序_ASP.NET MVC - Valens ASP.NET 应用程序的扩展策略 单页界面和AJAX模式 - Valens 删除Visual Studio IDE最近打开文件和项目列表
独有数据绑定控件ListView
Valens · 2008-06-05 · via 博客园 - Valens

     随 Visual Studio® 2008 一同发布的 ASP.NET 3.5 引入了新的数据绑定控件—ListView。我知道您正在想什么:为什么 ASP.NET 里还需要另一个数据绑定控件呢?毕竟,当显示数据收集时,我们已经有超过 10 个控件可供选择,其中包括逐渐不再使用的 DataGrid、新的和改进的 GridView、非常可靠和简单的 Repeater、独特和灵活的 DataList、方便的 FormView 及其稍显冗余的同行 DetailsView。当然,还有一维列表控件 BulletedList、ListBox、DropDownList、RadioButtonList 和 CheckBoxList。

     理论上,ListView 可以取代 ASP.NET 里的所有其它数据绑定控件。这一点没有疑义。您可以使用 ListView 控件代替上面列表中的其他每个控件。ListView 还可以使一些数据绑定任务比使用前几个控件工作起来更加便利,包括 CSS 样式设定、灵活的分页和完善的排序、插入、删除和更新功能。

     我们让介绍 ListView 的典型使用模式,然后讲解控件的功能,展示其灵活性和强大的能力。在本专栏的结尾,您将掌握足够的信息来决定应该在您的 ASP.NET 工具箱中保留多少个数据绑定控件。

ListView 基础

     ListView 是模板驱动的控件,这意味着它默认情况下不会呈现任何数据——您必须以模板的形式完全指定希望它呈现的 HTML。与大多数模板控件类似,ItemTemplate 将成为您工作的重点,您需要将绑定数据集中每一行不断重复的 HTML 内容放在 ItemTemplate 里。

     ListView 中的新功能,也是它与其它控件的真正不同之处在于引进了 LayoutTemplate。在 LayoutTemplate 中,您可以将要输出的顶级 HTML 定义为控件呈现的内容。例如,如果希望 ListView 作为表格呈现,则可以在 LayoutTemplate 中包含顶级 <table> 和 <thead> 元素,把行和单元格的呈现留给 ItemTemplate,如图 1 所示(在本示例中,绑定的数据源将显示包含电影标题和发行日期的简单表格)。图 2 显示了浏览器呈现。

Figure 1 Using LayoutTemplate and ItemTemplate

<asp:ListView runat="server" ID="_simpleTableListView" 
  DataSourceID
="_moviesDataSource">
  
<LayoutTemplate>
    
<table>
      
<thead>
        
<tr>
          
<th>ID</th>
          
<th>Title</th>
          
<th>Release Date</th>
        
</tr>
      
</thead>
      
<tbody>
        
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
      
</tbody>
    
</table>
  
</LayoutTemplate>
  
<ItemTemplate>
    
<tr>
      
<td><%Eval("movie_id"%></td>
      
<td><%Eval("title"%></td>
      
<td><%Eval("release_date""{0:d}"%></td>
    
</tr>
  
</ItemTemplate>
</asp:ListView>


Figure 2 显示在表格中的列表 

     LayoutTemplate 和 ItemTemplate 之间的关联由 LayoutTemplate 中 ID 设置为 itemPlaceholder 的单一服务器端控件完成。(您可以使用 ListView 的 ItemPlaceholderID 属性更改 ID 字符串的默认值。)在第一个示例中,我将 PlaceHolder 控件的实例放置在模板中,即我希望注入 ItemTemplate 内容的位置。请注意:尽管必须支持子控件,但并没有限制必须使用什么类型的控件作为占位符——ID 才是重要。例如,我可以使用服务器端表格行代替 PlaceHolder 控件编写 LayoutTemplate,实现同样的效果:

<LayoutTemplate>
  
<table>
    
<thead>
      
<tr>
        
<th>ID</th>
        
<th>Title</th>
        
<th>Release Date</th>
      
</tr>
    
</thead>
    
<tbody>
      
<tr runat="server" ID="itemPlaceholder" />
    
</tbody>
  
</table>
</LayoutTemplate>


     通常情况下,出于以下两个原因,我更喜欢使用通用的 PlaceHolder 控件。第一个原因是名称匹配得很好。而且,该控件并不呈现其自身的 HTML,而是用 ItemTemplate 的内容代替,因此如果控件除在层次结构中保留位置以外无任何其它目的,这是更合乎逻辑的选择。

     当然,使 ListView 如此灵活的原因是您可以完全控制 LayoutTemplate 的内容。您不是只能使用表格——您可以将任何希望呈现的 HTML 放置在 LayoutTemplate 中,只要注入 itemPlaceholder 控件位置时 ItemTemplate 的内容有效即可。以下是绑定到相同电影数据源的 ListView 示例,但这次不是表格,是带有标题和发行日期的电影显示在项目符号列表中(结果列表如图 3 所示):

Figure 3 相同列表,不同格式 

<asp:ListView runat="server"
     ID
="_simpleTableListView" 
     DataSourceID
="_moviesDataSource">
     
<LayoutTemplate>
       
<ul>
         
<asp:PlaceHolder runat="server" 
              ID
="itemPlaceholder" />
       
</ul>
     
</LayoutTemplate>
     
<ItemTemplate>
       
<li><%Eval("title"%>
           
<%Eval("release_date""{0:d}"%> </li>
     
</ItemTemplate>
                         
</asp:ListView>

ListView 和 CSS

     ASP.NET 开发人员长久以来在创建 CSS 驱动的站点时都受到单独控件的限制。许多默认控件呈现内联样式,或者难于使 CSS 类与其 HTML 输出部分相关联。实际上 Microsoft 在 2006 年 4 月已发布名为“CSS 控件适配器”的工具包,该工具包为几个完全由 CSS 驱动的控件(包括 GridView)提供了可选呈现机制,帮助纠正该问题(有关详细信息,请参见 2006 年 10 月的“非常 ASP.NET”专栏 msdn.microsoft.com/msdnmag/issues/06/10/ExtremeASPNET)。这些备用呈现机制从未并入完整版当中,所以需要单独安装且缺少设计人员支持。

     ListView 通过让您完全控制何时何地应用样式表,使您在站点里利用 CSS 变得更为简捷。一种常见的情形是开发人员为特定页面手动预先设计,通常包含 HTML 和 CSS。采用传统的 GridView 呈现数据表的特定设计总是很难保证正确,因为 GridView 类仅提供用于修改 HTML 结果的有限挂接集。

     我见过许多开发人员经历过的试验和错误,将样式属性应用到网格,查看页面源以准确理解样式放置的位置,并反复试验直到网格能够按要求呈现为止。使用 ListView,您不必再做这些猜测工作了,因为现在您可以控制布局和内容。

例如,假设提供给您的表格需要按照图 4 所示的方式显示,并使用由 .htm 和 .css 文件组成的设计,如图 5中所示。

Figure 5 HTML and CSS for the Table
HTML

<div class="PrettyGrid">
<table cellpadding="0" cellspacing="0" summary="">
<thead>
<tr>
<th scope="col"><href="http://.">ID</a></th>
<th scope="col"><href="http://.">Title</a></th>
<th scope="col"><href="http://.">Release date</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Where the Wild Things Are</td>
<td>12/15/2008</td>
</tr>
<!--  -->
</tbody>
</table>
<div class="Pagination">
<span>1</span>
<href="http://.">2</a>
<href="http://.">3</a>
</div>
</div>

CSS

.PrettyGrid
{
width
: 100%;
}
.PrettyGrid div.Pagination,
.PrettyGrid div.Pagination a,
.PrettyGrid div.Pagination span
{
color
: #00FFFF;
background
: #284775;
font-weight
: normal;
padding
: 2px;
}
.PrettyGrid table
{
border
: solid 1px #CCCCCC;
width
: 100%;
}
/**/

Figure 4 表格目标设计 

     您可以通过从 HTML 中选取适当部分,并将其置于的对应模板中,来快速创建能够精确按照 HTML/CSS 组合要求呈现的 ListView,如图 6 所示。最终结果将与完全使用 CSS 设定 HTML 样式时完全一致。仅更新 HTML 或相应的 CSS 就可以很容易地修改设计。

Figure 6 ListView to Construct the Table

<asp:ListView ID="_moviesGrid" runat="server" DataKeyNames="movie_id" 
  DataSourceID
="_moviesDataSource">            
  
<LayoutTemplate>
    
<div class="PrettyGrid">
      
<table cellpadding="0" cellspacing="0" summary="">
        
<thead>
          
<tr>
            
<th scope="col"><href="http://.">ID</></th>
            
<th scope="col"><href="http://.">Title</a></th>
            
<th scope="col"><href="http://.">Release date</a></th>
          
</tr>
        
</thead>
        
<tbody>
          
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />  
        
</tbody>
      
</table>
      
<div class="Pagination">
        
<span>1</span>
        
<href="http://.">2</a>
        
<href="http://.">3</a>
      
</div>
    
</div>
  
</LayoutTemplate><AlternatingItemTemplate>
    
<tr class="Alternate">
      
<td><asp:Label ID="movie_idLabel" runat="server" 
        Text
='<%# Eval("movie_id") %>' /></td>
      
<td><asp:Label ID="titleLabel" runat="server" 
        Text
='<%# Eval("title") %>' /></td>
      
<td><asp:Label ID="release_dateLabel" runat="server" 
        Text
='<%# Eval("release_date", "{0:d}") %>' />  </td>
    
</tr>
  
</AlternatingItemTemplate>  <ItemTemplate>
    
<tr>
      
<td><asp:Label ID="movie_idLabel" runat="server" 
        Text
='<%# Eval("movie_id") %>' /></td>
      
<td><asp:Label ID="titleLabel" runat="server" 
        Text
='<%# Eval("title") %>' /></td>
      
<td><asp:Label ID="release_dateLabel" runat="server" 
        Text
='<%# Eval("release_date", "{0:d}") %>' /> </td>
    
</tr>
  
</ItemTemplate>
</asp:ListView>


分页

     在前一部分开始时介绍的原 HTML 设计中内含分页和排序,所以根据规范完整实现该网格的任务尚未完成。我们先分页,然后再排序。

     ListView 控件中的分页通过引入另一个新控件 DataPager 实现。通过在单独的控件中隔离分页,DataPager 将分页 UI 与 ListView 用于呈现数据的页面分离。这意味着您可以在页面的任何位置放置分页 UI,并且可以创建任意多个 DataPager 控件。分页控件一个常见的应用是在数据网格的顶部和底部提供分页界面,这样用户不必滚动网格即可导航到下一页——DataPager 可以很容易做到这一点。

     我们先在上一节的 ListView 示例中实现分页。要创建与 ListView 关联的 DataPager 控件,最简单方法是将 DataPager 控件实际嵌入到 ListView 的 LayoutTemplate 中

<asp:ListView ID="_moviesGrid"
  runat
="server" DataKeyNames="movie_id" 
  DataSourceID
="_moviesDataSource">            
  
<LayoutTemplate>
    
<!--  -->
    
<div class="Pagination">
      
<asp:DataPager ID="_moviesGridDataPager" runat="server">
        
<Fields>
          
<asp:NumericPagerField />
        
</Fields>
      
</asp:DataPager>
    
</div>
  
</LayoutTemplate>
</asp:ListView>

     通过将 DataPager 嵌入 ListView 的 LayoutTemplate 中,它们会建立内在联系。另一种方法是将 DataPager 放在页面的其它位置,并通过将其 PagedControlID 设置为关联 ListView 的 ID 以便与 ListView 关联。

     在这种特例中,NumericPagerField 精确显示了我想要的界面——就是一系列显示为超链接的数字,可以导航至页面。DataPager 支持三种类型的字段:

  • NumericPagerField 显示 1 2 3... 分页界面。
  • NextPreviousPagerField 显示“Next”(下一页)、“Previous”(上一页)、“First”(第一页)和“Last”(最后一页)按钮在行间往复。
  • TemplatePagerField 让您使用 PagerTemplate 定义精确设计和实现分页接口的功能。

     一般情况下,采用 DataPager 控件是为任何实现了 IPageableItemContainer 界面的控件(目前 ListView 是唯一实现了该接口的控件)提供分页支持,如下所示:

public interface IPageableItemContainer
{
    
event EventHandler<PageEventArgs> TotalRowCountAvailable;
    
void SetPageProperties(int startRowIndex, int maximumRows, 
                           
bool databind);
    
int MaximumRows { get; }
    
int StartRowIndex { get; }
}

     图 7 显示了 ListView、DataPager 与关联的 DataSource 控件之间的关系。DataPager 从不与用于填充 ListView 的 DataSource 直接交互,而是通过该界面查询所需的数据。

Figure 7 ListView、DataPager 和 DataSource 之间的关系 

     准备分页时,首先是 ListView 查询 DataSource 以查看其是否支持分页,如果支持,那它是否能够返回总行数。如果能够返回总行数,ListView 将检索数据源中的总行数,然后引发 TotalRowCountAvailable 事件,该事件作为其 IPageableItemContainer 接口一部分实现。任何关联的 DataPager 控件都将订阅该事件,并使用总行数初始化显示分页界面所需的字段。DataPager 将随后调用 ListView 的 SetPageProperties 方法设置初始行索引和要返回的最大行数。

     当 ListView 从关联的数据源检索数据时,它将根据 DataPager 设置的值只请求行的子集。无论何时 DataPager 更改其当前页索引(通常由于用户交互),它都将再次调用 ListView 的 SetPageProperties 以反映当前需要检索行的子集。可以通过设置 DataPager 控件的 PageSize 属性来更改一个页面上显示的记录条数,该属性的值将影响相关 ListView 中设置的最大行数信息。

     DataPager 还支持 QueryStringField 属性,该属性可以彻底更改 DataPager 的工作方式。通过将 QueryStringField 属性设置为某些字符串(例如 pageNum),表示您请求 DataPager 发送 HTTP GET 请求以响应用户单击页面编号的操作,所请求的页面编号将通过您指定字符串的查询字符串参数发送,而不是通过传统的 POST 回发模式发送。

     该变化带来的另一个好处是:它让客户端能够创建到数据绑定 ListView 控件中特定页面的书签,因为可以在 URL 中看到页面编号。请注意,如果切换到这种 GET 通信模式,那由 ASP.NET AJAX UpdatePanel 控件使用的 POST 回发挂接机制将无法截取分页请求,并会将其变为异步回发:

<asp:DataPager ID="_moviesGridDataPager" runat="server"
  QueryStringField
="pageNum" >
  
<Fields>
    
<asp:NumericPagerField />
  
</Fields>
</asp:DataPager>

     请注意:因为 DataPager 完全依赖 ListView 执行实际的数据分页,而 ListView 又依赖 DataSource 控件,所以对于其它数据绑定控件也存在相同的分页限制。例如,对于 SqlDataSource 控件,仅当其设置为 DataSet 模式时分页才能正常工作,这意味着需要将整个结果集加载到内存中才能执行分页。当然,您可以使用自定义 DataSource 控件或使用 ObjectDataSource 控件自定义自己的分页。

排序、编辑、插入和删除

     如果 ListView 不具备支持排序和完备的创建、读取、更新和删除 (CRUD) 操作,那么它是不完整的。它实现每种命令的方式与 FormView 控件实现命令的方式相似。

     因为 ListView 完全由模板驱动,所以对于其模板中将 CommandName 属性设置为以下七种特定命令字符串之一的按钮,能够予以识别:Cancel(取消)、Delete(删除)、Select(选择)、Edit(编辑)、Insert(插入)、Update(更新)和 Sort(排序)。每个命令都会启动 ListView 中的相应操作——这样如果希望为 ListView 添加排序,那只需要在 LayoutTemplate 中放置一个按钮(图 8 的示例中使用 LinkButton),将其 CommandName 属性设置为 Sort,并将 CommandArgument 设置为希望数据源进行排序的列名称。在图 8 中,我将以前网格中每列的静态标题链接修改为可单击的链接,通过单击该链接可以请求 ListView 根据该列对数据进行排序。

Figure 8 Sorting in ListView

<asp:ListView ID="_moviesGrid" runat="server" DataKeyNames="movie_id" 
  DataSourceID
="_moviesDataSource">            
  
<LayoutTemplate>
    
<div class="PrettyGrid">
      
<table cellpadding="0" cellspacing="0" summary="">
        
<thead>
          
<tr>
            
<th scope="col">
              
<asp:LinkButton ID="_movieIdSortLink" 
                CommandName
="Sort" CommandArgument="movie_id" 
                runat
="server">ID</asp:LinkButton>
            
</th>
            
<th scope="col">
              
<asp:LinkButton ID="_titleSortLink" 
                CommandName
="Sort" CommandArgument="title" 
                runat
="server">Title</asp:LinkButton>
            
</th>
            
<th scope="col">
              
<asp:LinkButton ID="_releaseDateSortLink"
                CommandName
="Sort" CommandArgument="release_date" 
                runat
="server">Release date</asp:LinkButton>
            
</th>
          
</tr>
        
</thead>  
    
<!--  -->
  
</LayoutTemplate>
</asp:ListView>


     您可以为启动编辑模式、删除行或在数据集中插入新行添加命令按钮,详细过程与其它基于模板的数据绑定控件(如 FormView 和 GridView)基本相同,此处就不再赘述。

分组

     ListView 的最后一个主要功能是将数据分组成子集的能力,非常类似于 DataList 控件提供的功能。DataList 是表格格式的控件,它可以在所呈现数据表的每个单元格中呈现单行数据。您可以通过设置 RepeatColumns 属性来控制将基础数据集的多少行归入单个表格行中。

     由于 ListView 并不仅限于呈现表格,所以它需要更加一般的方式指定将组合呈现的项目组,而这正是 GroupTemplate 提供的方式。图 9 显示了 ListView 中 LayoutTemplate、GroupTemplate 和 ItemTemplate 元素之间的关系。GroupTemplate 可以为基础数据集中每 n 个元素指定外围 HTML,其中 n 的值由 ListView 的 GroupItemCount 属性指定。

Figure 9 ListView 中的模板 

     当在 ListView 中使用 GroupTemplate 时,不需要再在 LayoutTemplate 中指定带有 itemPlaceholder ID 的控件——该控件现在需要位于 GroupTemplate 之内。而是需要在 LayoutTemplate 中指定带有 groupPlaceholder ID 的控件(可以通过设置 ListView 的 GroupPlaceholderID 属性更改控件 ID)以说明对于基础数据集中每 n 个项目,应在哪个位置注入 GroupTemplate 的内容。

     例如,图 10 中的 ListView 显示了如何通过将 GroupTemplate 定义为搜索行,并将 ItemTemplate 设为仅布局单元格,以在表格的每一行中显示来自数据库的四个电影。结果如图 11 所示。

Figure 10 Defining Rows with GroupTemplate

<asp:ListView ID="_groupListView" runat="server" 
  DataKeyNames
="movie_id" DataSourceID="_moviesDataSource" 
  GroupItemCount
="4" >
  
<GroupTemplate>
    
<tr>
      
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
    
</tr>
  
</GroupTemplate>
  
<LayoutTemplate>
    
<table>
      
<asp:PlaceHolder ID="groupPlaceholder" runat="server" />
    
</table>
  
</LayoutTemplate>
  
<ItemTemplate>
    
<td>
      movie_id:
      
<asp:Label ID="_movie_idLabel" runat="server" 
        Text
='<%# Eval("movie_id") %>' /> <br />
      title:
      
<asp:Label ID="_titleLabel" runat="server" 
        Text
='<%# Eval("title") %>' /> <br />
      release_date:
      
<asp:Label ID="_release_dateLabel" runat="server" 
        Text
='<%# Eval("release_date", "{0:d}") %>' /> <br />
      
<br />
    
</td>
  
</ItemTemplate>
</asp:ListView>


Figure 11 结果 Web 页面中的 GroupTemplate 行

     这与使用 DataList 完成的工作非常相似,但因为使用的是 ListView,所以可以像前面所示的网格呈现一样轻松添加分页和排序功能,而使用 DataList 完成这些工作则非常复杂。用于本文的下载代码包含实现了分页和排序功能的示例供您参考。

开始执行 ListView

     您可能希望通过使用 Visual Studio 2008 中的设计器来试用 ListView 控件,该设计器可以提供五种不同的布局供您选择:网格、平铺、项目符号列表、流动和单行。您可以快速查看可用的各种布局选项——但 ListView 真正强大的功能在于您对它所呈现 HTML 的控制,所以在实际的项目中您很可能要自行构建 LayoutTemplate。您是否最后决定今后每次遇到数据绑定都使用 ListView 呢?虽然可能有点过头——但知道您会这样做我还是很高兴。我想今后我还会更多地研究这个灵活的数据绑定控件。

     请将您想向 Fritz 询问的问题和提出的意见发送至 xtrmaspt@microsoft.com.

     Fritz Onion 是 Pluralsight(一家 Microsoft .NET 培训提供商)的创始人之一,负责主持 Web 开发课题。Fritz 是《Essential ASP.NET》和《Essential ASP.NET 2.0》的作者。要了解本作者,请登录 pluralsight.com/fritz

下载源码