Accordion:可折叠面板的集合
Accordion控件中可以包含若干个面板,让用户通过点击不同面板的标题栏一次只展开并显示其中的一个内容,就像将好多个CollapsiblePanel堆到了一起。
10.2.1 应用场景
通常来讲,将一个无比长的列表直接显示给用户是一种极不礼貌的行为。解决方案有很多,归类显示就是其中之一。例如我们常见的QQ或MSN Messenger(Windows Live Messenger)的联系人归类,以及Visual Studio Toolbox中控件的归类等。图10-8显示了Visual Studio中Toolbox对各种控件归类摆放的界面。
图10-8 Visual Studio中的Toolbox
在图10-8中可以看到,种类繁多的控件被归类放在某个目录中,Login分类处于展开状态中。点击某个分类的标题栏,将展开该分类。采用这种设计方式,窗口的界面变得井井有条,用户也能够以最快的速度找到想要的控件。
使用ASP.NET AJAX Control Toolkit中的Accordion控件,我们也可以很方便地在Web页面中实现这样的功能。
10.2.2 声明语法以及常用属性
Accordion作为名词讲,其含义为“手风琴”,而作为形容词的含义为“可折叠的”,这两个词义即生动形象地描述出了该控件的外观和行为——一系列的面板折叠并堆在一起,只显示出每个面板的标题区域。若用户点击了某个标题,则该面板将自动展开,同时其他已经展开了的面板也将自动折叠。从功能上,Accordion类似于选项卡(Tab)控件,它们都提供了多个可选面板,且每次只根据用户的选择显示其中的某一个。
我们可以逐一地编写Accordion中的AccordionPane控件,每个AccordionPane控件即代表一个可折叠的面板。还可以使用数据绑定的方式,分别为其指定标题和内容区域的绑定模板以及数据源,让ASP.NET中的数据绑定为我们自动生成其中的若干个折叠面板。Accordion还能够把当前正处于展开状态的面板自动记录下来,并在页面回送之后恢复其提交前的样式。
声明Accordion控件的语法类似如下所示:
<ajaxToolkit:Accordion
ID="myAccordion"
runat="server"
SelectedIndex="0"
HeaderCssClass="header"
ContentCssClass="content"
AutoSize="None"
FadeTransitions="true"
TransitionDuration="500"
FramesPerSecond="25"
DataSourceID="myDataSource" >
<Panes>
...
...
<ajaxToolkit:AccordionPane
HeaderCssClass="paneHeader"
ContentCssClass="paneContent">
<Header>...</Header>
<Content>...</Content>
</ajaxToolkit:AccordionPane>
...
...
</Panes>
<HeaderTemplate>...</HeaderTemplate>
<ContentTemplate>...</ContentTemplate>
</ajaxToolkit:Accordion>
Accordion控件继承于System.Web.UI.WebControls.WebControl,也就拥有了该控件的所有属性/方法/事件。声明Accordion控件时所常用的属性标签如表10-2所示。
表10-2 声明Accordion控件时的常用属性标签
属性标签名 | 描 述 |
SelectedIndex | 该控件初次加载时展开的AccordionPane面板的索引值 |
HeaderCssClass | 该Accordion中包含的所有AccordionPane面板的标题区域所应用的CSS Class |
ContentCssClass | 该Accordion中包含的所有AccordionPane面板的内容区域所应用的CSS Class |
AutoSize | 在展开具有不同高度的AccordionPane面板时,该Accordion的总高度的变化方式。可选如下3个值: r None:该Accordion将随着当前展开的AccordionPane面板的高度自由伸长/缩短 r Limit:该Accordion将随着当前展开的AccordionPane面板的高度自由伸长/缩短,不过最高不会超过Accordion的Height属性设定值。若是其内容高度超过了Height属性设定值,则将自动显示滚动条 r Fill:该Accordion的高度将固定为Height属性的设定值,不随当前展开的不同高度的AccordionPane面板而变化。若是某个AccordionPane的内容高度超过了Height属性设定值,则将自动显示滚动条 |
FadeTransitions | 若该属性值设置为true,则在切换当前展开的AccordionPane面板时,将带有淡入淡出效果 |
TransitionDuration | 展开/折叠一个AccordionPane面板的过程所花费的时间,单位为毫秒 |
FramesPerSecond | 播放展开/折叠AccordionPane面板动画的每秒钟帧数 |
DataSourceID | 页面中某个DataSource控件的ID,用于通过数据绑定自动生成AccordionPane面板 |
<Panes> | 该标签内将包含一系列的<ajaxToolkit:AccordionPane>标签,即Accordion- Pane的声明,用来表示Accordion中包含的面板 |
<HeaderTemplate> | 在使用数据绑定功能自动生成AccordionPane面板时,该标签内将定义每个面板的标题区域中的内容模板 |
<ContentTemplate> | 在使用数据绑定功能自动生成AccordionPane面板时,该标签内将定义每个面板的正文区域中的内容模板 |
需要注意的是FadeTransitions和FramesPerSecond属性。自然,将FadeTransitions设置为true将让面板展开/折叠的过程显得非常酷,且将FramesPerSecond属性值设置得比较高也会让展开/折叠的动画变得更加平滑,但这样也同时会加重客户端程序执行时的负担,我们应该综合考虑各种情况合理地配置这两个属性。一般来讲,若是Accordion的大小适中,且其中也没有太多的AccordionPane面板,那么可以将FadeTransitions设置为true(即显示淡入淡出效果),并将FramesPerSecond设置为40(即每秒40帧)左右,让动画效果更加流畅眩目。而若是对于较大的,甚至全屏的Accordion,或是其中包含了许多个复杂的AccordionPane面板,那么就应该将FadeTransitions设置为false,且将FramesPer Second设置为25左右,尽可能地避免对客户端执行效率产生过多的影响。
与DataSourceID属性相关的还有DataSource和DataMember属性,分别表示将绑定到该控件上的数据源(一般在代码中设置)和DataSource中数据成员的名称。DataSourceID、DataSource和DataMember属性是ASP.NET中每一个支持模板化数据绑定的控件都包含的,这里不再占用过多篇幅描述。下面的代码演示了以编程方式在代码中为Accordion设置DataSource以及DataMember,并显式进行数据绑定的过程:
myAccordion.DataSource = someDataSet;
myAccordion.DataMember = "DataTableName";
myAccordion.DataBind();
若是采用了数据绑定方式动态生成AccordionPane面板,则<HeaderTemplate>和<ContentTemplate>中应该指定相应的绑定模板,详细的方式将在接下来的示例程序中演示。
作为支持模板化数据绑定的控件,Accordion也暴露出了我们所熟悉的ItemCommand、ItemCreated和ItemDataBound等事件,分别将在其中的控件引发回送、条目创建完成和条目数据绑定完成时被引发。这样,我们即可使用熟悉的方式通过编写这些事件相应的事件处理函数来对Accordion控件的完整生存周期进行控制。
Accordion控件的GetAccordionPanes()方法(或Panes属性)可以返回其中所包含的所有AccordionPane面板的列表。下述代码即演示了在Page_Load()中依次遍历所有的面板,并为处于奇偶行的面板标题应用不同CSS Class的方法:
bool isOddRow = true;
foreach (AccordionPane accordionPane in myAccordion.GetAccordionPanes())
{
accordionPane.HeaderCssClass = isOddRow ? "headerOdd" : "headerEven";
isOddRow = !isOddRow;
}
当然,对于通过数据绑定动态生成的AccordionPane面板来说,这个操作一般应该置于ItemCreated或ItemDataBound事件的处理函数中:
private bool isOddRow = true;
protected void myAccordion_ItemDataBound(object sender, AccordionItemEventArgs e)
{
if (e.ItemType == AccordionItemType.Header)
{
e.AccordionItem.CssClass = isOddRow ? "headerOdd" : "headerEven";
isOddRow = !isOddRow;
}
}
<Panes>标签中包含的若干个<ajaxToolkit:AccordionPane>标签,即AccordionPane控件也继承于System.Web.UI.WebControls.WebControl。声明AccordionPane控件时所常用的属性标签如表10-3所示。
表10-3 声明AccordionPane控件时的常用属性标签
属性标签名 | 描 述 |
HeaderCssClass | 该AccordionPane面板的标题区域所应用的CSS Class,将覆盖声明在Accordion中的同名属性 |
ContentCssClass | 该AccordionPane面板的内容区域所应用的CSS Class,将覆盖声明在Accordion中的同名属性 |
<Header> | 该标签内将定义此AccordionPane标题区域中的内容 |
<Content> | 该标签内将定义此AccordionPane正文区域中的内容 |
10.2.3 示例程序:直接声明Accordion中的AccordionPane
在本示例程序中,我将演示最基本的Accordion控件使用方法,即手工在其中定义若干个AccordionPane控件,完成后的页面将如图10-9所示。
图10-9 用Accordion控件实现的一组折叠面板
在图10-9中可以看到,该Accordion包含了4个AccordionPane面板,用来介绍ASP.NET AJAX客户端框架中的Transformer、Validator、Action和Behavior组件。当前,第一个面板处于展开状态,其他三个面板均被折叠。点击某个折叠面板的标题区域,将会看到该折叠面板以淡入效果慢慢展开,同时第一个面板也以淡出效果慢慢折叠,如图10-10所示。
图10-10 正在进行中的展开/折叠效果
展开/折叠动画效果结束之后,将得到如图10-11所示的显示第三个面板内容的界面,仍然只有一个面板被展开。
图10-11 切换当前显示面板之后的界面
首先,在新建页面的最前面添加ScriptManager控件,然后声明一个Accordion控件:
<ajaxToolkit:Accordion ID="myAccordion" FadeTransitions="true" CssClass= "myAccordion" HeaderCssClass="header" ContentCssClass="content" runat="server">
<Panes>
...
...
...
</Panes>
</ajaxToolkit:Accordion>
上面代码中将FadeTransitions属性设置为true,启用了面板展开/折叠动画过程中的淡入淡出效果。还设定了整个Accordion控件、其中AccordionPane控件的标题以及内容部分的3个CSS Class(CssClass、HeaderCssClass和ContentCssClass),这3个CSS Class的定义如下:
.myAccordion
{
border: 1px solid #ccc;
height: 600px;
}
.myAccordion .header
{
background-color: #ccc;
padding: 5px;
cursor: pointer;
font-weight: bold;
margin-bottom: 2px;
}
.myAccordion .content
{
padding: 5px;
}
上述3个CSS Class的定义非常简单,这里不赘。我们接着来看<Panes>标签中的Accordion- Pane控件声明。从图10-9中可以看到,该Accordion控件中包含了4个不同的面板,即4个AccordionPane控件,我们举出其中一个作为例子,其声明如下:
<ajaxToolkit:AccordionPane ID="AccordionPane1" runat="server">
<Header>
在ASP.NET Atlas中创建自定义的Transformer
</Header>
<Content>
...
...
...
</Content>
</ajaxToolkit:AccordionPane>
上述定义足够直观:<Header>标签中定义了该AccordionPane的标题,这个标题将始终显示在Accordion中;<Content>标签中则定义了该AccordionPane的内容,只有在该AccordionPane展开时内容才会显示出来。
采用同样的方式定义好另外的3个AccordionPane。编译并在浏览器中查看该页面,你将看到如图10-9、图10-10和图10-11所示的界面以及行为。
10.2.4 示例程序:使用数据绑定实现基于Accordion的RSS阅读器
在本示例程序中,我将演示Accordion控件的数据绑定功能,将其绑定到数据源控件上,并根据数据源的内容自动生成其中若干个AccordionPane面板。
该示例程序将通过RSS提要获取我的Blog(http://dflying.cnblogs.com/)中最新的10篇文章,然后显示在页面中的Accordion控件中。完成后的示例程序界面将如图10-12所示。
图10-12 用Accordion实现的RSS阅读器的界面
在图10-12中可以看到,每一篇文章都包含在一个AccordionPane面板中,用户可以通过点击某篇文章的标题栏将该文章展开并阅读。标题栏的右边还显示了一个“More…”链接,点击该链接将导航至Blog中的原文地址。在每篇贴子内容的上方,还显示了作者名、发布日期等信息。
首先仍然是新建页面并添加ScriptManager控件,然后需要考虑的问题就是如何解析RSS提要。我们都知道,RSS提要是一个有着固定格式的XML文档,这样,我们当然可以在取得后使用传统处理XML的方式对其进行解析。但本示例程序中,我们将使用微软公司的Dmitry Robsman所开发的一个开源RSS工具包来简化这个任务。该RSS工具包可以在Dmitry Robsman的Blog(http://blogs.msdn.com/dmitryr/)中下载,它包含了如下几个非常强大的组件/功能:
RSS数据源控件:和ASP.NET内建的集中数据源控件一样,该RSS数据源控件可以绑定到支持模板化数据绑定的控件上,并为其提供显示所需的数据。
在内存或磁盘上缓存远程RSS提要的内容。
基于RSS的URL地址为RSS提要生成易于开发调试的强类型对象。
对程序中生成RSS提要所需要的常用操作的封装。
在本示例程序中我们要用到的只有其中的RSS数据源控件。打开下载后的压缩包,把其中/bin目录中的RssToolkit.dll程序集和RssToolkit.pdb调试信息拷贝到我们Web站点的/bin目录下。然后在Visual Studio的Solution Exploror中刷新该解决方案,你会看到这两个文件已经被包含在了项目中,如图10-13所示。
图10-13 添加对RssToolkit.dll程序集的引用
然后在页面的开头注册这个程序集的前缀,我们将其叫作rssToolkit:
<%@ Register Assembly="RssToolkit" Namespace="RssToolkit" TagPrefix="rssToolkit" %>
这样,即可在页面中添加RssDataSource了。当然,若你愿意,也可以将其添加到Visual Studio的Toolbox中,方便今后的可视化开发。将某个程序集中控件添加到Toolbox中的方法在第2章、第7章中均有介绍,这里不赘。页面中添加完成后的RssDataSource代码如下:
<rssToolkit:RssDataSource ID="dflyingRssDataSource" runat="server" MaxItems="10" Url="http://dflying.cnblogs.com/Rss.aspx">
</rssToolkit:RssDataSource>
MaxItems属性指定了我们将只取得其中最新的10条记录,Url属性指定了该RSS提要的URL地址,这里为了简化,将其硬编码为我的Blog的RSS地址——http://dflying.cnblogs.com/ Rss.aspx。若是今后需要,也可以很方便地改成让用户自定义输入或是由程序其他部分提供该RSS提要地址。这样,该RssDataSource将全权代理从远程URL取得RSS提要内容,并解析成可绑定格式的工作,无需我们参与其中的任何实现细节。
随后添加Accordion控件的声明:
<ajaxToolkit:Accordion ID="rssAccordion" CssClass="myAccordion" HeaderCssClass="header"ContentCssClass="content" AutoSize="Fill" runat="server"
DataSourceID="dflyingRssData Source">
<HeaderTemplate>
...
...
</HeaderTemplate>
<ContentTemplate>
...
...
</ContentTemplate>
</ajaxToolkit:Accordion>
其中声明的三个CSS Class(CssClass、HeaderCssClass和ContentCssClass)依然沿用前一个示例程序中的定义,这里不再重复列出。需要注意的是,因为Blog文章有长有短,我们不希望因为文章的篇幅不同而导致整个Accordion控件的高度在浏览不同文章时不停地变化,所以把AutoSize属性设定为Fill,将Accordion控件的高度固定下来。同样还要注意的是,DataSourceID属性指向了前面定义的RssDataSource的ID,这样即把该Accordion控件和上述RssDataSource绑定了起来。
我们并没有在<ajaxToolkit:Accordion>标签中手工添加<Panes>标签以及其中的若干个<ajaxToolkit:AccordionPane>标签,取而代之的是<HeaderTemplate>和<Content- Template>。
<HeaderTemplate>用来定义每个运行时生成的AccordionPane标题部分的格式,其声明如下:
<HeaderTemplate>
<asp:HyperLink ID="hlMore" CssClass="more" runat="server" Target="_blank" NavigateUrl='<%# Eval("link") %>' Text="More..."></asp:HyperLink>
<asp:Label ID="lbTitle" Text='<%# Eval("title") %>' runat="server"></asp:Label>
</HeaderTemplate>
可以看到,其中定义了两个ASP.NET控件:用来显示“More…”链接的HyperLink控件和用来显示文章标题的Label控件。HyperLink控件的NavigateUrl属性绑定到了RssData- Source的link域上,用来导航至文章的原地址;Label控件的Text属性则绑定到了title域上,用来显示文章的标题。该HyperLink控件还应用了一个名为more的CSS Class(CssClass="more"),定义如下:
.myAccordion .header .more
{
float: right;
font-weight: normal;
}
<ContentTemplate>用来定义每个运行时生成的AccordionPane内容部分的格式,其声明如下:
<ContentTemplate>
published by
<asp:Label ID="lbAuthor" Text='<%# Eval("author") %>' runat="server"></asp:Label>
@
<asp:Label ID="lbPubDate" Text='<%# Eval("pubDate") %>' runat="server"> </asp: Label>
<hr />
<asp:Label ID="lbDescription" Text='<%# Eval("description") %>' runat="server"></asp:Label>
</ContentTemplate>
其中定义了3个Label,其Text属性分别绑定到了RssDataSource的author、pubDate和description域上,分别代表作者、发布日期以及文章描述。
为便于参考,特将完整的Accordion控件的声明代码列举如下:
<ajaxToolkit:Accordion ID="rssAccordion" CssClass="myAccordion" HeaderCssClass="header" ContentCssClass="content" AutoSize="Fill" runat="server"
DataSourceID="dflying RssDataSource">
<HeaderTemplate>
<asp:HyperLink ID="hlMore" CssClass="more" runat="server" Target="_blank" NavigateUrl='<%# Eval("link") %>' Text="More..."></asp:HyperLink>
<asp:Label ID="lbTitle" Text='<%# Eval("title") %>' runat="server"> </asp:Label>
</HeaderTemplate>
<ContentTemplate>
published by
<asp:Label ID="lbAuthor" Text='<%# Eval("author") %>' runat="server"> </asp:Label>
@
<asp:Label ID="lbPubDate" Text='<%# Eval("pubDate") %>' runat="server"> </asp: Label>
<hr />
<asp:Label ID="lbDescription" Text='<%# Eval("description") %>' runat="server" ></asp:Label>
</ContentTemplate>
</ajaxToolkit:Accordion>
这样即完成了本示例程序的编写,编译并在浏览器中查看该页面,将看到如图10-12所示的界面。
10.2.5 常见问题以及使用技巧
常见问题 如何在程序中以编程方式添加AccordionPane面板?
下面的这一段代码演示了如何在现有的Accordion中以编程方式添加两个AccordionPane面板:
AccordionPane pane1 = new AccordionPane();
pane1.HeaderContainer.Controls.Add(new LiteralControl("Pane 1"));
pane1.ContentContainer.Controls.Add(new LiteralControl("Content 1"));
myAccordion.Panes.Add(pane1);
AccordionPane pane2 = new AccordionPane();
pane2.HeaderContainer.Controls.Add(new LiteralControl("Pane 2"));
pane2.ContentContainer.Controls.Add(new LiteralControl("Content 2"));
myAccordion.Panes.Add(pane2);
需要注意的是,在指定AccordionPane的标题和内容时,应该首先相应地使用其HeaderContainer或ContentContainer属性,得到其标题容器或内容容器的引用。然后即可在该容器的Controls集合中使用Add()方法添加控件了。
在创建完新的AccordionPane之后,可以使用Accordion控件的Panes属性得到其当前包含的AccordionPane集合,然后使用其Add()方法把刚刚创建的AccordionPane加入到现有集合中。这样即完成了AccordionPane的动态添加。