数据源控件可以同时暴露平面表格式的或层次的数据。前面演示的sqldatasource和objectdatasource控件都是平面表格式的数据源控件。asp.net 2.0还包含两个层次数据源控件:用于连接xml文件的xmldatasource和用于连接站点导航数据的sitemapdatasource。这一部分将介绍这些控件的使用技术。
treeview和menu控件
数据绑定控件与数据源控件类似,也可以是层次的。表格式数据绑定控件显示数据列表或表格,层次数据绑定控件能够用递规方式获取层次数据,在ui中用父-子关系显示数据。asp.net 2.0中的分层数据绑定控件的例子有treeview和menu控件。下面将介绍把这些控件绑定到层次数据源的一些技术,包括很多示例。
绑定到xml
xmldatasource控件允许其它控件绑定到xml数据。xmldatasource支持datafile属性,它被用于指定作为输入(input)的xml数据文件的路径。你还可以指定tranformfile属性,给数据应用xslt转换;设置xpath属性来指定需要暴露的数据源节点的子集。
下面的例子演示了一个通过xmldatasource控件绑定到xml文件的treeview控件。这个treeview把每个treenode对象的属性与分层树中的xml节点的属性关联起来了(为了进行数据绑定,xml节点的属性都被处理为数据项的属性)。在默认情况下,treeview控件通过调用对象的tostring()方法简单地显示数据项。它显示了xml节点的元素(element)名称,这样你就可以看到treeview所绑定的节点层次。它不一定能够显示出你所需要的内容,但它提供了一个很好的出发点,未来你将更容易定制xml数据的显示方式。
<asp:xmldatasource id="mysource" datafile="~/app_data/bookstore.xml" runat="server"/>
<asp:treeview id="treeview1" skinid="bookstore" datasourceid="mysource"
expanddepth="3" maxdatabinddepth="3" runat="server" />
为了让treeview显示更有意义的内容,你可以为树中的每个节点指定不同的数据绑定。为了定义层次数据项的字段如何映射到treenode属性,你可以把treenodebinding对象添加到treeview的databindings集合中。treenodebinding的两个重要属性决定了如何在层次数据项集合上使用绑定。datamember属性指定了数据项的类型或者xml数据中用于绑定的元素名称。depth属性指定了应用于层次树的数据绑定的深度。你可以设置datamember或depth,或者两个属性都设置。例如,如果要定义xml文件中的所有book元素的数据绑定,只需要把datamember设置为"book"。为了定义所有深度为1的节点的数据绑定,只需要把depth设置为1。如果要定义深度为1的所有book节点,需要把treenodebinding对象的datamember设置为"book",同时把depth设置为1。
当你设置了datamember或depth用于匹配节点集合之后,就可以定义treenodedatabinding的另外一些属性来定制数据项的属性(或xml数据中的xml节点属性)如何映射到treeview控件的treenode的属性。例如,textfield属性定义了显示为treenode文本的属性名称;类似地,valuefield属性定义了作为treenode值的数据项属性;navigateurlfield属性定义了treenode导航链接的字段/属性,等等。你还可以为一个已有数据绑定的treenode属性指定静态值。例如,指定book元素的treenode使用"book.gif"图像、设置datamember属性是"book"的treenodebinding的imageurl属性。
下面的例子演示了一个绑定到xml数据的treeview,数据绑定只应用在xml层次树的特定元素上。
<databindings>
<asp:treenodebinding datamember="bookstore" text="bookstore" imageurl="~/images/xp/folder.gif" />
<asp:treenodebinding datamember="genre" textfield="name" imageurl="~/images/xp/folder.gif" />
</databindings>
xmldatasource支持xpath属性,你可以用它来过滤数据源所暴露的节点集合。在下面的例子中,xpath属性被设置为bookstore/genre[@name='business']/book,以过滤数据源的节点,仅显示"business"类型下的book元素。在指定xpath属性的语法时要特别小心,否则可能出现数据源任何节点都不暴露的情况(相关的数据绑定控件也不会显示)。
<asp:xmldatasource id="mysource" datafile="~/app_data/bookstore.xml" xpath="bookstore/genre[@name='business']/book" runat="server"/>
请注意,treeview树准确地匹配了源xml中的层次。由于这个原因,通常会为了绑定到treeview而具体构造xml,或者为了绑定到treeview而使用xsl转换重新把数据构造为适当的分层结构。
<asp:xmldatasource id="mysource" datafile="~/app_data/bookstore2.xml" transformfile="~/app_data/bookstore2.xsl" xpath="bookstore/genre[@name='business']/book" runat="server"/>
把表格式数据绑定控件绑定到分层数据源也是可行的,但是它只能显示第一层数据。在下面的例子中,模板化的datalist控件绑定到bookstore xml文件。由于数据源暴露的顶层节点是<book/>节点,datalist可以在自己的itemtemplate模板中使用eval数据绑定语法绑定到这些节点的属性。
<asp:datalist id="mydatalist" datasourceid="mysource" runat="server">
<itemtemplate>
<img alt="cover image" src='<%#"images/" + eval("isbn") + ".gif"%>'>
<%# eval("title") %>
isbn: <%# eval("isbn") %>
price: <%# eval("price") %>
</itemtemplate>
</asp:datalist>
虽然只显示一层也是有用的,但是如果能够用嵌套的表格式数据绑定控件来显示下面的层次应该会更好。幸运的是,asp.net 2.0允许你实现这种功能。除了eval数据绑定语法之外,asp.net 2.0还提供了基于xpath的数据绑定语法,在实现了ixpathnavigable接口的任何数据项上都可以使用它。有两种可用的表达式类型:
· xpath(expression, [formatstring]) - 根据数据项计算xpath表达式的值,返回单个值。
· xpathselect(expression, [formatstring]) - 根据数据项计算xpath表达式的值,返回节点列表。
下面的例子是建立在前面的例子基础之上的,它用xpath数据绑定表达式代替eval表达式绑定到book节点的属性。从表面上看,这样的操作与每个表达式的"@"前缀相比没有什么更多功能,只是用于引用节点属性的xpath语法。但是,xpath的真正灵活性就是依赖于这种引用层次中的任意项(不仅仅是属性)的能力的。
这个示例给外部datalist的itemtemplate模板另外增加了一个datalist,并把这个内部datalist的属性绑定到一个描述当前book节点的chapter子节点的xpathselect表达式。在内部datalist的itemtemplate模板中,xpath数据绑定表达式根据这些"chapter"内容节点来计算值。asp.net 2.0利用这种技术,使你能够通过组合表格式数据绑定控件简便地构造出丰富的、分层的数据显示方式。
<asp:datalist id="mydatalist" datasourceid="mysource" runat="server">
<itemtemplate>
<img alt="cover image" src='<%# "images/" + xpath("@isbn") + ".gif" %>'>
<%# xpath("@title") %>
isbn: <%# xpath("@isbn") %>
price: <%# xpath("@price") %>
<asp:datalist id="mydatalist" datasource='<%# xpathselect("chapter") %>' runat="server">
<itemtemplate>
chapter <%# xpath("@num") %>:
<%# xpath("@name") %>
<%# xpath(".") %>
</itemtemplate>
</asp:datalist>
</itemtemplate>
</asp:datalist>
为了处理数据绑定控件对特定位置的节点的请求,xmldatasource等层次数据源控件让层次中的每个节点与唯一的路径相对应。这样就带来了一些特性,例如treeview的按需求填充(popoulateondemand)特性,当某个节点被扩展的时候,来自数据源的节点才被发送到客户端,而不是一次性地发送所有的节点。它也允许你在页面代码中用这种方式配置数据源来显示特定位置的节点。不同的数据类型的路径语法是不同的,而且不能在代码中构造。但是,你可以使用treenode的datapath属性来访问绑定到treeview的节点的数据路径。由于xmldatasource把xpath表达式作为自己的数据路径语法,这些路径也可以指定给xmldatasource的xpath属性来进行节点列表的过滤。下面的例子演示了 这种技术,它使用xmldatasource实现了一个主-从表。例子中有两个xmldatasource控件,其中一个绑定到treeview(主表控件),另一个绑定到datalist(从表控件)。当用户点击treeview节点的时候,就检索它的datapath属性,并把它赋予绑定到datalist的xmldatasource控件,以显示被点击的节点的详细信息。
sub mytreeview_selectednodechanged(sender as object, e as eventargs)
dim path as string = mytreeview.selectednode.datapath
mydetailssource.xpath = path
mydatalist.datasource = mydetailssource
mydatalist.databind()
end sub
绑定到站点导航数据(site navigation)
站点导航数据是asp.net中的另外一种层次数据。asp.net 2.0不仅支持使用asp.net中的站点导航api编程访问站点地图数据,还支持使用sitemapdatasource控件进行宣告式的数据绑定。当你把treeview(或menu)控件绑定到sitemapdatasource的时候,站点地图的text和url属性可以绑定到treenode(或menuitem)。尽管你可以用一个数据绑定集合来建立这种绑定,但是这样的操作不是必要的。treeview和 menu控件自动地把treenode或 menuitem的text和navigateurl属性绑定到相关的站点地图属性(这是使用sitemapnode的inavigateuidata接口实现的)。当treeview和menu绑定到sitemapdatasource的时候,它们还有一个特性,会自动地把selectednode或selecteditem属性设置为站点地图中的当前节点。
下面的例子演示了一个绑定到sitemapdatasource控件的treeview。尽管这个例子使用的是数据绑定集合,但是如果你只需要绑定到节点的text和url属性,这样的操作就是没必要的。
<asp:sitemapdatasource id="sitemapsource" runat="server"/>
<asp:treeview id="mytreeview" skinid="bulletedlist3"
datasourceid="sitemapsource" runat="server">
<databindings>
<asp:treenodebinding textfield="title" navigateurlfield="url" />
</databindings>
</asp:treeview>
绑定到关系数据库
当关系数据库中的多个表通过外部键相关联的时候,也可以用层次结构来表现。例如,在产品数据库中,产品与产品类别关联,它们就可以用类别和产品之间的层次(1对多)关系来表现。尽管当前的asp.net版本没有包含一个用于把关系数据显示为层次结构的控件,你仍然可以通过编程填充层次的数据绑定控件(例如treeview或menu)的节点/数据项来实现这种目的。下面的例子显示了一个用关系数据库填充的treeview控件。这个例子利用treeview的populateondemand特性,按需求(客户端上扩展某个treenode的时候)来填充子节点。
sub getproductcategories(byval node as treenode)
dim categories as categorylist = warehousedb.getproductcategories()
dim c as category
for each c in categories
dim newnode as treenode = new treenode(c.name, c.id)
newnode.selectaction = treenodeselectaction.expand
newnode.populateondemand = true
node.childnodes.add(newnode)
next
end sub
sub getproductsforcategory(byval node as treenode)
dim categoryid as string = node.value
dim products as productlist = warehousedb.getproductsforcategory(categoryid)
dim p as product
for each p in products
dim newnode as treenode = new treenode(p.name, p.id)
node.childnodes.add(newnode)
next
end sub
sub populatenode(byval source as object, byval e as treenodeeventargs)
select case e.node.depth
case 0
getproductcategories(e.node)
case 1
getproductsforcategory(e.node)
end select
end sub
<asp:treeview id="treeview1" ontreenodepopulate="populatenode" skinid="simple" width="250" expanddepth="0" runat="server">
<nodes>
<asp:treenode text="inventory" selectaction="expand" populateondemand="true"/>
</nodes>
</asp:treeview>
新闻热点
疑难解答
图片精选